柚子快報(bào)激活碼778899分享:JAVA 詳解常見排序
柚子快報(bào)激活碼778899分享:JAVA 詳解常見排序
目錄
?編輯
插入排序
希爾排序(縮小增量排序)
選擇排序
冒泡排序
堆排序
快速排序
hoare版
?挖坑法
前后指針法
非遞歸版
歸并排序
遞歸版
非遞歸版
計(jì)數(shù)排序
聲明:以下排序代碼由Java實(shí)現(xiàn)!!!
插入排序
步驟:
1.我們可以認(rèn)為數(shù)組的第一個元素已經(jīng)被排好序,因此只需考慮對后面的元素進(jìn)行插入排序;
2.取下一個位置的元素val,讓它和它之前的元素進(jìn)行比較,順序?yàn)閺挠蚁蜃螅?/p>
3.如果該元素大于val,則將該元素移動到該元素所處位置的下一個位置;
4.重復(fù)步驟3,知道找到已排好序的序列中小于等于val的元素;
5.將值val放到該位置的下一個位置,如果已排好序的所有元素的值都大于val,則將val存放到數(shù)組下標(biāo)為0的位置;
6.重復(fù)2~5步驟。
動畫演示:
代碼如下:
public static void insertSort(int[] array){
for(int i=1;i int tmp=array[i]; int j=i-1; for(;j>=0;j--){ if(array[j]>tmp) array[j+1]=array[j]; else break; } array[j+1]=tmp; } } 折半插入排序? 在該值為val的元素找合適的位置時,是在已排好序的序列中進(jìn)行找的,因此該過程可以使用二分查找(折半查找)來進(jìn)行優(yōu)化。 代碼如下: public static void bsInsertSort(int[] array){ for(int i=0;i int tmp=array[i]; int left=0,right=i; while(left int mid=(left+right)>>1; if(tmp>=array[mid]) left=mid+1; else right=mid; } for(int j=i;j>left;j--) array[j]=array[j-1]; array[left]=tmp; } } 時間復(fù)雜度:最好情況下為O(N),此時待排數(shù)組為升序,或者說非常接近升序; ? ? ? ? ? ? ? ? ? ? ?最壞情況下為O(N^N),此時待排數(shù)組為降序,或者說非常接近降序。 空間復(fù)雜度:O(1) 穩(wěn)定性:穩(wěn)定 希爾排序(縮小增量排序) 思想: 先選定一個小于N的整數(shù)gap作為第一增量,然后將所有距離為gap的元素劃分在同一組,對每組的元素進(jìn)行直接插入排序,然后再選取一個比第一增量小的整數(shù)作為第二增量gap,然后將所有距離為gap的元素劃分在同一組,對每組的元素進(jìn)行直接插入排序,以此類推.........,直到增量減小為1時,此時就相當(dāng)于整個數(shù)組被劃分為一組,進(jìn)行一次直接插入排序即可。 增量gap大于1時,稱為“預(yù)排序”,使得待排數(shù)組接近有序;增量gap為1時,稱為直接插入排序。 動畫演示 代碼如下: public static void shellSort(int[] array){ int gap=array.length; while(gap>1){ gap=gap/3+1; shell(array,gap); } } private static void shell(int[] array,int gap){ for(int i=gap;i int tmp=array[i]; int j=i-gap; for(;j>=0;j-=gap){ if(array[j]>tmp) array[j+gap]=array[j]; else break; } array[j+gap]=tmp; } } 平均時間復(fù)雜度:O(N^1.3) 空間復(fù)雜度:O(1) 選擇排序 每一次從待排序列中選出最?。ɑ蛘咦畲螅┑囊粋€元素,存放在待排序列的起始位置,同時將待排序列原來起始位置的值放到待排序列中最小元素的位置,直到待排數(shù)據(jù)全部排完序。 動畫演示 代碼如下: public static void selectSort(int[] array){ for(int i=0;i int minIndex=i; for(int j=i+1;j if(array[j] minIndex=j; swap(array,i,minIndex); } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 實(shí)際上,我們可以每一趟同時選擇出待排序列中的最小值和最大值,然后將最小值放待排序列起始位置,將最大值放到待排序列的末尾,直到待排數(shù)據(jù)全部排完序,這樣的話比前一種方法快一倍。 代碼如下: public static void DoubleSelectSort(int[] array){ int left=0,right=array.length-1; while(left int minIndex=left,maxIndex=left; for(int i=left+1;i<=right;i++){ if(array[i]>array[maxIndex]) maxIndex=i; if(array[i] minIndex=i; } swap(array,left,minIndex); if(maxIndex==left) maxIndex=minIndex; swap(array,right,maxIndex); left++; right--; } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 時間復(fù)雜度:O(N^2) 空間復(fù)雜度:O(1) 冒泡排序 進(jìn)行N趟,每一趟中,如果前一個位置的元素大于后一個位置的元素,則交換兩個位置的元素。 動畫演示 代碼如下: public static void bubbleSort(int[] array){ for(int i=0;i boolean flag=false; for(int j=0;j if(array[j]>array[j+1]) { swap(array, j, j + 1); flag=true; } } if(!flag) break; } } 時間復(fù)雜度:O(N^2) 空間復(fù)雜度:O(1) 堆排序 排升序建大根堆,排降序建小根堆。 以排升序?yàn)槔?,先對?shù)組建立大根堆,然后將堆頂元素和堆最后一個元素交換,然后從堆頂進(jìn)行向下調(diào)整,不過此時堆的大小要 -1,因?yàn)橐呀?jīng)把最大的元素找出來并放在數(shù)組的末尾了,不斷重復(fù)上述操作,直到將整個數(shù)組的元素排完序。 動畫演示: 代碼如下: public static void heapSort(int[] array){ createBigHeap(array); int end=array.length-1; while(end>0){ swap(array,0,end); shiftDown(array,0,end); end--; } } private static void createBigHeap(int[] array){ for(int parent=array.length-1-1;parent>=0;parent--) shiftDown(array,parent,array.length); } private static void shiftDown(int[] array,int parent,int end){ int child=2*parent+1; while(child if(child+1 child++; if(array[parent] swap(array,parent,child); parent=child; child=2*parent+1; } else break; } } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(1) 快速排序 hoare版 步驟: 1.選定一個值Key,下標(biāo)記為Keyi,通常是最左邊的元素或者最右邊的元素; 2.定義兩個指針begin和end,being從左往右走,end從右往左走; 3.若選定的Key值是最左邊的元素,需要end先走,若選定的Key值是最右邊的元素,需要begin先走; 4.在走的過程中,當(dāng)end遇到小于Key的數(shù),才停下;然后begin開始走,直到遇到大于Key的數(shù),然后交換位于begin和end位置的值,然后end再走,規(guī)則如上,直到begin和end相遇,此時再將位于Keyi位置的Key值和begin和end相遇點(diǎn)的值進(jìn)行交換; 5.此時,Key左邊的值都是小于等于Key的數(shù),Key右邊的值都是大于等于Key的數(shù); 6.然后再對Key左邊的數(shù)以及Key右邊的數(shù)分別進(jìn)行如上操作,直到待排序列只有一個元素為止。 動畫演示: 代碼如下:? 因?yàn)樵摲椒ò罅康倪f歸,當(dāng)數(shù)據(jù)量較大時會發(fā)生棧溢出,因此做一些優(yōu)化,包括【三數(shù)取中】和【當(dāng)待排區(qū)間長度小于某個常數(shù)時,不再遞歸進(jìn)行快排,而是使用直接插入排序】 public static void quickSort(int[] array){ quick(array,0,array.length-1); } private static void quick(int[] array,int start,int end){ if(start>=end) return; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionHoare(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(int[] array,int begin,int end) { for (int i = begin+1; i <= end; i++) { int tmp = array[i]; int j = i-1; for (; j >= begin ; j--) { if(array[j] > tmp) { array[j+1] = array[j]; }else { break; } } array[j+1] = tmp; } } private static int midOfThree(int[] array, int left, int right) { int mid = (left+right) / 2; if(array[left] < array[right]) { if(array[mid] < array[left]) { return left; }else if(array[mid] > array[right]) { return right; }else { return mid; } }else { if(array[mid] > array[left]) { return left; }else if(array[mid] < array[right]) { return right; }else { return mid; } } } private static int partitionHoare(int[] array,int left,int right){ int key=array[left]; int keyi=left; while(left while(left right--; while(left left++; swap(array,left,right); } swap(array,keyi,left); return left; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(1)? ?挖坑法 步驟: 1.選定一個Key值,通常是位于最左邊或者是最右邊,在該位置形成一個坑; 2.定義兩個指針left和right,left從左向右走,right從左向左走; 3.如果Key值位于數(shù)組最左邊,需要right先走;如果Key值位于數(shù)組組右邊,需要left先走; 4.在走的過程中,當(dāng)end遇到小于Key的數(shù),才停下,然后將right位置的值填放到坑位置,此時right位置處形成一個坑;然后begin開始走,直到遇到大于Key的數(shù),然后將left位置的值填放到坑位置,此時left位置處形成一個坑;然后right再走,規(guī)則如上,直到left和right相遇,此時再將位于Key值填放到坑位置處; 5.此時,Key左邊的值都是小于等于Key的數(shù),Key右邊的值都是大于等于Key的數(shù); 6.然后再對Key左邊的數(shù)以及Key右邊的數(shù)分別進(jìn)行如上操作,直到待排序列只有一個元素為止。 動畫演示: 代碼如下:? 因?yàn)樵摲椒ò罅康倪f歸,當(dāng)數(shù)據(jù)量較大時會發(fā)生棧溢出,因此做一些優(yōu)化,包括【三數(shù)取中】和【當(dāng)待排區(qū)間長度小于某個常數(shù)時,不再遞歸進(jìn)行快排,而是使用直接插入排序】 public static void quickSort(int[] array){ quick(array,0,array.length-1); } private static void quick(int[] array,int start,int end){ if(start>=end) return; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionHole(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(int[] array,int begin,int end) { for (int i = begin+1; i <= end; i++) { int tmp = array[i]; int j = i-1; for (; j >= begin ; j--) { if(array[j] > tmp) { array[j+1] = array[j]; }else { break; } } array[j+1] = tmp; } } private static int midOfThree(int[] array, int left, int right) { int mid = (left+right) / 2; if(array[left] < array[right]) { if(array[mid] < array[left]) { return left; }else if(array[mid] > array[right]) { return right; }else { return mid; } }else { if(array[mid] > array[left]) { return left; }else if(array[mid] < array[right]) { return right; }else { return mid; } } } private static int partitionHole(int[] array,int left,int right){ int key=array[left]; while(left while(left right--; array[left]=array[right]; while(left left++; array[right]=array[left]; } array[left]=key; return left; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(1)? 前后指針法 步驟: 1.選定數(shù)組最左邊的值為基準(zhǔn)值; 2.定義兩個指針prev和cur,開始時prev在最左邊,cur在prev的下一個位置; 3.讓cur向右走,如果cur位置的值大于基準(zhǔn)值,只需cur繼續(xù)向右移動,直到遇到比基準(zhǔn)值小的值; 4.如果cur位置的值小于基準(zhǔn)值,先讓prev向右移動一個位置,然后交換prev位置的值和cur位置的值,直到cur走到數(shù)組末尾。 代碼如下: 因?yàn)樵摲椒ò罅康倪f歸,當(dāng)數(shù)據(jù)量較大時會發(fā)生棧溢出,因此做一些優(yōu)化,包括【三數(shù)取中】和【當(dāng)待排區(qū)間長度小于某個常數(shù)時,不再遞歸進(jìn)行快排,而是使用直接插入排序】 public static void quickSort(int[] array){ quick(array,0,array.length-1); } private static void quick(int[] array,int start,int end){ if(start>=end) return; if(end-start<=7){ insertSortRange(array,start,end); return; } int pivot=partitionDouble(array,start,end); quick(array,start,pivot-1); quick(array,pivot+1,end); } private static void insertSortRange(int[] array,int begin,int end) { for (int i = begin+1; i <= end; i++) { int tmp = array[i]; int j = i-1; for (; j >= begin ; j--) { if(array[j] > tmp) { array[j+1] = array[j]; }else { break; } } array[j+1] = tmp; } } private static int midOfThree(int[] array, int left, int right) { int mid = (left+right) / 2; if(array[left] < array[right]) { if(array[mid] < array[left]) { return left; }else if(array[mid] > array[right]) { return right; }else { return mid; } }else { if(array[mid] > array[left]) { return left; }else if(array[mid] < array[right]) { return right; }else { return mid; } } } private static int partitionDouble(int[] array,int left,int right){ int prev=left,cur=prev+1; while(cur if(array[cur] swap(array,prev,cur); cur++; } swap(array,prev,left); return prev; } private static void swap(int[] array,int i,int j){ int ret=array[i]; array[i]=array[j]; array[j]=ret; } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(1)? 非遞歸版 代碼如下: public static void quickSortNor(int[] array) { Stack int left = 0; int right = array.length-1; int piovt = partitionHole(array,left,right); if(piovt - 1 > left) { stack.push(left); stack.push(piovt-1); } if(piovt + 1 < right) { stack.push(piovt+1); stack.push(right); } while (!stack.isEmpty()) { right = stack.pop(); left = stack.pop(); piovt = partitionHole(array,left,right); if(piovt - 1 > left) { stack.push(left); stack.push(piovt-1); } if(piovt + 1 < right) { stack.push(piovt+1); stack.push(right); } } } 歸并排序 遞歸版 使用遞歸不斷將區(qū)間二分,直到區(qū)間只有一個元素為止,然后將兩個區(qū)間進(jìn)行排序合并,直到將所有區(qū)間合并完。 代碼如下: public static void mergeSort(int[] array){ int[] dst=new int[array.length]; dst= Arrays.copyOf(array,array.length); merge(array,dst,0,array.length-1); for(int i=0;i array[i]=dst[i]; } private static void merge(int[] src,int[] dst,int start,int end){ if(start>=end) return; int mid=(start+end)>>1; merge(dst,src,start,mid); merge(dst,src,mid+1,end); int i=start,j=mid+1,k=start; while(i<=mid || j<=end){ if(j>end || (i<=mid && src[i] dst[k++]=src[i++]; else dst[k++]=src[j++]; } } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(N)? 非遞歸版 將整個區(qū)間劃分為長度為1,2,4,8,..........最大為N的小區(qū)間,然后對相鄰的長度為1,2,4,8,..........最大為N的小區(qū)間分別進(jìn)行排序合并,最終就排好序了。 代碼如下: public static void mergeSortNor(int[] array){ int[] src=array; int[] dst=new int[array.length]; for(int step=1;step for(int start=0;start int mid=Math.min(start+step-1,array.length-1); int end=Math.min(start+2*step-1,array.length-1); int i=start,j=mid+1,k=start; while(i<=mid || j<=end){ if(j>end || (i<=mid && src[i] dst[k++]=src[i++]; else dst[k++]=src[j++]; } } int[] tmp=src; src=dst; dst=tmp; } for(int i=0;i array[i]=src[i]; } 時間復(fù)雜度:O(N*logN) 空間復(fù)雜度:O(N)? 計(jì)數(shù)排序 先求出序列中的最大值maxVal和最小值minVal,然后開辟一個長度為maxVal-minVal+1的數(shù)組,值全都初始化為0,然后遍歷整個數(shù)組,將下標(biāo)為每個位置的值減去minVal處的值++,然后再重復(fù)將每個位置的值表示的次數(shù)次,將對應(yīng)的值【下標(biāo)+minVal】存放到原數(shù)組中。 動畫演示: 代碼如下:? public static void countSort(int[] array) { int minVal = array[0]; int maxVal = array[0]; for (int i = 1; i < array.length; i++) { if(array[i] < minVal) { minVal = array[i]; } if(array[i] > maxVal) { maxVal = array[i]; } } int[] count = new int[maxVal-minVal+1]; for (int i = 0; i < array.length; i++) { count[array[i]-minVal]++; } int index = 0; for (int i = 0; i < count.length; i++) { while (count[i] > 0) { array[index] = i+minVal; index++; count[i]--; } } } 時間復(fù)雜度:O(N) 空間復(fù)雜度:O(N)? 柚子快報(bào)激活碼778899分享:JAVA 詳解常見排序 文章來源
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。