【數據結構與算法】簡單排序(選擇、冒泡、插入、希爾排序)、二分查找


選擇排序

概念

image
首先,找到數組中最小的那個元素,其次,把它和數組的第一個元素交換位置(如果第一個元素就是最小的元素那么它就和自己交換)。再次,在剩下的元素中找到最小的元素,將它與數組的第二個元素交換位置。如此往復,直到將整個數組排序。這種方法叫做選擇排序,因為它在不斷地選擇剩余元素中地最小者。

代碼實現

   public static void SelectionSort(int[] arr){
        if(arr==null||arr.length<2) return;     //去除多余情況
        int N = arr.length;
        for (int i = 0; i < N-1; i++) {
            int minIndex = i;
            for (int j = i+1; j < N; j++){
                if(arr[j] < arr[minIndex]) minIndex = j;  //更新每一輪最小元素的下標
            }
            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 selectionSort2(int[] arr) {
    int minIndex, maxIndex;
    // i 只需要遍歷一半
    for (int i = 0; i < arr.length / 2; i++) {
        minIndex = i;
        maxIndex = i;
        for (int j = i + 1; j < arr.length - i; j++) {
            if (arr[minIndex] > arr[j]) {
                // 記錄最小值的下標
                minIndex = j;
            }
            if (arr[maxIndex] < arr[j]) {
                // 記錄最大值的下標
                maxIndex = j;
            }
        }
        // 如果 minIndex 和 maxIndex 都相等,那么他們必定都等於 i,且后面的所有數字都與 arr[i] 相等,此時已經排序完成
        if (minIndex == maxIndex) break;
        // 將最小元素交換至首位
        int temp = arr[i];
        arr[i] = arr[minIndex];
        arr[minIndex] = temp;
        // 如果最大值的下標剛好是 i,由於 arr[i] 和 arr[minIndex] 已經交換了,所以這里要更新 maxIndex 的值。
        if (maxIndex == i) maxIndex = minIndex;
        // 將最大元素交換至末尾
        int lastIndex = arr.length - 1 - i;
        temp = arr[lastIndex];
        arr[lastIndex] = arr[maxIndex];
        arr[maxIndex] = temp;
    }
}

復雜度分析

選擇排序過程中,0~N-1 上任意位置i都要進行一次交換和N-1-i次比較。因此總共有N次交換和(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2次比較。
也就是O(N^2)

  • 不穩定排序

冒泡排序

概念

image
從第一個元素開始遍歷數組每兩個元素一組比較,如果不滿足從小到大的排序規則則進行交換,這樣最后可以得到最大元素在數組最后的位置排好。如此往復,直到整個數組排好。

代碼實現

法一:
循環每經過一輪,剩余數字中的最大值仍然是被移動到當前輪次的最后一位

    public static void sort(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;
    }

法二:flag標記
如果一輪比較中沒有發生過交換,則立即停止排序,因為此時剩余數字一定已經有序了。

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

復雜度分析

總的比較次數是(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2
也就是O(N^2)

  • 穩定排序

插入排序

概念

image
從左向右遍歷,每次把遍歷到的元素放到前面已經排好順序的數組的合適位置。如此往復,直到整個數組排好。

代碼實現

    public static void sort(int[] arr){
        if(arr==null||arr.length<2) return;     //去除無效情況
        for(int i = 1; i < arr.length; i++){    //i從1開始,認為i為1時已經排好
            for(int j = i-1; j >= 0 && arr[j] > arr[j+1]; j--)
                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;
    }

復雜度分析

總的比較和交換次數都是(N-1)+(N-2)+……+1=N(N-1)/2~N^2/2
也就是O(N^2)

  • 穩定排序

希爾排序

概念

image

希爾排序是插入排序的一種。
也稱縮小增量排序,是直接插入排序算法的一種更高效的改進版本。希爾排序是非穩定排序算法

  • 思路:如序列 9 8 7 6 5 4 3 2 1
    確定一個增量序列,如 4(length/2) 2 1 ,從大到小使用增量
    使用第一個增量,將序列划分為若干個子序列,下標組合為0-4-8,1-5,2-6,3-7
    依次對子序列使用直接插入排序法;
    使用第二個增量,將序列划分為若干個子序列(0-2-4-6-8),(1-3-5-7)
    依次對子序列使用直接插入排序法:
    使用第三個增量1,這時子序列就是元序列(0-1-2-3-4-5-6-7-8),使用直接插入法完成排序。

  • 時間復雜度:不太確定在O(nlogn)~O(n²)之間

  • 空間復雜度:O(1)

  • 原址排序

  • 穩定性:由於相同的元素可能會被划分至不同子序列單獨排序,因此穩定性是無法保證的——不穩定

代碼實現

  public static void main(String[] args) {
    int[] arr = {9, 9, 6, 7, 5, 4, 2, 2, 1};
    shellSort(arr);
    for(int i = 0; i < arr.length; i++){
	    System.out.println(arr[i]+" ");
	}
  }

  public static void shellSort(int[] arr) {
    //不斷地縮小增量
    for (int interval = arr.length / 2; interval > 0; interval = interval / 2) {
      //增量為interval的插入排序
      for (int i = interval; i < arr.length; i++) {
        int target = arr[i];  //使用target臨時變量保存arr[i]
        int j = i - interval; //j 指向縮小增量數組的i 元素的前一個元素下標
        while (j >= 0 && target < arr[j]) {
          arr[j + interval] = arr[j];
          j -= interval;
        }
        arr[j + interval] = target;
      }
    }
  }

時間性能分析

希爾排序開始時增量較大,每個子序列中的記錄個數較少,從而排序速度較快;當增量較小時,雖然每個子序列中記錄個數較多,但整個序列已基本有序,排序速度也較快。

希爾排序算法的時間性能取決於增量的函數,而到目前為止尚未有人求得一種最好的增量序列。研究表明,希爾排序的時間性能在O(n^2)和O(nlogn)之間。當n在某個特定范圍內,希爾排序所需的比較次數和記錄的移動次數約為O(n ^1.3)。

二分查找

左側邊界二分查找

  • 如果數組中有目標值,在arr上,找滿足=value的最左位置

  • 如果數組中沒有目標值,返回大於value的第一個數的位置

// 區間[l, r]被划分成[l, mid]和[mid + 1, r]時使用:
    public static int binarySearch(int[] nums, int target) {
        int n = nums.length;
        int l = 0;
        int r = n - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }
        return nums[l] == target ? l : -1;
    }

右側邊界二分查找

  • 如果數組中有目標值,在arr上,找滿足=value的最右位置

  • 如果數組中沒有目標值,返回小於value的第一個數的位置

// 區間[l, r]被划分成[l, mid - 1]和[mid, r]時使用:
    public static int binarySearch(int[] nums, int target) {
        int n = nums.length;
        int l = 0;
        int r = n - 1;
        while (l < r) {
            int mid = l + r + 1 >> 1;
            if (nums[mid] <= target) l = mid;
            else r = mid - 1;
        }
        return nums[l] == target ? l : -1;
    }

整數二分總結

  • 左側邊界二分查找
    先寫if (arr[mid] >= value)
  • 右側邊界二分查找
    先寫if (arr[mid] <= value)
  • 別忘了最后對index為-1做特殊處理,防止數組越界訪問

浮點數二分

手動開方

public static double sqrt(double x) {
        double l = 0, r = x;
        while (r - l > 1e-8) {
            double mid = (l + r) / 2;
            if (mid * mid >= x)
                r = mid;
            else
                l = mid;
        }
        return r;
    }


免責聲明!

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



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