4、由java實現的簡單算法——選擇排序(雙向選擇排序以及改進方法)

一  前言

如果對本系列產生什麼疑問的話,  建議先下前言,  裏面有我的聯繫方式,  教材的下載地址,   特殊詞語的規定之類的........   鏈接地址: https://blog.csdn.net/qq_41057280/article/details/89209081 ;   最後, 以下說法僅爲個人理解, 如有錯誤, 歡迎教正

二  介紹

選擇排序(Selection sort),是一種通過不斷選擇剩餘數組的元素來實現排序的算法。

具體的步驟如下:

  1. 從第一位開始遍歷數組選出最小的元素將其放到第一位
  2. 從第二位開始遍歷選出最小的元素放到第二位
  3. ......
  4. 從第n-1位開始遍歷選出最小的元素放到第n-1位.
  5. 最後整個數組排序完成
     

三  java代碼

首先,  先來個常規的照着步驟寫一個

/**
* 選擇排序
* 從第一位開始遍歷數組選出最小的元素將其放到第一位
* 從第二位開始遍歷選出最小的元素放到第二位
* ......
* 從第n-1位開始遍歷選出最小的元素放到第n-1位.
* 整個數組排序完成
*/
public void sort(int[] arr) {
    int n = arr.length;// 數組長度
    int min = 0; // 最小值的下標

    for (int i=0; i<n-1; i++) {
        min = i;
        // 如果有比最小值小的,就更新下標
        for (int j=min+1; j<n; j++) {
            if (arr[j] < arr[min]) {
                min = j;
            }
        }
        // 如果最小值的下標不在開始位置, 則進行交換
        if (i != min) {
            Algorithm.swap(arr, min, i);
        }
        Algorithm.printArray(arr);
    }
}

這是一個非常簡單而且十分直觀的排序方法。雖然簡單,但還是有需要注意的點。在排序過程中最小值可能在開始位置,所以不能直接交換,需要進行判斷。

另外, 根據代碼我們可以發現,這個算法的時間複雜度O(n²)。具體怎麼算,這裏就不列舉了。 最後,如果待排序數組有數值相同的元素, 那麼他們的相對先後順序就會被破壞(後面的元素到前面去了,因爲我們是順序比較的),因此該算法是一個不穩定的排序算法。

不過,該算法是交換次數最少的排序算法,最多隻會移動n-1次。 這應該也算得上他爲數不多的優點

五  雙向選擇排序算法

基礎的選擇算法每一趟循環只能確保一個元素位置正確, 算法效率不高。 我們可以試着改進,比如說一次循環分別確定最大、小元素的位置,從而減少循環次數。 這就是雙向選擇排序算法

通俗點解釋就是,每次排序都從待排序的無序數組中選出最大和最小值的索引,將最小值交換至無序表表頭、最大值交換至無序表表尾,從而逐漸在無序表的兩端形成有序表。 理論上,大小爲n的數組只要進行n/2次排序即可。 

代碼如下:

在排序的過程中,有可能出現最大值和最小值的索引與邊界值重合,所以最後必須分情況進行判斷。

ps:我總感覺這段判斷很醜,有沒有大佬願意幫我改進下的

/**
* 雙向選擇排序算法 在選擇算法的基礎上,在一次循環中選擇兩個元素(最大和最小)。 以此減少循環次數提高效率
*/
public void doubleSelectionSort(int[] arr) {
    int n = arr.length;// 數組長度
    for (int i = 0; i < n / 2; i++) {
        int rangeL = i; // 待排序數組的左邊界
        int rangeR = n - i - 1; // 待排序數組的右邊界
        int min = rangeL;
        int max = rangeR;
        
        for (int j = rangeL; j <= rangeR; j++) {
            // 如果有比最小值小的,就更新最小值下標
            if (arr[j] < arr[min]) {
                min = j;
            }

            // 如果有比最大值大的,就更新最大值下標
            if (arr[j] > arr[max]) {
                max = j;
            }
        }
        // 注意特殊情況
        if (rangeL == min && rangeR == max) {
            // 位置正確,不改動
        } else if (rangeL == min) {
            // 最小值在開始位置,則交換最大值到結束位置
            // swap (max, rangeR)
            Algorithm.swap(arr, max, rangeR);
        } else if (rangeR == max) {
            // 最大值在結束位置,則交換最小值到開始位置
            // swap (max, rangeR)
            Algorithm.swap(arr, min, rangeL);
        } else if (rangeR == min && rangeL == max) {
            // 最大值在開始位置,最小值在結束位置
            // swap(rangeL, rangeR)
            Algorithm.swap(arr, rangeL, rangeR);
        } else if (rangeL == max) {
            // 最大值在開始位置,先交換最大值
            Algorithm.swap(arr, max, rangeR);
            Algorithm.swap(arr, min, rangeL);
        } else {
            // 正常交換
            Algorithm.swap(arr, min, rangeL);
            Algorithm.swap(arr, max, rangeR);
        }
        
        // 打印
        Algorithm.printArray(arr);
    }
}

雖然我是說這是選擇排序的一種改進方式,但根據代碼我們可以發現,這個算法的時間複雜度還是爲O(n²)。而且比較次數也比原先要多,再加上最後的分情況討論,使得算法的複雜性高於原算法、效率也不及原算法。 總結,這種改進毫無意義。

六、雙向選擇排序算法(改)

綜上所述,普通的雙排並沒有對原來的選擇排序算法改進優化。  主要問題在於最後交換前的分情況處理使算法變得複雜效率下降,如果我們能省略這一步的話,那才能真正意義上得到優化。

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章