1、查找:在查找池中查找目標元素或肯定查找池中不存在該目標元素html
2、排序:基於一個標準,將一組項目按照某個順序排列java
排序算法
順序排序:選擇排序、插入排序、冒泡排序
對數排序:快速排序、歸併排序
n個元素排序:順序排序大約n^2次比較,對數排序大約nlog2 n次比較
n較小時,這兩類算法幾乎不存在實際差異git
選擇排序:
算法
第1趟排序,在待排序數據arr[1]~arr[n]中選出最小的數據,將它與arrr[1]交換;
第2趟,在待排序數據arr[2]~arr[n]中選出最小的數據,將它與r[2]交換;
以此類推,第i趟在待排序數據arr[i]~arr[n]中選出最小的數據,將它與r[i]交換,直到所有排序完成。編程
反覆地將某一特定值插入到元素列表的已排序的子集中來完成排序
須要注意的是,每次插入可能須要元素移位,而且每插入一次已排序子集都將多一個元素數組
n個元素,每一輪排序都將最大值移到最終位置,需比較n-1輪;
每一輪事後,下一輪須要比較的值就會少一個
冒泡排序的算法彷佛還能夠設計兩邊一塊兒冒。。。
函數
分區元素的選擇是任意的,但最好選擇列表的第一個元素,從而第一輪快速排序分區元素能把列表大體分爲兩半
持續對兩個分區進快速排序,直至分區只含有一個元素,排序即完成
值得注意的是,決定放置好了初始分區元素,就不會對其進行考慮和移動了性能
歸併排序包括"從上往下"和"從下往上"2種方式
如圖所示:
學習
首先看到這個排序算法的時候,有一個疑惑:算法好像只是一半一半地將原列表元素分紅只含有一個元素的子列表,而後再將只含有一個元素的子列表歸併成一個新的已排好序的列表,即完成了排序。
那問題是,歸併的時候是怎麼把序排好的?
將兩個已經有序的子序列合併成一個有序序列,好比下圖中的一次合併,要將[4,5,7,8]和[1,2,3,6]兩個已經有序的子序列,
合併爲最終序列[1,2,3,4,5,6,7,8],步驟爲:
測試
【參考資料】
圖解排序算法(四)之歸併排序
比較相鄰兩個元素的大小。若是前一個元素比後一個元素大,則兩元素位置交換;
奇數趟時從左向右進行比較和交換;
偶數趟時從右向左進行比較和交換;
當從左端開始遍歷的指針與從右端開始遍歷的指針相遇時,排序結束;
通俗來講就是:
首先將第一個數和第二個數比較,若第二個數比第一個數小,則交換,而後比較第二第三個,並以此類推,
直到第n-1個數和第n個數比較爲止,這時最大的數在第n個位置,這是第一次比較;
而後第二次比較從第n-1個數依次到第一個數,此時最小的數在第一個位置;
第三次比較則是從第二個數開始依次比較,直到第n-1個數...依次類推,直到中間兩個數比較完爲止
代碼實現:
int arrayLength = array.length; int preIndex = 0; int backIndex = arrayLength - 1; while(preIndex < backIndex) { preSort(array, arrayLength, preIndex); preIndex++; if (preIndex >= backIndex) { break; } backSort(array, backIndex); backIndex--; } } // 從前向後排序 private void preSort(int[] array, int length, int preIndex) { for (int i = preIndex + 1; i < length; i++) { if (array[preIndex] > array[i]) { ArrayUtils.swap(array, preIndex, i); } } } // 從後向前排序 private void backSort(int[] array, int backIndex) { for (int i = backIndex - 1; i >= 0; i--) { if (array[i] > array[backIndex]) { ArrayUtils.swap(array, i, backIndex); } }
【參考資料】
排序算法系列:冒泡排序與雙向冒泡排序
排序-----冒泡排序(單向冒泡,雙向冒泡,優化版本)
這裏會出現的問題就是,間隔的元素i加上去以後可能超過數組的長度,即不存在這個元素,就會出現如圖的錯誤:
(1)對i進行限制,先把i定義成數組的長度少一,而後在每一輪比較前先對掃描到的索引處+i是否超過數組長度,不超過則進行比較,超過則對i遞減1,直到不超過數組長度
(2)也可對加i以後超過數組長度的索引處元素不予比較,緊接着比較下一索引處元素(固然這就更不可能啦。。。),也能夠遇到超過數組長度的索引處元素,直接將i減1,進行下一輪循環
思路(1)代碼以下:
int i,scan; for (i = data.length - 1;i>0;i--){ for (scan = 0; scan < data.length - 1; scan++) { if (scan + i < data.length) { if (data[scan].compareTo(data[scan + i]) > 0) swap(data, scan, scan + i); } } }
運行結果如圖:
思路(2)的代碼以下:
int i,scan; for (i = data.length - 1;i>0;i--){ for (scan = 0; scan < data.length - 1; scan++) { if (scan + i >= data.length) continue; if (data[scan].compareTo(data[scan + i]) > 0) swap(data, scan, scan + i); } }
用一個if語句來判斷是否間隔i個元素後過界,而後直接continue跳出循環,進行下一輪循環
運行結果跟上圖一致。
【更新】
通過與侯澤洋同窗的一番探討,我發現我看題有點不仔細:
每一輪迭代中,i減小的數量是一個大於1的數
這樣的話,在外層循環對i進行修改操做就沒問題了;
百度了方法以後很簡單,代碼以下:
long startTime=System.nanoTime(); //獲取開始時間 doSomeThing(); //測試的代碼段 long endTime=System.nanoTime(); //獲取結束時間 System.out.println("程序運行時間: "+(endTime-startTime)+"ns");
一開始用的是毫秒計算,可是結果顯示都是0ms,因而換了納秒計算;
有一個頗有趣的現象,就是已經排好序的列表排序的時間甚至比沒排好序的列表花費的時間還要多,如圖:
又嘗試運行了幾十次程序,也有亂序的運行時間長於順序的狀況,但大多數狀況仍是順序花費的時間更多
這是跟電腦有關係仍是一種巧合,仍是其它的什麼緣由?
暫時並無百度到相關的解釋,等找到了再來補充
還存在一個問題,就是,遞歸的計數與時間計算好像跟其它排序算法不同,不能直接一次輸出結果,每次調用本身,就會又一次把結果打印一遍,像這樣:
因此不能在遞歸方法裏寫計算時間差的方法,調用次數仍是能夠的,設一個全局變量,每次調用都會自增1,便可(可是輸出還得寫在測試類裏)
那時間的話,能夠統計開始調用這個方法到結束時計算機的時間,作差
那問題就是如何得到計算機時間(但時間精確度可能不高)
百度的方法有說能夠調用這個方法1000次,而後取千分之一,可是通過前面的體驗,方法的每一次調用花費的時間跟電腦的性能和狀態有很大關係,所以一次計算不是很準確,1000次求平均的話,可能更有表明性,更合理;
【更新】
有百度到能夠獲取當前精確時間(毫秒)的方法,這樣就簡單了,代碼以下:
Calendar Cld = Calendar.getInstance(); int YY = Cld.get(Calendar.YEAR) ;//年 int MM = Cld.get(Calendar.MONTH)+1;//月 int DD = Cld.get(Calendar.DATE);//日 int HH = Cld.get(Calendar.HOUR_OF_DAY);//時 int mm = Cld.get(Calendar.MINUTE);//分 int SS = Cld.get(Calendar.SECOND);//秒 int MI = Cld.get(Calendar.MILLISECOND);//毫秒 //由整型而來,所以格式不加0,如 2017/5/5-1:1:32:694 System.out.println(YY + "/" + MM + "/" + DD + "-" + HH + ":" + mm + ":" + SS + ":" + MI);
emmm,好像。。。
經過從新寫一個方法來調用這個排序的遞歸方法,而後在這個方法裏面再進行時間統計,就能夠避免重複打印屢次了,也就不用在測試類裏統計時間了,更符合題意,運行結果如圖:
也百度到一些計算遞歸方法運行的時間,但可能涉及到後面的內容,也沒有詳細講,看的不是很明白
【參考資料】
java如何計算程序運行時間
怎麼記錄遞歸函數的使用次數
解遞歸算法的運行時間的三種方法
無
代碼行數(新增/累積) | 博客量(新增/累積) | 學習時間(新增/累積) | 重要成長 | |
---|---|---|---|---|
目標 | 5000行 | 30篇 | 400小時 | |
第一週 | 0/0 | 1/1 | 4/4 | |
第二週 | 560/560 | 1/2 | 6/10 | |
第三週 | 415/975 | 1/3 | 6/16 | |
第四周 | 1055/2030 | 1/4 | 14/30 | |
第五週 | 1051/3083 | 1/5 | 8/38 |