【算法】排序算法總結,手寫快排,歸並,堆排序算法


算法復雜度

相關概念:

穩定:如果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

https://www.cnblogs.com/chengxiao/p/6103002.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM