排序!排序!排序!

一、概述

排序算法是學習編程語言基礎過程中躲不開的內容,其實也是很有趣的部分。雖然實際應用還沒使用過,但是瞭解算法的來源以及編碼過程對自身邏輯能力的提高非常有幫助。

二、代碼
  • 直接插入

    關鍵字:認爲前面的是已經排好序的。O(n),O(n^2)

public void directInsert(int[] a){
    for(int i = 0; i < a.length; i++){
        for(int j = i; j>0; j--){
            if(a[j]<=a[j-1]){
			  int temp = a[j-1];
               a[j-1] = j[j];
               a[j] = temp;
            }else{
               break; 
            }
        }
    }
}
  • 直接選擇

    關鍵字:選擇最小值,放在最前面,記錄下標索引O(n2),O(n2)

public void directSelect(int[] a){
    for(int i = 0; i < a.length; i++){
        int current = i;
        for(int j = i+1; j < a.length; j++){
            if(a[current]<a[j]){
                current = j;
            }
            if(current != i){
                int temp = a[i];
                a[i] = a[current];
                a[current] = temp;
            }
        }
    }
}
  • 冒泡排序

    關鍵字:選出最大的 O(n) O(n^2)

public void bubble(int[] a){
    for(int i = 0; i < a.length; i++){
        for(int j = 0; j < a.length-1; j++){
            if(a[j]>=a[j+1]){
			   int temp = a[j+1];
                a[j+1] = a[j];
                a[j] = temp;
            }
        }
    } 
}
  • 快速排序

    • 不知道爲什麼叫做快速排序,我們只能記住他的核心思想是,選定一個基準,分割數據。其實我認爲他也是分治思想。他的指針是 頭 和 尾, 歸併排序他的指針是 頭 和 mid+1,所以他們的退出條件不一樣,快速排序是,頭要小於尾,歸併排序是low小於mid,mid+1要小於high
    • 值得注意的是,要先比較後面的(高位),我也不知道爲什麼
    • 退出遞歸的條件要能想到

    關鍵字:low high 基準 O(nlogn) O(n^2)

public void quickSort(int[] a, int start, int end){
    //退出遞歸條件 ---- 這個要想到
    if(start < end) {  
        int value = a[start];
        int low = start;
        int high = end;
        while(low < high) {
            while(low < high && a[high] >= value) { // 這個要放在前面,我也不知道爲什麼
                high--;
            }
            a[low] = a[high];
            while(low < high && a[low] <= value) {
                low++;
            }
            a[high] = a[low];
        }
        a[low] = value;
        quickSort(a, start, low); // start 到 low
        quickSort(a, low+1,end);
    }
}
  • 歸併排序

    • 是分治思想的體現,分爲兩半,前面一半爲start—mid,後面爲mid+1 ---- end,有兩個指針,分別指向前後面的第一個數字,然後分別比較,小的一個會進入到一個臨時數組中。

    • 寫關鍵代碼的時候,只要心中想着最後一個情況就行,不要想着只有1個或者兩個數字的情況,否則比較難相通。

    • 注意while()中的判斷條件,已經最後要把temp臨時數組全部放入對應的原數組中。

    • 關鍵字:low middle high
      時間複雜度 n log n

public void mergeSort(int[] a, int start, int end){
    int mid = (end - start) / 2;
    if(start<end){
        mergeSort(a,start,mid);
        mergeSort(a,mid+1,end);
        merge(a,start,mid,end);
    }
}

public void merge(int[] a, int start, int mid, int end){
    int[] temp = new int[end-start+1];
    int index = 0;
    int hh = mid + 1;
    int i = start;
    
    while(i<=mid && hh<=end){
        if(a[i]<=a[hh]){
            temp[index] = a[i];
            i++;
        }else{
            temp[index] = a[hh];
            hh++;
        }
        index++;
    }
    while(hh<=end){
        temp[index] = a[hh];
        hh++;
        index++;
    }
    while(i<=mid){
        temp[index] = a[i];
        i++;
        index++;
    }
    for(int k = 0;k< temp.length; k++){
		a[k+start] = temp[k];
    }
}
  • 堆排序

    • 首先一個數組可以變成一個完全二叉樹,什麼是完全二叉樹,就是除了最後一層可能是不完全的,其他層都是完全的。
    • 那麼數組怎麼變成一個完全二叉樹呢?其實就是單純的從上倒下,從左到右的把數組的數字放入二叉樹中。
    • 因此也會有一些表達式:比如如果一個節點對應數組第index位置,那麼他的左節點爲2*index+1,右節點爲2×index+2
    • 另外就是什麼是大頂堆,也就是根節點大於左右節點。
    • 其實構建一個大頂堆代碼很簡單,兩個步驟,構建大頂堆,交換首尾節點,構建大頂堆—
    • 關鍵在於幾個點:一個是關鍵代碼有一個遞歸,因爲把最大的數字移動到根節點可能破壞已經弄好的樹。 一個是構建大頂堆的時候,從index 開始,遞減循環。另外一個點是交換完首尾節點之後再次構建大頂堆,這個時候index 爲0(還是想不明白);
    public static void heapSort(int[] a) {
    		int index = (a.length - 1) / 2;
    		for(int i = index; i >= 0; i--) {
    			getMaxHeap(a, i, a.length);  // 這裏爲length是因爲getMaxHeap 裏面是< size
    		}
    		for(int k = a.length-1; k >= 0; k--) { // 這裏爲length-1 我無法描述,你想一下過程能													 明白的
    			int temp = a[k];
    			a[k] = a[0];
    			a[0] = temp;
    			getMaxHeap(a, 0, k);
    		}
    	}
    
    	public static void getMaxHeap(int[] a, int index, int size) {
    		int left = 2 * index + 1;
    		int right = 2 * index + 2;
    		int max = index;
    		if (left < size  && a[left] >= a[max]) {
    			max = left;
    		}
    		if (right < size && a[right] >= a[max]) { // right < size 這個容易忘記
    			max = right;
    		}
    		if (max != index) { // 這其實就是個退出遞歸的條件,因此不需要在外面加上 index < size
    			int temp = a[index];
    			a[index] = a[max];
    			a[max] = temp;
    			getMaxHeap(a, max, size);
    		}
    	}
    
    • 桶排序

    桶排序還是很重要的,典型的空間換時間,對於海量數據,結合位圖有時候效果更好,同樣的如果不在乎內存大小,用桶排也是ok的,他的時間複雜度就是O(N),穩定性取決於每個桶中的排序算法。

    另外一些算法題中,使用桶排有時候也能非常快速的求得答案。

    具體思想就是把這些數字按照一定的規律放到若干個數組中,這個規律讓他們儘量平均分佈,然後每個數組中的數字單獨排序,之後再拼起來。

    一般桶的數量是: 先遍歷集合,得到最大值,和最小值。然後用下面的公式計算:
    Length=max/10min/10+1 Length= max/10 - min/10 + 1

    然後每個元素放在哪個桶裏的規律是:這是以10爲間隔的。
    f(x)=x/10min/10 f(x) = x/10 - min/10

    	public void method(int[] arr) {
    		int max = 0;
    		int min = 0;
    		// 得到最大值
    		for(int i=0; i < arr.length; i++){
    	         max = Math.max(max, arr[i]);
    	         min = Math.min(min, arr[i]);
    		}
    		// 創建桶
    		int bucketNum = max / 10 - min /10 + 1;
    		 ArrayList<ArrayList<Integer>> bucketArr = new ArrayList<>(bucketNum);
    		for(int j = 0; j < bucketNum; j++){
    			bucketArr.add(new ArrayList<Integer>());
    		}
    		// 根據映射關係放入對應的桶內
    		for(int i=0; i < arr.length; i++){
    			int index = arr[i] / 10 - min/10;
    			bucketArr.get(index).add(arr[i]);
    		}
    		 // 對每個桶進行排序
    	    for(int i = 0; i < bucketArr.size(); i++){
    	        Collections.sort(bucketArr.get(i));
    	    }
    	    // 把排序號的結果返回a中	
    	    int index = 0;
    		for(int i = 0; i < bucketArr.size(); i++){
    			for(int j = 0; j < bucketArr.get(i).size(); j++){
    				arr[index++] = bucketArr.get(i).get(j);
    			}
    		} 
    	}
    
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章