十大排序算法(Java)
文章目錄
- 十大排序算法(Java)
- 一、冒泡排序(Bubble Sort)
- 二、選擇排序(Selection Sort)
- 三、堆排序(Heap Sort)
- 四、插入排序(Insertion Sort)
- 五、歸并排序(Merge Sort)
- 六、快速排序(Quick Sort)
- 七、希爾排序(Shell Sort)
- 八、計數排序(Counting Sort)
- 九、基數排序(Radix Sort)
- 十、桶排序(Bucket Sort)
- 擴展:十一、史上“最強”排序-休眠排序666(僅供參考,不要用!!)
十大排序算法(Java)
小猴子自定義口訣(瞎編的,哈哈):冒泡兩個兩個對比,選擇最大的排在最后,插入前面有序列表,歸并先分區后合并,快速將一列數劃分小于pivot在左邊大于pivot在右邊再依次遞歸左右子列,希爾分列排序每列排序算法借用插入;以上是基于比較的排序算法
以下不是基于比較的排序算法,(以空間換時間),計數計算每個數出現的次數然后從小到大排序,基數非常適合非負整數個十百分別比較,桶是把每個數分到相應的桶再排序
各個排序圖片歸納:
一、冒泡排序(Bubble Sort)
冒泡排序是一種簡單的排序算法。它重復地走訪過要排序的數列,一次比較兩個元素,如果它們的順序錯誤就把它們交換過來。走訪數列的工作是重復地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列的頂端。
//原始排序(無優化) public void bubbleSort(Integer[] array){for(int end=array.length-1;end>0;end--){for(int begin=1;begin<=end;begin++){if(array[begin] < array[begin-1]){int temp = array[begin];array[begin] = array[begin-1];array[begin-1] = temp;}}} }//優化方案1(當數組完全有序或者數組在循環少量次數后就有序時) public void bubbleSort1(Integer[] array){for(int end=array.length-1;end>0;end--){boolean sorted = true;for(int begin=1;begin<=end;begin++){if(array[begin] < array[begin-1]){int temp = array[begin];array[begin] = array[begin-1];array[begin-1] = temp;sorted = false;}}if(sorted){break;}} }//優化方案2(當數組中后面部分完全有序) public void bubbleSort2(Integer[] array){for(int end=array.length-1;end>0;end--){//sortedIndex的初始值在數組完全有序的時候有用int sortedIndex = 1;for(int begin=1;begin<=end;begin++){if(array[begin] < array[begin-1]){int temp = array[begin];array[begin] = array[begin-1];array[begin-1] = temp;sortedIndex = begin;}}end = sortedIndex;} }二、選擇排序(Selection Sort)
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再從剩余未排序元素中繼續尋找最小(大)元素,然后放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
public void selectionSort(Integer[] arr){for(int end=arr.length-1;end>0;end--){int maxIndex = 0;for(int begin=1;begin<=end;begin++){if(arr[maxIndex] <= arr[begin]){ //增加其穩定性(=)maxIndex = begin;}}int temp = arr[maxIndex];arr[maxIndex] = arr[end];arr[end] = temp;} }三、堆排序(Heap Sort)
(可以看做對選擇排序的優化)
堆排序(Heap-sort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似完全二叉樹的結構,并同時滿足堆積的性質:即子結點的鍵值或索引總是小于(或者大于)它的父節點。
//重構代碼,寫一個父類,其他排序都繼承這個抽象類,重寫sort方法 public abstract class Sort{protected Integer[] arr;protected int cmpCount; //比較的次數 protected int swapCount; //交換的次數 private long time;public void sort(Integer[] arr){if(arr == null || arr.length < 2) return;this.arr = arr;long begin = System.currentTimeMillis();sort();time = System.currentTimeMillis() - begin;//運行時間}protected abstract void sort();//比較:等于0 arr[i] == arr[j]//小于0 arr[i] < arr[j]//大于0 arr[i] > arr[j]protected int cmp(int i,int j){cmpCount++;return arr[i] - arr[j];}//比較元素大小protected int compare(Integer v1,Integer v2){return v1 - v2;}//交換protected void swap(int i,int j){swapCount++;int temp = arr[i];arr[i] = arr[j];arr[j] = temp;}@Overridepublic String toString(){String string = getClass().getSimpleName()+" "+"耗時:"+(time/1000.0)+"s("+time+"ms)";return string;} } //注:上面兩個排序的代碼可重寫,這里我就不寫了 //交換堆頂元素與尾元素 //堆的元素數量減1 //對0位置進行1次shiftDown操作 public class HeapSort extends Sort{private int heapSize;@Overrideprotected void sort(){heapSize = arr.length; //原地建堆//heapSize>>1表示把heapSize右移1位,相當于heapSize/2for(int i = (heapSize >> 1)-1;i >= 0;i--){ shiftDown(i);}while(heapSize > 1){//交換堆頂元素與尾元素//swap(0,heapSize-1);//heapSize--;swap(0,--heapSize);//合并以上兩步//對0位置進行1次shiftDown操作(恢復堆的性質)shiftDown(0);}}private void shiftDown(int index){Integer element = arr[index];int half = heapSize >> 1;while(index < half){ //index必須是非葉子節點//默認是左邊跟父節點比int childIndex = (index << 1)+1;Integer child = arr[childIndex];//右子節點比左子節點大int rightIndex = childIndex + 1;if(rightIndex < heapSize && compare(arr[rightIndex],child) > 0){child = arr[childIndex = rightIndex];}//大于等于子節點if(compare(element,child) >= 0) break;arr[index] = child;index = childIndex;}arr[index] = element;} }四、插入排序(Insertion Sort)
類似撲克牌的排序
插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工作原理是通過構建有序序列,對于未排序數據,在已排序序列中從后向前掃描,找到相應位置并插入。
public class InsertionSort extends Sort{@Overrideprotected void sort(){for(int begin=1;begin<arr.length;begin++){int cur = begin;//要把當前begin記錄下來,不能改變,所以用cur來代替begin變化while(cur > 0 && cmp(cur,cur-1) < 0){swap(cur,cur-1);cur--;}}}//優化后的代碼1@Overrideprotected void sort(){for(int begin=1;begin<arr.length;begin++){int cur = begin;//要把當前begin記錄下來,不能改變,所以用cur來代替begin變化Integer v = arr[cur];while(cur > 0 && cmp(cur,cur-1) < 0){arr[cur] = arr[cur-1]; //逐個往右移一個位置cur--;}arr[cur] = v; //最后前面空出來的那個位置,v直接插入進去}} //利用二分搜索優化代碼2(見下面方法search)@Overrideprotected void sort(){for(int begin=1;begin<arr.length;begin++){int v = arr[begin];int searchIndex = search(begin);//將searchIndex到begin位置的元素往右邊挪動一位//for(int i=begin-1;i>=searchIndex;i--){// arr[i+1] = arr[i];//}for(int i=begin;i>searchIndex;i--){arr[i] = arr[i-1];}arr[searchIndex] = v;}} //利用二分搜索找到index位置元素的待插入位置//已經排好序數組的區間范圍值[0,index)private int search(int index){int v = arr[index];int begin = 0;int end = index-1; //左閉右開區間 while(begin < end){int mid = (begin+end) >>1;if(v <arr[mid]){ //compare(v,arr[mid])<0end = mid;}else{begin = mid+1;}}return begin;}}//寫一下二分搜索(可參考學習) public class BinarySearch{//查找v在有序數組array中的位置public static int indexOf(int[] array,int v){ //返回索引if(array == null ||array.length == 0){return -1;}int begin = 0;int end = array.length; //左閉右開區間 while(begin < end){int mid = (begin+end)/2;if(v<array[mid]){end=mid;}else if(v>array[mid]){begin=mid+1;}else{return mid;}}return -1;}//查找v在有序數組array中的插入位置,第一個比v大的位置的索引public static int search(int[] array,int v){if(array == null ||array.length == 0){return -1;}int begin = 0;int end = array.length; //左閉右開區間 while(begin < end){int mid = (begin+end) >>1;if(v <array[mid]){end = mid;}else{begin = mid+1;}}return begin;}}五、歸并排序(Merge Sort)
歸并排序是建立在歸并操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。將已有序的子序列合并,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合并成一個有序表,稱為2-路歸并。
public class MergeSort extends Sort{private Integer[] leftArray;@Overrideprotected void sort(){leftArray = new Integer[arr.length >> 1];sort(0,arr.length);}//對[begin,end)范圍內的數據進行歸并排序private void sort(int begin,int end){if(end - begin < 2) return;//只有一個元素的情況int mid = (begin+end) >> 1;sort(begin,mid); //都是左閉右開區間sort(mid,end); //都是左閉右開區間merge(begin,mid,end);}//合并//將[begin,end)與[mid,end)范圍內的序列合并成一個有序的序列private void merge(int begin,int mid,int end){int li = 0,le = mid -begin;int ri = mid,re = end;int ai = begin;//備份左邊數組for(int i=li;i<le;i++){leftArray[i] = arr[begin+i];}while(li<le){ //如果左邊還沒有結束if(ri<re && leftArray[li] > arr[ri]){arr[ai] = arr[ri];ri++;ai++;}else{arr[ai++] = leftArray[li++];}}}}六、快速排序(Quick Sort)
快速排序的基本思想:通過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分的關鍵字小,則可分別對這兩部分記錄繼續進行排序,以達到整個序列有序。
/*快速排序的本質:逐漸將每一個元素都轉換成軸點元素(遞歸的過程)*/public class QuickSort extends Sort{@Overrideprotected void sort(){//sort(0,arr.length);sort2(0,arr.length-1);}//對[begin,end)范圍的元素進行快速排序private void sort2(int begin,int end){if(end-begin<2) return;//確定軸點位置int mid = partition(begin,end);//對子序列進行快速排序sort(begin,mid-1);sort(mid+1,end);}private int partition(int begin,int end){int privot = begin,index = privot+1;for(int i=index;i<=end;i++){if(arr[privot] > arr[i]){swap(i,index);index++;}}swap(privot,index-1);return index-1;}private void swap(int a,int b){int temp = arr[a];arr[a] = arr[b];arr[b] = temp;}//對[begin,end)范圍的元素進行快速排序private void sort(int begin,int end){if(end-begin<2) return;//確定軸點位置int mid = pivotIndex(begin,end);//對子序列進行快速排序sort(begin,mid);sort(mid+1,end);}//確定[begin,end)范圍的軸點位置,return begin=end的位置pivotprivate int pivotIndex(int begin,int end){//備份begin位置的元素Integer v = arr[begin]; //arr[pivot] = v//end指向最后一個元素end--;while(begin < end){while(begin < end){if(arr[end] > v){end--;}else{arr[begin] = arr[end];begin++;break; //只退出當前循環,掉頭了(本來是從右到左查找對比,只要arr[end]<v了就掉頭,從begin方向往end方向查找)}}while(begin < end){if(arr[begin] < v){begin++;}else{arr[end] = arr[begin];end--;break;}}}//將軸點元素放入最終的位置arr[begin] = v;return begin;//或者end,因為begin=end=pivot軸點元素的位置} }七、希爾排序(Shell Sort)
1959年Shell發明,第一個突破O(n2)的排序算法,是簡單插入排序的改進版。它與插入排序的不同之處在于,它會優先比較距離較遠的元素。希爾排序又叫縮小增量排序。
public class ShellSort extends Sort{@Overrideprotected void sort(){List<Integer> stepSequence = myStepSequence(); //步長序列{8,4,2,1} for(Integer step:stepSequence){sort(step);}}//分成step列進行排序private void sort(int step){//col:第幾列 //對第col列進行排序for(int col = 0;col < step;col++){//col,col+step,col+2*step,col+3*stepfor(int begin=col+step;begin<arr.length;begin+=step){int cur = begin;//要把當前begin記錄下來,不能改變,所以用cur來代替begin變化while(cur > 0 && cmp(cur,cur-step) < 0){swap(cur,cur-step);cur-=step;}}}}private List<Integer> myStepSequence(){List<Integer> stepSequence = new ArrayList<>();int step = arr.length;while((step >> 1) > 0){stepSequence.add(step);}return stepSequence;} }八、計數排序(Counting Sort)
計數排序不是基于比較的排序算法,其核心在于將輸入的數據值轉化為鍵存儲在額外開辟的數組空間中。 作為一種線性時間復雜度的排序,計數排序要求輸入的數據必須是有確定范圍的整數
public class CountingSort extends Sort{@Overrideprotected void sort(){//找出最大值int max = arr[0];for(int i=1;i<arr.length;i++){if(arr[i] > max){max = arr[i];}}//開辟內存空間,存儲每個整數出現的次數int[] counts = new int[1+max];//統計每個整數出現的次數for(int i=0;i<arr.length;i++){counts[arr[i]]++;}//根據整數的出現次數,對整數進行排序int index=0;for(int i=0;i<counts.length;i++){while(conuts[i]-- >0){arr[index++] = i;}}} }//優化(內存浪費,無負數,不穩定) public class CountingSort extends Sort{@Overrideprotected void sort(){//找出最大值、最小值int max = arr[0];int min = arr[0];for(int i=1;i<arr.length;i++){if(arr[i] > max){max = arr[i];}for(int i=1;i<arr.length;i++){if(arr[i] < min){min = arr[i];}//開辟內存空間 存儲次數int[] counts = new int[max-min+1];//統計每個整數出現的次數for(int i=0;i<arr.length;i++){counts[arr[i] - min]++;}//累加次數 for(int i=1;i<counts.length;i++){counts[i] += counts[i-1];}//從后往前遍歷元素,將其放到有序數組中的合適位置int[] newArr = new int[arr.length];for(int i=arr.length;i>0;i--){//--counts[arr[i]-min] arr[i]-min就是這個數,counts[arr[i]-min]就是次數(前面次數和)要先減一才是新數組的索引newArr[--counts[arr[i]-min]] = arr[i]; //理解} //將有序數組賦值到arrfor(int i=0;i<arr.length;i++){arr[i]=newArr[i];}} }九、基數排序(Radix Sort)
基數排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次類推,直到最高位。有時候有些屬性是有優先級順序的,先按低優先級排序,再按高優先級排序。最后的次序就是高優先級高的在前,高優先級相同的低優先級高的在前。(非常適合非負整數)
public class RadixSort extends Sort{@Overrideprotected void sort(){//找出最大值int max = arr[0];for(int i=1;i<arr.length;i++){if(arr[i] > max){max = arr[i];}}//max=593 593%10=3 593/10%10=9 593/100%10=5for(int divider = 1;divider <= max;divider *= 10){countingSort(divider);}}//基于計數排序protected void countingSort(int divider){ //divider=1、10、100、1000、... //開辟內存空間 存儲次數int[] counts = new int[10]; //0-9//統計每個整數出現的次數for(int i=0;i<arr.length;i++){counts[arr[i] / divider % 10]++;}//累加次數 for(int i=1;i<counts.length;i++){counts[i] += counts[i-1];}//從后往前遍歷元素,將其放到有序數組中的合適位置int[] newArr = new int[arr.length];for(int i=arr.length;i>0;i--){//--counts[arr[i]-min] arr[i]-min就是這個數,counts[arr[i]-min]就是次數(前面次數和)要先減一才是新數組的索引newArr[--counts[arr[i] / divider % 10] = arr[i]; //理解} //將有序數組賦值到arrfor(int i=0;i<arr.length;i++){arr[i]=newArr[i];}} }十、桶排序(Bucket Sort)
桶排序是計數排序的升級版。它利用了函數的映射關系,高效與否的關鍵就在于這個映射函數的確定。桶排序 (Bucket sort)的工作的原理:假設輸入數據服從均勻分布,將數據分到有限數量的桶里,每個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排)。(當數是小數時)
public class BucketSort extends Sort{@Overrideprotected void sort(){//桶數組List<Double>[] buckets = new List[arr.length];for(int i=0;i<arr.length;i++){int bucketIndex = (int) (arr[i] * arr.length);List<Double> bucket = buckets[bucketIndex];if(bucket == null){bucket = new LinkedList<>();buckets[bucketIndex] = bucket;}bucket.add(arr[i]);}//對每個桶進行排序int index = 0;for(int i=0;i<buckets;i++){if(buckets[i] == null) continue;buckets[i].sort(null);//java內部的排序方法for(Double d:buckets[i]){arr[index++] = d;}}} }擴展:十一、史上“最強”排序-休眠排序666(僅供參考,不要用!!)
private static class SortThread extends Thread{private int value;public SortThread(int value){this.value = value;}public void run(){try{Thread.sleep(value);System.out.println(value);}catch(IntereuptedException e){e.printStackTrace();}} }public class SortThreadTest{public static void main(String[] args){int[] array = [10,100,50,60,30];for(int i=0;i<array.length;i++){new SortThread(array[i]).start();}} }主要是理解某個算法本身的意義和思想,再根據代碼聯系思考。
這篇博客是自己在學習的時候跟著老師一起敲的,如有錯誤或不足之處還請多指教,以下是幾個參考學習的鏈接。
可視化算法結構
前端JS實現十大排序算法可參見博文
學習視頻bilibili
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的十大排序算法(Java)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: springboot-vue简单小项目搭
- 下一篇: 二叉树遍历(附Java实现代码)