排序演算法,按照自己的理解描述的
1> 插入排序:
基本思想: 把需要排序的集合分為兩部分,一部分是排序好的,一部分未排序的。就像打撲克一樣,如果你習慣從左到右按從小到達的方式排列,開始都背面蓋著放在桌上,然後抓起一張,抓第二張時,先和手上的第一張對比,如果點數大於第一張,那就把它插到第一張的右邊。剩下桌上的牌都按照這個方式去處理。
實際上,在用java陣列處理的時候,還是和玩撲克是有區別的。撲克的左右沒有邊界限制,而實際陣列有邊界限制,所以,需要考慮到邊界的問題,所以最好是從右到左查詢過去,不會產生越界的問題。
------------------用java陣列進行實現------------------------
從右到左查詢
---------------------------code-begin-----------------------
public static int[] insertSort(int[] rawArr) {
// 常識性的判斷,不要沒有牌或者牌只有1張
if (rawArr == null || rawArr.length < 2) {
return rawArr;
}
//從第二個數開始,第一張已經抓在手上了
for(int i = 1 ; i < rawArr.length ; i++ ) {
int j = i - 1 ; //對比手上的牌,最右邊的那張
int insertVal = rawArr[i] ; //待插入的牌
while(j > -1 && rawArr[j] > insertVal) { //如果牌比待插入的大,往左邊繼續看
rawArr[j+1] = rawArr[j]; //把牌向右挪一位
j-- ; //檢查左邊的牌
}
rawArr[j+1] = insertVal ; //最後,把牌插入正確的位置
//到下一個迴圈,也就是下一張沒上來的牌。
}
return rawArr ;
}
----------------------------code-end------------------------
2> 氣泡排序:
基本思想:
可以想想成陣列的樣子是從上到下的。如果排序是從大到小,那麼就是從底部開始兩兩對比,哪個數大就放到上面。這樣經過第一輪的對比,最大的數就會“浮”到最上面,也就是第一個位置。第二輪的時候,最上面的數就不用對比了,第二輪完畢之後,倒數第二大的數就會“浮”到倒數第二個位置。依次類推,最後迴圈完,就會按從大到小的順序排列了。
-------------------------code-begin-------------------------
public int[] popUpSort(int[] rawArr) {
if(rawArr == null || rawArr.length < 2) {
return rawArr ;
}
//有n個數,就是執行n-1次,最後一個數就不用比對了
for(int i = 1 ; i < rawArr.length ; i++) {
//兩個兩個對比,從下往上,到排序完的那個數為止(不包含)
for(int j = 0 ; j < rawArr.length - i ; j ++) {
if(rawArr[j] < rawArr[j+1]) {
int tmp = rawArr[j];
rawArr[j] = rawArr[j+1];
rawArr[j+1] = tmp ;
}
}
}
return rawArr ;
}
-------------------------code-end---------------------------
3> 歸併排序
基本思想:
一個無序的陣列,假如要從小到大進行排序。先分割成兩部分,然後每部分再進行分割,直到分割的部分只有一個元素時,就認為它是有序的。這時候,再進行合併,合併的流程就是新建一個臨時陣列,長度是兩個子陣列長度的和。然後分別掃描兩個子陣列(a和b),如果a[0]小於b[0],那就把a[0]先加到臨時陣列中(反之,就是把b[0]先加到臨時陣列中),然後a陣列向前移動一位,然後a[1]再與b[0]比較,比較小的就加到臨時陣列中。以此類推,當有一個數組新增完畢之後,而另一個數組有所剩餘,就把剩下的元素都加到臨時陣列中,這樣就完成了排序。然後遞迴的返回,然後合併,最終就得到整個陣列的排序後的陣列。
-------------------------code-begin-------------------------
public int[] mergeSort(int[] rawArr) {
if(rawArr == null && rawArr.length < 2) {
return rawArr;
}
return merge(rawArr);
}
private int[] merge(int[] slice) {
//只有一個元素,直接就是排序好的
if(slice.length == 1) {
return slice;
}
//切成兩部分
int half = slice.length / 2 ;
int[] aArr = getSubArrayOf(slice,0,half);
int[] bArr = getSubArrayOf(slice,half,slice.length);
//遞迴呼叫,這樣aArr和bArr就是排序好的了
aArr = merge(aArr) ;
bArr = merge(bArr) ;
//下面就進行合併
int aLength = aArr.length ;
int bLength = bArr.length ;
int totalLength = aLength + bLength ;
int[] mArr = new int[totalLength];
int i = 0 ;
int j = 0 ;
int k = 0 ;
//掃描兩個子陣列,根據條件進行排序
while(i < aLength && j < bLength) {
if(aArr[i] < bArr[j]) {
mArr[k++] = aArr[i++];
}else {
mArr[k++] = bArr[j++] ;
}
}
//如果是a陣列剩餘,就把剩下的加到父陣列中
while(i < aLength) {
mArr[k++] = aArr[i++];
}
//如果是b陣列剩餘,就把剩下的加到父陣列中
while( j < bLength) {
mArr[k++] = bArr[j++];
}
//合併好就返回
return mArr;
}
private int[] getSubArrayOf(int[] arr, int startIndex , int endIndex) {
if(endIndex < startIndex) {
return null;
}
int len = endIndex - startIndex ;
int[] newArr = new int[len];
for(int i = startIndex ; i < endIndex ; i++) {
newArr[i-startIndex] = arr[i];
}
return newArr;
}
-------------------------code-end---------------------------
4> 堆排序
堆是樹狀結構的,主要有兩個特點:
1) 根節點的值最小(最大)
2)子樹也是一個堆,稱為最小堆(最大堆)
二叉堆是完全二叉樹或者是近似完全二叉樹來構造的堆。
還有其他“堆”(二項式堆,斐波納契堆)
利用堆的結構,比如最小堆,那麼根節點是當前的樹狀結構中值最小的元素。
那麼,如果一組數要從小到大,先把它調節成一個最小堆,然後每次取它的根節點,然後刪除根節點,再對剩下的樹狀結構進行調整,重新成為一個最小堆。然後再取根節點,這樣迴圈的做下去,這樣,最後元素都取完了,那麼也就把資料都從小到大排序好了。
這樣,需要兩個步驟對排序進行輔助。
1)根節點刪除
2)調整成堆
按照陣列的方式儲存堆,那麼節點之間的關係為:
節點位置為i。那麼父節點位置為 i-1 / 2
兩個子節點的位置為 2*i + 1 與 2*i + 2
如果是一個數組,要使用堆的性質進行排序。那麼就是:
1)調整成堆
2)把根節點a[0]和最後一個節點a[n-1]對調。
然後再對a[0]到a[n-2]進行恢復堆,然後再把a[0]和a[n-2]進行對調。
迴圈下去,直到a[0],這樣就排好了。
5> 快速排序
----------------------------------------------------
http://www.cnblogs.com/surgewong/p/3381438.html
----------------------------------------------------
快速排序的基本思想: 通過一次迴圈,把需要排序的元素分割成兩部分A和B,長度不一定相等,但其中A中任何元素的值比B中任何元素的值都要小。
然後再遞迴呼叫,分別再對A和B進行切割,分別分成兩部分,也是一部分中的元素值比另一部分都小。迴圈分割,到只有一個數,那麼每段都是排序好了。
然後再遞迴返回,由於每一段都是已經排序好的,那麼返回的時候,兩段就不需要重新進行比較,直接連線起來就是排序好的。
那麼,具體怎麼在一次迴圈中就把資料分割成滿足要求的兩部分呢?
先選一個值x,然後分別從陣列的兩端進行掃描,比這個數小的就放到這個數的左邊,比這個數大的就放到這個數的右邊。
以下為例:
0 1 2 3 4 5 6 7
--------------------------------------------
| 46 | 30 | 82 | 90 | 56 | 17 | 95 | 15 |
--------------------------------------------
i=0 j=7
選第一個數 46 為x , 然後開始比較, a[j] = 15 < 46 ,那麼就交換兩個數的位置,同時i向前移動一位。
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 82 | 90 | 56 | 17 | 95 | 46 |
--------------------------------------------
i=1 j=7
然後再比較, a[i] = 30 < 46 ,那麼不動,i再移動一位
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 82 | 90 | 56 | 17 | 95 | 46 |
--------------------------------------------
i=2 j=7
然後再比較,a[i] = 82 > 46 ,那麼交換兩者的位置,然後j向後移動一位。
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 46 | 90 | 56 | 17 | 95 | 82 |
--------------------------------------------
i=2 j=6
然後比較 a[j] = 82 > 46 , 不動,j再向後移動一位
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 46 | 90 | 56 | 17 | 95 | 82 |
--------------------------------------------
i=2 j=5
然後再比較, a[j] = 17 < 46 ,兩者交換,i向前移動一位
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 17 | 90 | 56 | 46 | 95 | 82 |
--------------------------------------------
i=3 j=5
再比較 a[i] = 90 > 46 ,兩者交換,j向後移動一位
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 17 | 46 | 56 | 90 | 95 | 82 |
--------------------------------------------
i=3 j=4
再比較 a[j] = 56 > 46 ,不交換,j向後移動一位
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 30 | 17 | 46 | 56 | 90 | 95 | 82 |
--------------------------------------------
i=j=3
此時發現i=j=3,這時候,此次分割結束。
這樣,陣列被分割成[0-2][3][4-7]三個區間,然後再遞迴的對[0-2]和[4-7]按照以上方式進行分割。
最後分割成一個個數之後再分別合併起來,組成一個有序的序列。
-------------------------code-begin-------------------------
private void quick_sort(int[] rawArr,int left , int right) {
if(left < right) {
int i = left ;
int j = right ;
int x = rawArr[i];
while( i < j ) {
while(i < j && rawArr[j] < x) {
j-- ;
}
if(i < j) {
rawArr[i++] = rawArr[j] ;
}
while(i < j && rawArr[i] >= x) {
i++ ;
}
if(i < j) {
rawArr[j--] = rawArr[i];
}
}
rawArr[i] = x;
quick_sort(rawArr,left,i-1);
quick_sort(rawArr,i+1,right);
}
-------------------------code-end---------------------------
6> shell排序
通過不斷“對摺”,然後對各個分組進行“插入排序”。
例如以下資料:
有8個數,第一次先取 gap = 4
0 1 2 3 4 5 6 7
--------------------------------------------
| 46 | 30 | 82 | 90 | 56 | 17 | 95 | 15 |
--------------------------------------------
這樣資料就分為了(0,4)(1,5)(2,6)(3,7)總共4組。 然後對每組元素分別進行“插入排序”。
0 1 2 3 4 5 6 7
--------------------------------------------
| 46 | 17 | 82 | 15 | 56 | 30 | 95 | 90 |
-----------------------------------------
然後再“對摺”,gap = gap / 2 = 2。這樣分為了2組。
(0,2,4,6)(1,3,5,7) ,然後再對每組進行“插入排序”。
0 1 2 3 4 5 6 7
--------------------------------------------
| 46 | 15 | 56 | 17 | 82 | 30 | 95 | 90 |
--------------------------------------------
然後再“對摺”,gap= gap / 2 = 1 這樣就只有1組。(0,1,2,3,4,5,6,7) 再進行插入排序。
0 1 2 3 4 5 6 7
--------------------------------------------
| 15 | 17 | 30 | 46 | 56 | 82 | 90 | 95 |
--------------------------------------------
然後再對摺 gap = gap / 2 = 0 結束。
-------------------------code-begin-------------------------
for(gap = n / 2 ; gap > 0 ; gap /= 2) { // count of split
for(int i = 0 ; i < gap ; i++) { // group size
for(int j = i + gap ; j < n ; j += gap) {
if(rawArr[j] < rawArr[ j- gap ]) {
int insertVal = rawArr[j] ;
int k = j - gap ;
while(k >= 0 && rawArr[k] > insertVal) {
rawArr[k + gap] = rawArr[k] ;
k -= gap ;
}
rawArr[k + gap] = insertVal ;
}
}
}
}
-------------------------code-end---------------------------