七大主流排序

一、插入排序

算法思想:
從第二個數開始,往前找,如果前面的數比自己大,就交換,最終找到合適的位置就結束了。(這個交換可以理解爲向後移位,你玩撲克牌的時候,按大小排序,前面插一張,後面的就要往後挪一點)
時間複雜度:
最好:O(n)
最壞:O(n^2)
平均:O(n)
空間複雜度: O(1)
穩定性:穩定

public static void InserSort(int[] arr){
int tmp;
for(int i=0;i<arr.length-1;i++){
	for(int j=i+1;j>0;j--){
		if(arr[j] < arr[j-1]){
		 tmp = arr[j];
		 arr[j] = arr[j-1];
		 arr[j-1] = tmp;
		} else{
		break;
		}
	}
}

}

換個版本

public static void InsertSort(int[] arr){
	for(int i=1;i<arr.length;i++){
		int key = arr[i];
		int j = i-1;
		for(;j>=0 && arr[j] > v;j--){
			arr[j+1] = arr[j];
		}
		arr[j+1] = key;
	}
}

二、希爾排序

算法思想:基於直接插入方法的升級版本,目的就是爲了先讓數組變得更有序,有序了插入排序的時間複雜度就會降低。

時間複雜度:
最好:O(n)
最壞:O(n^2)
平均:O(n^1.3)
空間複雜度: O(1)
穩定性: 不穩定

public static void ShellSort(int[] arr,int length){
	int pos = length;
	int tmp;
	while(true){
	pos = pos/2;	
	for(int k=0;k<pos;k++){
		for(int i= k+pos;i<arr.length;i+=pos){
			for(int j=i;j>k;j-=pos){
				if(arr[j] < arr[j-pos]){
					tmp = arr[j];
					arr[j] = arr[j-pos];
					arr[j-pos] = tmp; 		
					} else {
					  break;
					}
				}
			}
	    }
	    if(pos == 1){
		   break;
		}
	}
}

換個版本:

public static void shellSort(int[] array) {
	int gap = array.length;
	while (gap > 1) {
	insertSortGap(array, gap);
	gap = (gap / 3) + 1; // OR gap = gap / 2;
	}
	insertSortGap(array, 1);//就是直接插入排序了
}
private static void insertSortGap(int[] array, int gap) {
 	for (int i = 1; i < array.length; i++) {
	int v = array[i];
	int j = i - gap;
	for (; j >= 0 && array[j] > v; j -= gap) {
	array[j + gap] = array[j];
	}
	array[j + gap] = v;
  }
}

三、冒泡排序

算法思想:每次讓最大的冒出來;這麼說吧,從最後一個往前找,發現左邊的比後面的大,就交換,然後第一趟,把最小的放到了前面,第二趟,把次小的放到了前面。

時間複雜度:
最好:O(n)
最壞:O(n^2)
平均:O(n^2)
空間複雜度: O(1)
穩定性: 穩定

public static void BubbleSort(int[] arr){
	int tmp;
	for(int i=0;i<arr.length;i++){
		for(int j= arr.length-1;j>i;j--){
			if(arr[j] < arr[j-1]){
			tmp = arr[j];
			arr[j] = arr[j-1];
			arr[j-1] = tmp;
			}
		}
	}
}

升級版:主要是爲了解決沒有交換,說明已經有序,直接跳出。

public static void BubbleSort(int[] arr){
	int tmp;
	boolean flag = false;
	for(int i=0;i<arr.length;i++){
		flag = false;
		for(int j= arr.length-1;j>i;j--){
			if(arr[j] < arr[j-1]){
			tmp = arr[j];
			arr[j] = arr[j-1];
			arr[j-1] = tmp;
			flag = true;
			}
		}
		if(!flag){
		break;	
		}
	}
}

四、選擇排序

算法思想:打擂臺方法,若爲升序,第一個肯定最小,然後我找後面有比它還小的嗎,如果有更改最小標誌位,直到找到最小的,然後交換。

時間複雜度: O(n^2)
空間複雜度: O(1)
穩定性: 不穩定

 public static void SelectSort(int arr[]){
	int tmp=0;
	for(int i=0;i<arr.length-1;i--){
		int minIndex = i;
		for(int j=i+1;j<arr.length;j++){
			if(arr[minIndex] > arr[j]){
				minIndex = j;
			}
		}
		if(minIndex != i){
		tmp = arr[i];
		arr[i] = arr[minIndex];
		arr[minIndex] = tmp; 
		}
	}
}

五、快速排序

算法思想:先找出一個基準值,找到之後,比他小的放左邊,比他大的放右邊,排好序後,把臨界點的值給返回來。(看代碼說話),分治法的思想。
時間複雜度:
最好:O(nlog(n))
最壞:O(n^2)
平均:O(n
log(n))
空間複雜度: 最好平均都是O(log(n)),最壞O(n)
穩定性: 不穩定

遞歸版本

public static void QuickSort(int[] arr){
	QuickSortHelper(arr,0,arr.length-1);
}

private static void QuickSortHelper(int arr[],int left,int right){
	if(left >= rigth){		//遞歸結束標誌
	return;
	}
	int partIndex = partition(arr,left,right);//找出臨界值
	QuickSortHelper(arr,left,partIndex-1);//排序左半部分
	QuickSortHelper(arr,partIndex+1,right);//排序右半部分
}

private static void partition(int[] arr,int left,int right){
int BaseIndex = right;  //以最右邊爲基準點
int BaseValue = arr[right];
	while(left < BaseIndex){
		while(left < BaseIndex && arr[left] <= BaseValue ){
			left++;		//從左往右找到第一個大於基準值的
		}
		while(left < BaseIndex && BaseIndex <= arr[BaseIndex]){
			BaseIndex--;	//從右邊往左找第一個小於基準值的
		}
		swap(arr,left,BaseIndex);//把兩個交換
}
	swap(arr,left,right);//把臨界點的那個值放到最右邊去
	return left;//返回臨界值的座標
]
private static void swap(int[] arr,int left,int right){
	int tmp = arr[left];
	arr[left] = arr[right];
	arr[rigth] = tmp;
}

非遞歸版本:

public static void quickSort(int[] array) {
Stack<Integer> stack = new Stack<>();
stack.push(array.length - 1);
stack.push(0);
while (!stack.isEmpty()) {
int left = stack.pop();
int right = stack.pop();
if (left >= right) {
continue;
}
int pivotIndex = partition(array, left, right);
stack.push(right);
stack.push(pivotIndex + 1);
stack.push(pivotIndex - 1);
stack.push(left);
}
}

private static void partition(int[] arr,int left,int right){
int BaseIndex = right;
int BaseValue = arr[right];
	while(left < BaseIndex){
		while(left < BaseIndex && arr[left] <= BaseValue ){
			left++;
		}
		while(left < BaseIndex && BaseIndex <= arr[BaseIndex]){
			BaseIndex--;
		}
		swap(arr,left,BaseIndex);
}
	swap(arr,left,right);
	return left;
]
private static void swap(int[] arr,int left,int right){
	int tmp = arr[left];
	arr[left] = arr[right];
	arr[rigth] = tmp;
}

六、歸併排序

算法思想:是分治法的一個非常典型的應用,將已有序的子序列合併,得到完全有序的序列;即先使每個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲二路歸併

時間複雜度: O(n*log(n))
空間複雜度: O(n)
穩定性: 穩定

public static void MergeSort(int arr[]){
	MergeSortHelper(arr,0,arr.length);
}

private static void MergeSort(int[] arr,int left,int right){
	if(left >= right || rigth-left == 1){
		return;
	}
	int mid = (left+right)/2;
	MergeSort(arr,left,mid);//排序左邊
	MergeSort(arr,mid,right);//排序右邊
	merge(arr,left,mid,right);//合併
}
private static void merge(int[] arr, int left,int mid ,int right){
int length = right-left;
int[] Output = new int[length];//搞個臨時數組,就排序的
int OutputIndex = 0;
int i=left;
int j=mid;
while (i < mid && j < right){
	if(arr[i] <= arr[j]){
	Output[OutputIndex++] = arr[i++];插入排序
	} else {
	Output[OutputIndex++] = arr[j++];
	}
}
	while(i < mid){
	Output[OutputIndex++] = arr[i++];//看看哪個子串沒空,補上
	}
	while(j < right){
	Output[OutputIndex++] = arr[j++];
	}

	for(int k=0;k<OutputIndex;k++){
	arr[left+k] = Output[k]; //賦值回原數組
	}
}



七、堆排序

算法思想:基本原理也是選擇排序,只是不在使用遍歷的方式查找無序區間的最大的數,而是通過堆來選擇無序區間的最大的數。
堆:其實有一個類似於完全二叉樹的結構,並同時滿足堆積的性質:即子結點的鍵值或索引總是小於(或者大於)它的父節點。

注意: 排升序要建大堆;排降序要建小堆

時間複雜度: O(n*log(n))
空間複雜度: O(1)
穩定性: 不穩定

public class HeapSort {


    /**
     * 選擇排序-堆排序
     * @param array 待排序數組
     * @return 已排序數組
     */
    public static int[] heapSort(int[] array) {
        //這裏元素的索引是從0開始的,所以最後一個非葉子結點array.length/2 - 1
        for (int i = array.length / 2 - 1; i >= 0; i--) {
            adjustHeap(array, i, array.length);  //調整堆
        }

        // 上述邏輯,建堆結束
        // 下面,開始排序邏輯
        for (int j = array.length - 1; j > 0; j--) {
            // 元素交換,作用是去掉大頂堆
            // 把大頂堆的根元素,放到數組的最後;換句話說,就是每一次的堆調整之後,都會有一個元素到達自己的最終位置
            swap(array, 0, j);
            // 元素交換之後,毫無疑問,最後一個元素無需再考慮排序問題了。
            // 接下來我們需要排序的,就是已經去掉了部分元素的堆了,這也是爲什麼此方法放在循環裏的原因
            // 而這裏,實質上是自上而下,自左向右進行調整的
            adjustHeap(array, 0, j);
        }
        return array;
    }

    /**
     * 整個堆排序最關鍵的地方
     * @param array 待組堆
     * @param i 起始結點
     * @param length 堆的長度
     */
    public static void adjustHeap(int[] array, int i, int length) {
        // 先把當前元素取出來,因爲當前元素可能要一直移動
        int temp = array[i];
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) {  //2*i+1爲左子樹i的左子樹(因爲i是從0開始的),2*k+1爲k的左子樹
            // 讓k先指向子節點中最大的節點
            if (k + 1 < length && array[k] < array[k + 1]) {  //如果有右子樹,並且右子樹大於左子樹
                k++;
            }
            //如果發現結點(左右子結點)大於根結點,則進行值的交換
            if (array[k] > temp) {
                swap(array, i, k);
                // 如果子節點更換了,那麼,以子節點爲根的子樹會受到影響,所以,循環對子節點所在的樹繼續進行判斷
                i  =  k;
            } else {  //不用交換,直接終止循環
                break;
            }
        }
    }

    /**
     * 交換元素
     * @param arr
     * @param a 元素的下標
     * @param b 元素的下標
     */
    public static void swap(int[] arr, int a, int b) {
        int temp = arr[a];
        arr[a] = arr[b];
        arr[b] = temp;
    }
}

【理解多少,全憑個人造化。】

補充

其他非主流排序,就說說算法吧,當做興趣看一看。
1.基數排序
就是建立一個長數組,數字是多少就放在對應下標,然後裏面的數字就是元素的個數,然後遍歷一遍數組就得到了排序的結果。優劣性,想想就知道了,數字特別大豈不是涼涼,算是一種空間換時間的做法。(PS:數字大也是可以解決的,選擇一個基數,除以這個數來放,有的哈希的味道)

2.睡眠排序
這個算好玩吧,這個是多線程方面的,就是進來一個數字,我就創建一個線程,然後讓線程休眠,休眠的時間就是這個數字,到了這個時間,線程顯示出這個數字就銷燬。這排序可以自己寫着玩玩,缺點一樣的,數字大休眠到天荒地老。這好像算時間換空間把

3.猴子排序
這純粹是搞笑來的,據說只要猴子夠多,讓猴子打字,猴子就能寫出一本《哈姆雷特》,而猴子排序也是這樣的。就是隨機,把數組所有的數字隨機排序一下,檢查是否有序,沒有的話就再來一次。我前面寫過C語言版本的

發佈了43 篇原創文章 · 獲贊 12 · 訪問量 3389
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章