你知道和你不知道的選擇排序

1. 什麼是選擇排序?

首先貼上從wiki上弄下來的關於選擇排序的定義。java

選擇排序(Selection sort)是一種簡單直觀的排序算法。它的工做原理以下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。web

更加直白的解釋是,每次都從數組中選出最大或者最小的元素,而後放到數組的左邊。算法

2. 選擇排序的過程展現

老規矩,咱們仍是經過動圖來看一下選擇排序的過程。如下的gif來自於wiki。數組

而後咱們再經過我製做的gif,配上數據再瞭解一下過程。假設咱們的待排序數組仍是[5, 1, 3, 7, 6, 2, 4]。服務器

3. 選擇最小值的算法

咱們使用Java來實現最多見的,選擇最小值的選擇排序,其代碼以下。微信

private void selectionSort(int[] arr) {
  int min;
  int minIndex;
  for (int i = 0; i < arr.length - 1; i++) {
    min = arr[i];
    minIndex = -1;
    for (int j = i; j < arr.length; j++) {
      if (arr[j] < min) {
        min = arr[j];
        minIndex = j;
      }
    }
    // 排序結束 交換位置
    if (minIndex != -1) {
      exchange(arr, i, minIndex);
    }
  }
}

private void exchange(int arr[], int i, int j) {
  int temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼

假設數組的長度爲7,那麼算法就須要進行6輪。若是數組的長度爲n,則算法須要進行n - 1輪。app

每一輪,算法都會從剩下的待排序元素中,選出最小的元素,並將其與當前數組下標爲i也就是有序序列的起始位置的元素交換。這樣一來,通過反覆的排序,最終造成有序數組。frontend

4. 選擇最大值的算法

上面實現了選擇最小值的代碼,接下來咱們繼續實現選擇最大值的代碼。微服務

private void selectionSort(int[] arr) {
  int max;
  int maxIndex;

  for (int i = 0; i < arr.length - 1; i++) {
    max = Integer.MIN_VALUE;
    maxIndex = -1;
    for (int j = 0; j < arr.length - i; j++) {
      if (max < arr[j]) {
        max = arr[j];
        maxIndex = j;
      }
    }

    // 排序結束 交換位置
    if (maxIndex != -1) {
      exchange(arr, maxIndex, arr.length - i - 1);
    }
  }
}

private void exchange(int arr[], int i, int j) {
  int temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼

這個思想與選擇最小值的算法徹底同樣,只不過是選擇了最大值,每次都將剩餘序列的最大值放到數組的有序序列的最左邊。工具

那麼到此,選擇排序最多見的兩種寫法咱們都已經實現了。有的兄弟可能會想,這篇博客是否是結束了。其實咱們能夠從上面兩個算法中想到能夠優化的點。

既然咱們有兩個選擇,一種選擇最小值,另一種選擇最大值。那麼咱們爲何不一樣時進行兩個操做呢?

下面咱們就來實現這種算法。

5. 同時選擇最大值和最小值

private void selectionSort(int[] arr) {
  int min;
  int max;
  int minIndex;
  int maxIndex;

  for (int i = 0; i <= arr.length / 2; i++) {
    min = Integer.MAX_VALUE;
    max = Integer.MIN_VALUE;
    minIndex = -1;
    maxIndex = -1;
    for (int j = i; j < arr.length - i; j++) {
      if (arr[j] < min) {
        min = arr[j];
        minIndex = j;
      }
      if (arr[j] > max) {
        max = arr[j];
        maxIndex = j;
      }
    }
    // 排序結束 交換位置
    if (minIndex != -1) {
      if (maxIndex == i) {
        maxIndex = minIndex;
      }
      exchange(arr, i, minIndex);
    }

    if (maxIndex != -1) {
      exchange(arr, maxIndex, arr.length - 1 - i);
    }
  }
}

private void exchange(int arr[], int i, int j) {
  int temp = arr[i];
  arr[i] = arr[j];
  arr[j] = temp;
}

int[] arr = new int[]{5, 1, 3, 7, 6, 2, 4};
selectionSort(arr);
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7]
複製代碼

由於選擇最大值和最小值同時進行,相對於上面兩種算法,同時選擇算法在執行次數上比前兩種算法減小了50%。

在運行時間上相對於選擇最小值和最大值分別減小了39.22%和62.20%。

6. 總結

如下是對同一個長度爲10000的隨機亂序數組使用三種算法的狀況。

[0 - 10000] 的亂序數組 取最小值 取最大值 同時取最大值最小值
100次平均執行時間(ms) 51 82 31
執行次數(次) 50004999 50004999 25005000

最後咱們看一下選擇排序算法的時間複雜度。

  • 最好的狀況爲O(n ^ 2). 即便整個數組都是有序的,選擇排序也會執行完選擇最大值或者最小值的過程,只是不會去進行元素交換。
  • 最壞的狀況爲O(n ^ 2). 同上,會執行完選擇最大值或者最小值的過程,而且每次都須要進行元素交換。

其空間複雜度爲O(n),上面三種算法都屬於原地排序算法,除了交換元素使用了一個輔助空間以外,沒有額外申請空間,同時選擇排序是不穩定排序。

往期文章:

相關:

  • 微信公衆號: SH的全棧筆記(或直接在添加公衆號界面搜索微信號LunhaoHu)
相關文章
相關標籤/搜索