經典十大排序演算法(含升序降序,基數排序含負數排序)
寫在前面的話
雖然已經有很多人總結過這十大排序演算法,優秀的文章也不少,但是Java完整版的好像不多,還存在某些文章程式碼存在錯誤的情況,同時也為了自己練手,決定把所有的寫一遍鞏固下,同時也真誠的希望閱讀到這篇文章的小夥伴們可以自己去從頭敲一遍,不要貼上複製!希望我的文章對你有所幫助,每天進步一點點!!!
我用通俗的理解寫下對演算法的解釋,對某個演算法的執行過程不是很理解的話或者想看比較官方的解釋的話,單獨搜尋某個演算法,看幾篇不同的解釋,就可以有自己的理解了,這裡我主要展示程式碼以及進行通俗的解釋!整起來,再強調一次,一定要自己敲一遍,這樣才能理解的更深刻!
十大排序演算法對比
關於最後一列的穩定性,我稍微解釋下,例如對序列:1 2 4 2 6 排序,序列中存在兩個2,如果我們把這兩個2標記上(讓他倆不同),排序之後,前面的2還在前面,那麼就稱這種排序是穩定的,反之不穩定。
氣泡排序
本文的圖片來源網路,僅用於大家學習,侵權聯絡刪除!(下同)
完整程式碼:
package com.keafmd.Sequence;
public class BubbleSort {
//氣泡排序
public static void bubbleSort(int[] arr, boolean ascending) { //exchange標誌表示為升序排序還是降序排序
boolean flag = true; //加一個標誌位,記錄上一次是否發生了交換,如果是,我們則進行下一輪,如果沒有,說明已經冒泡好了
for (int i = 1; i
flag = false; //假定未交換
for (int j = 0; j
if (ascending ? arr[j] > arr[j + 1] : arr[j]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
flag = true;
}
}
}
}
//氣泡排序 -- 預設不傳參升序
public static void bubbleSort(int[] arr) {
bubbleSort(arr, true);
}
}
測試程式碼:
升序排序(從小到大)
執行結果:
降序排序(從大到小)
執行結果:
下面幾個演算法的測試也就是換了下類名和方法名(換成相應的排序演算法),如果想降序就在陣列後面傳個false即可。我就不一一複製了,我在最下面給出含所有演算法的測試類,需要的自取即可。
快速排序
簡單解釋:
快速排序就是每次找一個基點(第一個元素),然後兩個哨兵,一個從最前面往後走,一個從最後面往前面走,如果後面那個哨兵找到了一個比基點大的數停下來,前面那個哨兵找到比基點大的數停下來,然後交換兩個哨兵找到的數,如果找不到最後兩個哨兵就會碰到一起就結束,最後交換基點和哨兵相遇的地方的元素,然後就將一個序列分為比基點小的一部分和比基點大的一部分,然後遞迴左半部分和右半部分,最後的結果就是有序的了。
完整程式碼:
package com.keafmd.Sequence;
public class QuickSort {
//快速排序
public static void quickSort(int[] arr) {
quickSort(arr, true);
}
public static void quickSort(int[] arr, boolean ascending) {
if (ascending) {
quickSort(arr, 0, arr.length - 1, true);
} else {
quickSort(arr, 0, arr.length - 1, false);
}
}
public static void quickSort(int[] arr, int begin, int end, boolean ascending) {
if (ascending)
quickSort(arr, begin, end);
else
quickSortDescending(arr, begin, end);
}
//快排序升序 -- 預設
public static void quickSort(int[] arr, int begin, int end) {
if (begin > end) { //結束條件
return;
}
int base = arr[begin];
int i = begin, j = end;
while (i
while (arr[j] >= base && i
j--;
}
while (arr[i]
i++;
}
if (i
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//最後將基準為與i和j相等位置的數字交換
arr[begin] = arr[i];
arr[i] = base;
quickSort(arr, begin, i - 1); //遞迴呼叫左半陣列
quickSort(arr, i + 1, end); //遞迴呼叫右半陣列
}
//快排序降序
public static void quickSortDescending(int[] arr, int begin, int end) {
if (begin > end) { //結束條件
return;
}
int base = arr[begin];
int i = begin, j = end;
while (i
while (arr[j]
j--;
}
while (arr[i] >= base && i
i++;
}
if (i
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
//最後將基準為與i和j相等位置的數字交換
arr[begin] = arr[i];
arr[i] = base;
quickSortDescending(arr, begin, i - 1); //遞迴呼叫左半陣列
quickSortDescending(arr, i + 1, end); //遞迴呼叫右半陣列
}
}
直接選擇排序
簡單解釋:
陣列分為已排序部分(前面)和待排序序列(後面)
第一次肯定所有的數都是待排序的
從待排序的序列中找到最大或最小的那個元素,放到前面的已排序部分,然後一直找,不斷縮小待排序的範圍,直到所有的數都是已排序的了
完整程式碼:
package com.keafmd.Sequence;
public class SelectSort {
//直接選擇排序
public static void selectSort(int[] arr, boolean ascending) {
for (int i = 0; i
int m = i; //最小值或最小值的下標
for (int j = i + 1; j
if (ascending ? arr[j] arr[m]) {
m = j; //找到待排序的數中最小或最大的那個數,記錄下標
}
}
//交換位置
int temp = arr[i];
arr[i] = arr[m];
arr[m] = temp;
}
}
public static void selectSort(int[] arr) {
selectSort(arr, true);
}
}
堆排序
先理解下大頂堆和小頂堆,看圖
大頂堆,雙親結點的值比每一個孩子結點的值都要大。根結點值最大
小頂堆,雙親結點的值比每一個孩子結點的值都要小。根結點值最小
簡單解釋:
構建好大頂堆或小頂堆結構,這樣最上面的就是最大值或最小值,那麼我們取出堆頂元素,然後重新構建結構,一直取,一直重新構建,那麼最後達到排序的效果了。
完整程式碼:
package com.keafmd.Sequence;
public class HeapSort {
//堆排序
public static void heapSort(int[] arr) {
//對傳入的陣列進行建立堆,這裡預設建立大頂堆,進行升序排列
heapSort(arr, true);
}
public static void heapSort(int[] arr, boolean maxheap) {
//1.構建大頂堆
for (int i = arr.length / 2 - 1; i >= 0; i--) {
//從第一個非葉子結點從下至上,從右至左調整結構
sift(arr, i, arr.length , maxheap);
}
//2.調整堆結構+交換堆頂元素與末尾元素
for (int j = arr.length - 1; j > 0; j--) {
//現在的陣列第一個就是根結點,最小值所在,進行交換,把它放到最右邊
int temp = arr[j];
arr[j] = arr[0];
arr[0] = temp;
//重新建立堆
sift(arr, 0, j , maxheap); //重新對堆進行調整
}
}
//建立堆的方法
private static void sift(int[] arr, int parent, int len, boolean maxheap) {
int value = arr[parent]; //先取出當前元素i
for (int child = 2 * parent + 1; child
if (child+1 arr[child + 1])) { //如果左子結點小於右子結點,child指向右子結點
child++; //右孩子如果比左孩子大,我們就將現在的孩子換到右孩子
}
//判斷是否符合大頂堆的特性, 如果右孩子大於雙親,自然左孩子也大於雙親,符合
//如果子節點大於父節點,將子節點值賦給父節點(不用進行交換)
if (maxheap ? value arr[child]) {
arr[parent]=arr[child];
parent = child;
}
else {//如果不是,說明已經符合我們的要求了。
break;
}
}
arr[parent] =value; //將value值放到最終的位置
}
}
歸併排序
簡單解釋:
該演算法是採用分治法,把陣列不斷分割,直至成為單個元素,然後比較再合併(合併的過程就是兩部分分別從頭開始比較,取出最小或最大元素的放到新的區域內,繼續取兩部分中最大或最小的元素,直到這兩部分合並完,最後所有的都合併完,最後形成完整的有序序列)
完整程式碼:
package com.keafmd.Sequence;
public class MergeSort {
//歸併排序
public static void mergeSort(int []arr ,boolean ascending){
int[] temp = new int[arr.length]; //在排序前,先建好一個長度等於原陣列長度的臨時陣列,避免遞迴中頻繁開闢空間
mergeSort(arr,0,arr.length-1,temp,ascending);
}
public static void mergeSort(int []arr){
mergeSort(arr,true);
}
public static void mergeSort(int []arr,int left,int right,int[] temp,boolean ascending){
if(left //對半分,比如總長度是10,left=0,right=9,mid=4確實是中間分了,0~4,5~9 //當長度9,left=0,right=8,mid=4,0~4,5~8 int mid = left + (right-left)/2; // 防止越界的寫法 //int mid = (left+right)/2; mergeSort(arr,left,mid,temp,ascending); //左邊歸併排序,使得左子序列有序 mergeSort(arr,mid+1,right,temp,ascending); //右邊歸併排序,使得右子序列有序 merge(arr,left,mid,right,temp,ascending); //將兩個有序子陣列合並操作 } } private static void merge(int[] arr,int left,int mid,int right,int[] temp,boolean ascending){ int i = left; //左序列起始下標 int j = mid+1; //右序列起始下標 int t = 0; //臨時陣列指標 while(i if(ascending?arr[i] temp[t++] = arr[i++]; }else { temp[t++] = arr[j++]; } } while(i temp[t++] = arr[i++]; } while(j temp[t++] = arr[j++]; } t = 0; //將temp中的元素全部複製到原陣列中 while(left arr[left++] = temp[t++]; } } } 插入排序 簡單解釋: 最簡單的理解就是打地主時我們拿到牌後的整理過程,從第二個牌(假設我們拿起來這個牌開始比較)開始,(說下升序)從後往前比較如果比前面的那個牌小,就把牌往後移動,直到找到一個合適的位置(這個位置的前面的那個牌不比這個要放下的牌大)就把這個牌放到這個位置,慢慢的前面的部分變得有序,直至全部有序即可。 完整程式碼: package com.keafmd.Sequence; public class StraghtInsertSort { //插入排序 public static void straghtInsertSort(int[] arr) { straghtInsertSort(arr, true);//預設進行升序 } public static void straghtInsertSort(int[] arr, boolean ascending) { for (int i = 1; i int temp = arr[i]; int j=0; //這就是那個合適的位置 for (j = i - 1; j >= 0 && (ascending ? temp arr[j]); j--) { arr[j + 1] = arr[j]; } //把牌放下,為啥是j+1, //是因為上面的迴圈遍歷到不符合情況的時候 j是合適的位置的前面的那個數的位置 //有點拗口,但是就是這個意思,看圖方便理解下 arr[j + 1] = temp; } } } 簡單解釋: 完整程式碼: package com.keafmd.Sequence; public class ShellSort { public static void shellSort(int[] arr) { shellSort(arr,true); } public static void shellSort(int[] arr,boolean ascending) { for(int d = arr.length/2;d>0;d/=2){ for(int i=d;i int temp = arr[i]; int j=0; for(j=i-d;j>=0&&(ascending?temp arr[j+d]=arr[j]; } arr[j+d] = temp; } } } } 簡單解釋: 完整程式碼: package com.keafmd.Sequence; public class CountSort { public static void countSort(int[]arr){ countSort(arr,true); } public static void countSort(int[]arr,boolean ascending){ int d,min=arr[0],max=arr[0]; //找出最大、最小值 for(int i=0;i if(arr[i] min =arr[i]; } if(arr[i]>max){ max = arr[i]; } } //建立一個用於計數的陣列 d = min; int[] count_map = new int[max-min+1]; for(int i=0;i count_map[arr[i]-d]++; } int k =0; if(ascending){ for(int i=0;i if(count_map[k]>0){ arr[i] = k+d; i++; count_map[k]--; }else k++; } }else { for(int i=arr.length-1;i>=0;){ if(count_map[k]>0){ arr[i] = k+d; i--; count_map[k]--; }else k++; } } } } 簡單解釋: 完整程式碼: package com.keafmd.Sequence; import java.util.ArrayList; import java.util.Collections; public class BucketSort { public static void bucketSort(int[] arr){ bucketSort(arr,true); } public static void bucketSort(int[] arr,boolean ascending){ if(arr==null||arr.length==0){ return; } //計算最大值與最小值 int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; for(int i=0;i max = Math.max(arr[i],max); min = Math.min(arr[i],min); } //計算桶的數量 int bucketNUm = (max-min)/ arr.length+1; ArrayList for(int i=0;i bucketArr.add(new ArrayList()); } //將每個元素放入桶中 for(int i=0;i int num = (arr[i]-min)/ (arr.length); bucketArr.get(num).add(arr[i]); } //對每個桶進行排序 for (int i = 0; i //用系統的排序,速度肯定沒話說 Collections.sort(bucketArr.get(i)); } //將桶中元素賦值到原序列 int index; if(ascending){ index=0; }else{ index=arr.length-1; } for(int i=0;i for(int j= 0;j arr[index] = bucketArr.get(i).get(j); if(ascending){ index++; }else{ index--; } } } } } 基數排序 簡單解釋: 首先說一下,我發現好多人寫的基數排序只能排序正整數,其實只要處理下就可以排序含有負數的了,就是我們排序前先把所有的數整體變大(就是減上最小的負數,也就是加了),都變成正數,然後排序好之後,在減下來(加上最小的負數,也就減了)就好了。 基數排序就是按數位排序可分為LSD(從最低位[也就是個位]開始排序)和MSD(從最高位開始排序),下面寫的事LSD基數排序。 基數排序就是把數按位考慮,讓後我們一位數只能是[0,9],就是我們在考慮某位(個位、百位· · ·)的時候就只看這個位的數,放到在[0,9]相應的位置,然後順序取出,最後再按其它位這樣操作(上面說了要不從低位開始到高位,要不就是從高位到低位) 完整程式碼: package com.keafmd.Sequence; public class RadixSort { public static void radixSort(int[] arr){ radixSort(arr,true); } public static void radixSort(int[]arr,boolean ascending){ int max = Integer.MIN_VALUE; int min = Integer.MAX_VALUE; //求出最大值、最小值 for (int i = 0; i max = Math.max(max, arr[i]); min = Math.min(min, arr[i]); } if (min for (int i = 0; i arr[i] -= min; } max -= min; //max也要處理! } //很巧妙求出最大的數有多少位 int maxLength = (max+"").length(); int[][] bucket = new int[10][arr.length]; //一個二維陣列,一維代表0到9,二維存放符合數 int[] bucketElementCount = new int[10]; // 用於記錄0到9某位存在數字的個數 for (int i = 0 ,n = 1 ; i for (int j = 0; j int value = arr[j]/n % 10; bucket[value][bucketElementCount[value]] = arr[j]; bucketElementCount[value]++; } //升序 if(ascending) { int index = 0; //從左到右,從下到上取出每個數 for (int j = 0; j if (bucketElementCount[j] != 0) { for (int k = 0; k arr[index] = bucket[j][k]; index++; } } bucketElementCount[j] = 0; } }else { // 降序 int index=0; //從右到左,從下到上取出每個數 for (int j = bucketElementCount.length-1; j >=0; j--) { if (bucketElementCount[j] != 0) { for (int k = 0; k arr[index] = bucket[j][k]; index++; } } bucketElementCount[j] = 0; } } } if (min for (int i = 0; i arr[i] += min; } } } } 完整測試類 package com.keafmd.Sequence; import java.util.*; import java.util.stream.IntStream; import java.util.stream.Stream; public class Sort { public static void main(String[] args) { int[] nums = {12, 4, 25, 47, 58, 34, 25, 9, 99, 26, 1, -13, 162, 10093, -66, -1}; // int[] nums = {12, 43,56,42,26,11}; int[] temparr; //利用系統Collections.sort方法進行對比 //將int陣列轉換為Integer陣列 //1、先將int陣列轉換為數值流 temparr = nums.clone(); IntStream stream = Arrays.stream(temparr); //2、流中的元素全部裝箱,轉換為流 ---->int轉為Integer Stream //3、將流轉換為陣列 Integer[] integers = integerStream.toArray(Integer[]::new); //把陣列轉為List List //使用Collections.sort()排序 System.out.println("使用系統的Collections.sort()的對比:"); //Collections.sort Collections.sort(tempList, new Comparator @Override public int compare(Integer o1, Integer o2) { return o1-o2; //return o2-o1; } }); //tempList.sort 也可以排序 //遍歷輸出結果 for (Integer integer : tempList) { System.out.print(integer+" "); } System.out.println(); //測試氣泡排序 System.out.println("測試氣泡排序:"); temparr = nums.clone(); BubbleSort.bubbleSort(temparr); //降序 //BubbleSort.bubbleSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試快速排序 System.out.println("測試快速排序:"); temparr = nums.clone(); QuickSort.quickSort(temparr); //QuickSort.quickSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試直接選擇排序 System.out.println("測試直接選擇排序:"); temparr = nums.clone(); SelectSort.selectSort(temparr); //SelectSort.selectSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試堆排序 System.out.println("測試堆排序:"); temparr = nums.clone(); HeapSort.heapSort(temparr); //HeapSort.heapSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試歸併排序 System.out.println("測試歸併排序:"); temparr = nums.clone(); MergeSort.mergeSort(temparr); //MergeSort.mergeSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試插入排序 System.out.println("測試插入排序:"); temparr = nums.clone(); StraghtInsertSort.straghtInsertSort(temparr); //StraghtInsertSort.straghtInsertSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試希爾排序 System.out.println("測試希爾排序:"); temparr = nums.clone(); ShellSort.shellSort(temparr); //ShellSort.shellSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試計數排序 System.out.println("測試計數排序:"); temparr = nums.clone(); CountSort.countSort(temparr); //CountSort.countSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試桶排序 System.out.println("測試桶排序:"); temparr = nums.clone(); BucketSort.bucketSort(temparr); //BucketSort.bucketSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); //測試基數排序 System.out.println("測試基數排序:"); temparr = nums.clone(); RadixSort.radixSort(temparr); //RadixSort.radixSort(temparr,false); for (int i = 0; i System.out.print(temparr[i] + " "); } System.out.println(); } } ———————————————— 版權宣告:本文為CSDN博主「牛哄哄的柯南」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。 原文連結:https://blog.csdn.net/weixin_43883917/article/details/118193663?spm=1001.2014.3001.5501希爾排序
希爾排序是插入排序的改進版,我們理解一個叫做下標差的的東西,也就是下面那個圖中的增量d,初始下標差為arr.length/2,然後繼續/2,對在同一下標差(相當於把這幾個數單獨拿出來了)的若干個數進行插入排序即可。計數排序
這個排序演算法看名字也很好理解,就是就是額外找個陣列來計數,然後在這個陣列從小到大或從大到小把數取出來即可。桶排序
就是把一個陣列分成幾個桶(其實是幾個區間,從小到大或從大到小的幾個區間)裝,然後讓每個桶(區間)有序,然後取出來放一起就可以了,相當於把幾個有序的段拿出來放一起,自然還是有序的,當然需要是按照區間的順序拿了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4662/viewspace-2797471/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【資料結構與演算法】內部排序之五:計數排序、基數排序和桶排序(含完整原始碼)資料結構演算法排序原始碼
- 排序演算法__基數排序排序演算法
- 十大經典排序演算法之氣泡排序排序演算法
- 十大經典排序演算法最強總結(含JAVA程式碼實現)排序演算法Java
- 計數排序、桶排序和基數排序排序
- 經典演算法之基數排序兩種實現演算法排序
- 計數排序vs基數排序vs桶排序排序
- Python基礎_根據隨機數_按長度_升序或降序排序列表Python隨機排序
- 【資料結構與演算法】非比較排序(計數排序、桶排序、基數排序)資料結構演算法排序
- 經典排序演算法 - 快速排序Quick sort排序演算法UI
- 【演算法】基數排序演算法排序
- 桶排序和基數排序排序
- 排序演算法__計數排序排序演算法
- 看動畫學演算法之:排序-基數排序動畫演算法排序
- 基數排序排序
- 排序(2)--選擇排序,歸併排序和基數排序排序
- 排序演算法(七大經典排序演算法)排序演算法
- 十大經典排序演算法動畫與解析排序演算法動畫
- 十大經典排序演算法(動圖演示)排序演算法
- 歸併排序和基數排序排序
- 十大經典排序演算法總結(JavaScript描述)排序演算法JavaScript
- 經典排序之選擇排序(Java)排序Java
- java 基數排序Java排序
- 資料結構與演算法——排序演算法-基數排序資料結構演算法排序
- C#基數排序演算法C#排序演算法
- 經典演算法之快速排序演算法排序
- [經典排序演算法][集錦]排序演算法
- 經典排序演算法回顧:排序演算法
- 基於桶的排序之基數排序以及排序方法總結排序
- (戀上資料結構筆記):計數排序、基數排序 、桶排序資料結構筆記排序
- 基於桶的排序之計數排序排序
- 排序演算法-N個正整數排序排序演算法
- 八大排序演算法—16張圖搞懂基數排序排序演算法
- 十大經典排序演算法動畫,看我就夠了!排序演算法動畫
- Java十大經典排序演算法最強總結Java排序演算法
- Python八大演算法的實現,插入排序、希爾排序、氣泡排序、快速排序、直接選擇排序、堆排序、歸併排序、基數排序。Python演算法排序
- 撲克牌排序:基於基數排序的方法排序
- 【筆記】基數排序筆記排序