算法復雜度
相關概念:
穩定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不穩定:如果a原本在b的前面,而a=b,排序之后 a 可能會出現在 b 的后面。
時間復雜度:對排序數據的總的操作次數。反映當n變化時,操作次數呈現什么規律。
空間復雜度:是指算法在計算機內執行時所需存儲空間的度量,它也是數據規模n的函數
手寫快排:
先選擇第一個數字作為標尺,然后分別從第二個數字往右找,找到比第一個數大的數,和從倒數第一個數字往左找,找到比第一個數小的數,然后將找到的兩個數進行交換,一直下去。
- 從數列中挑出一個元素,稱為 “基准”(pivot);
- 重新排序數列,所有元素比基准值小的擺放在基准前面,所有元素比基准值大的擺在基准的后面(相同的數可以到任一邊)。在這個分區退出之后,該基准就處於數列的中間位置。這個稱為分區(partition)操作;
- 遞歸地(recursive)把小於基准值元素的子數列和大於基准值元素的子數列排序。
public class quickSort { private static int[] quickSort(int[] arr, int left, int right) { if (left < right) { int p = patition(arr, left, right);//基數 quickSort(arr, left, p - 1); quickSort(arr, p + 1, right); } return arr; } private static int patition(int[] arr, int left, int right) {// 分區操作 int temp = left; while (left < right) { //從右邊開始找到比主鍵值a[0]小的值,移到左邊 while (left < right && arr[right] >= arr[temp]) right--; while (left < right && arr[left] <= arr[temp]) //從左邊開始找到比主鍵值a[0]大的值,移到右邊 left++; swap(arr,left,right); } //最終將基准數歸位 swap(arr, temp, right); return right; } private static void swap(int[] arr, int left, int right) { int temp=arr[left]; arr[left]=arr[right]; arr[right]=temp; } }
手寫歸並:
該算法采用經典的分治(divide-and-conquer)策略(分治法將問題分(divide)成一些小的問題然后遞歸求解,而治(conquer)的階段則將分的階段得到的各答案"修補"在一起,即分而治之)。
- 把長度為n的輸入序列分成兩個長度為n/2的子序列;
- 對這兩個子序列分別采用歸並排序;
- 將兩個排序好的子序列合並成一個最終的排序序列。
public class MergeSort { public static void main(String[] args) { int[] arr = { 9, 8, 7, 6, 5, 4, 3, 2, 1 }; int[] temp = new int[arr.length]; sort(arr, 0, arr.length - 1, temp); System.out.println(Arrays.toString(arr)); } private static void sort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; sort(arr, left, mid, temp); sort(arr, mid + 1, right, temp); MergeSort(arr, left, mid, right, temp); } } private static void MergeSort(int[] arr, int left, int mid, int right, int[] temp) { int i = left; int j = mid + 1; int t = 0; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t++] = arr[i++]; } else { temp[t++] = arr[j++]; } } while (i <= mid) {// 將左邊剩余的元素填充進temp temp[t++] = arr[i++]; } while (j <= right) { temp[t++] = arr[j++]; } t = 0; while (left <= right) { arr[left++] = temp[t++]; } } }
手寫堆排序:
堆排序
堆排序是利用堆這種數據結構而設計的一種排序算法,堆排序是一種選擇排序,它的最壞,最好,平均時間復雜度均為O(nlogn),它也是不穩定排序。首先簡單了解下堆結構。
堆
堆是具有以下性質的完全二叉樹:每個結點的值都大於或等於其左右孩子結點的值,稱為大頂堆;或者每個結點的值都小於或等於其左右孩子結點的值,稱為小頂堆。
堆排序的基本思想是:將待排序序列構造成一個大頂堆,此時,整個序列的最大值就是堆頂的根節點。將其與末尾元素進行交換,此時末尾就為最大值。然后將剩余n-1個元素重新構造成一個堆,這樣會得到n個元素的次小值。如此反復執行,便能得到一個有序序列了
用簡單的公式來描述一下堆的定義就是:
大頂堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小頂堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
public class HeapSort { private static void sort(int[] arr) { // 1、構建大頂堆 for(int i=arr.length/2-1;i>=0;i--){ //從第一個非葉子節點從下至上,從右至左調整結構 adjustHeap(arr,i,arr.length); } //2、調整堆結構+交換堆頂元素與末尾元素 for(int j=arr.length-1;j>0;j--){ swap(arr,0,j);//將堆頂元素與末尾元素進行交換 adjustHeap(arr, 0, j);//重新對堆進行調整 } } private static void swap(int[] arr, int a, int b) { int temp=arr[a]; arr[a] = arr[b]; arr[b] = temp; } private static void adjustHeap(int[] arr, int i, int length) { int temp=arr[i]; for(int k=i*2+1;k<length;k=k*2+1){//從i結點的左子結點開始,也就是2i+1處開始 if(k+1<length&&arr[k]<arr[k+1]){ k++; } if(arr[k]>temp){//如果子節點大於父節點,將子節點值賦給父節點(不用進行交換) arr[i]=arr[k]; i=k; }else{ break; } } arr[i]=temp;//將temp值放到最終的位置 } }
參考博文:
十大經典排序算法(動圖演示)https://www.cnblogs.com/onepixel/articles/7674659.html