一、排序算法複雜度比較
T(N) = a*T(N/b) + O(N^d)
- -> 複雜度爲
- -> 複雜度爲
- -> 複雜度爲
二、冒泡排序
- 時間複雜度 ,額外空間複雜度
- 可實現成穩定的
經過上圖一輪後,最大的數就到了最後位置,接下來我們對剩下的1,2,3,6,4進行同樣的方式,最終可以將數值從小到大排序
Java代碼:
package day01;
/**
* 冒泡排序
* @author Danycym
*
*/
public class Code01_BubbleSort {
public static void bubbleSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5, 9, 8, 7};
printArray(arr);
bubbleSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
三、插入排序
- 時間複雜度 ,額外空間複雜度
- 可實現成穩定的
插入排序和我們平時插撲克牌類似,上圖中橙色框內表示手裏的牌,橙色框下一個元素表示下一張要抓的牌,每抓到一張新的牌,我們就將其插入到手裏對應的位置(這裏需要從新牌的上一張到第一張逐一比較,比手裏的牌小就交換,直到比手裏的牌大,此時插入到小牌後面,大牌前面完成插入),依次類推,直到最後一張牌
Java代碼:
package day01;
/**
* 插入排序
* @author Danycym
*
*/
public class Code02_InsertionSort {
public static void insertionSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
for(int i = 1; i < arr.length; i++) {
for(int j = i - 1; j >= 0; j--) {
if(arr[j] > arr[j+1]) {
swap(arr, j, j + 1);
}
}
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void printArray(int[] arr) {
if (arr == null) {
return;
}
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5, 9, 8, 7};
printArray(arr);
insertionSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
四、選擇排序
- 時間複雜度 ,額外空間複雜度
- 不可實現成穩定的
每次設定橙色框位置爲最小值,如果該最小值後面的數有比它小的就和它交換
Java代碼:
package day01;
/**
* 選擇排序
* @author Danycym
*
*/
public class Code03_SelectionSort {
public static void selectionSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
for(int i = 0; i < arr.length - 1; i++) {
int minIndex = i;
for(int j = i + 1; j < arr.length; j++) {
minIndex = arr[j] < arr[minIndex] ? j : minIndex;
}
swap(arr, i, minIndex);
}
}
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5, 9, 8, 7};
printArray(arr);
selectionSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
五、歸併排序
- 時間複雜度 ,額外空間複雜度
- 可實現成穩定的
將原問題拆分成子問題,再對子問題進行同樣的操作,直到不能再分,然後就是排序合併
Java代碼:
package day01;
/**
* 歸併排序
* @author Danycym
*
*/
public class Code04_MergeSort {
public static void mergeSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
mergeSort(arr, 0, arr.length - 1);
}
private static void mergeSort(int[] arr, int l, int r) {
if(l == r) {
return;
}
int mid = l + ((r - l) >> 1); //中點位置
mergeSort(arr, l, mid); //左分支
mergeSort(arr, mid + 1, r); //右分支
merge(arr, l, mid, r); //合併
}
private static void merge(int[] arr, int l, int m, int r) {
int[] help = new int[r - l + 1]; //存儲合併結果
int i = 0;
int p1 = l;
int p2 = m + 1;
//比較大小,依次添加到help中
while(p1 <= m && p2 <= r) {
help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
}
//p1還沒到最後,直接將後面的都添加到help
while(p1 <= m) {
help[i++] = arr[p1++];
}
//p2還沒到最後,直接將後面的都添加到help
while(p2 <= r) {
help[i++] = arr[p2++];
}
//最後將help中的元素都拷貝到原數組中
for(i = 0; i < help.length; i++) {
arr[l + i] = help[i];
}
}
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5, 9, 8, 7};
printArray(arr);
mergeSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
六、快速排序
- 時間複雜度 ,額外空間複雜度
- 不可實現成穩定的
上圖中,我們選擇最後一個元素作爲劃分的那個值,程序中做了修改,每次隨機選擇一個數作爲劃分的數,具體做法如下:
- 先隨機選擇一個元素,然後將它與最後一個元素交換,最後在進行看劃分
swap(arr, l + (int)(Math.random() * (r - l + 1)), r);
Java代碼:
package day01;
/**
* 快速排序
* @author Danycym
*
*/
public class Code05_QuickSort {
public static void quickSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
quickSort(arr, 0, arr.length - 1);
}
public static void quickSort(int[] arr, int l, int r) {
if(l < r) {
//隨機選擇一個元素作爲劃分的數
swap(arr, l + (int)(Math.random() * (r - l + 1)), r);
int[] p = partition(arr, l, r);
quickSort(arr, l, p[0] - 1);
quickSort(arr, p[1] + 1, r);
}
}
//交換
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//劃分
public static int[] partition(int[] arr, int l, int r) {
int less = l - 1;
int more = r;
while(l < more) {
if(arr[l] < arr[r]) {//遍歷到的元素比最後一個元素小
swap(arr, ++less, l++);//左邊界往右擴大1,並與當前遍歷到的元素交換
}else if(arr[l] > arr[r]) {//遍歷到的元素比最後一個元素大
swap(arr, --more, l);//右邊界往左擴大1,並與當前遍歷到的元素交換
}else {//否則,直接遍歷下個元素
l++;
}
}
swap(arr, more, r);//交換arr[more]與arr[r]
return new int[] {less + 1, more};
}
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5};
printArray(arr);
quickSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
黃色代表基數(劃分的數)
紅色代表當前遍歷的
綠色代表小於基數的
紫色代表大於基數的
橙色代表排序好的
- 該圖每次都是以第一個元素作爲劃分基數的
七、堆排序
- 時間複雜度 ,額外空間複雜度
- 不可實現成穩定的
數組理解爲完全二叉樹:
大根堆:
- 堆就是完全二叉樹
- 大根堆:在這棵完全二叉樹中,任何一棵子樹的最大值都是該子樹的頭部
將數組腦補的完全二叉樹轉換爲大根堆:
堆排序:
Java代碼:
package day01;
/**
* 堆排序
* @author Danycym
*
*/
public class Code06_HeapSort {
public static void heapSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
//構建大根堆
for(int i = 0; i < arr.length; i++) {
heapInsert(arr, i);
}
//大根堆長度
int heapSize = arr.length;
//將大根堆第一個元素與此時剩下的大根堆的最後一個元素交換
swap(arr, 0, --heapSize);
while(heapSize > 0) {
heapify(arr, 0, heapSize); //進行下沉操作
swap(arr, 0, --heapSize);
}
}
public static void heapInsert(int[] arr, int i) {
while(arr[i] > arr[(i -1) / 2]) { //如果i對應的值大於其父節點
swap(arr, i, (i -1) / 2); //則和父節點交換
i = (i -1) / 2; //並將父節點位置更新爲當前遍歷
}
}
//
public static void heapify(int[] arr, int i, int heapSize) {
int left = i * 2 + 1; //左孩子下標
while(left < heapSize) {
//判斷左右孩子中較大的孩子的下標
int largest = left + 1 < heapSize && arr[left + 1] > arr[left] ? left + 1 : left;
//再將較大者和當前遍歷比較,記錄較大者下標
largest = arr[largest] > arr[i] ? largest : i;
if(largest == i) { //如果相等,較大者是自己,退出
break;
}
//否則交換
swap(arr, largest, i);
i = largest; //更新i爲交換後的位置
left = i * 2 + 1;
}
}
//交換
public static void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
//打印
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 4, 3, 6, 5};
printArray(arr);
heapSort(arr);
printArray(arr);
}
}
動圖參考來自網絡:
八、桶排序
- 時間複雜度 ,額外空間複雜度
- 可以實現成穩定的
桶排序是一種非基於比較的排序,其實現包括計數排序和基數排序兩種,其基本思想如下:
- 得到無序數組的取值範圍
- 根據取值範圍創建對應數量的桶
- 遍歷數組,將每個元素放到對應的桶中
- 按照順序遍歷桶中的每個元素,依次放到數組中,即可完成數組的排序
堆排序是否穩定取決於“桶”用的是什麼數據結構,如果是隊列,那麼可以保證相同的元素取出後的相對位置與“放進去”之前是相同的,如果用棧來實現桶,就不是穩定的
“桶”是一種容器,這個容器可以用多種數據結構實現,包括數組、隊列或者棧
1、計數排序
- 桶排序的實現之計數排序
Java代碼:
package day01;
/**
* 桶排序
* @author Danycym
*
*/
public class Code07_BucketSort {
public static void bucketSort(int[] arr) {
if(arr == null || arr.length < 2) {
return;
}
int max = Integer.MIN_VALUE;
//求數組的最大值
for(int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int[] bucket = new int[max + 1];
for(int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
int i = 0;
for(int j = 0; j < bucket.length; j++) {
while(bucket[j]-- > 0) {
arr[i++] = j;
}
}
}
public static void printArray(int[] arr) {
if(arr == null) {
return;
}
for(int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
}
public static void main(String[] args) {
int[] arr = {2, 1, 12, 1, 3, 3, 4};
printArray(arr);
bucketSort(arr);
printArray(arr);
}
}
2、基數排序
待更新。。。