1. 程式人生 > >排序演算法總結(一)——選擇排序

排序演算法總結(一)——選擇排序

選擇排序的基本宗旨就是每次選出剩餘元素中最大的或者最小放在最終排序的對應位置。

1.直接選擇排序
基本思想:
在a[1]-a[n-1]中選擇最小的元素和a[0]交換;
在a[2]-a[n-1]中選擇最小的元素和a[1]交換;
……
在a[i]-a[n-1]中選擇最下的元素和a[i-1]交換;
以此類推。。。。。。

演算法步驟:
迴圈比較:
第一輪:將a[0]和a[1]-a[n-1]中的每個元素依次比較,若出現a[0]>a[j],則將兩者進行交換;由此可以將陣列中最小的元素放到a[0];
第二輪:將a[1]和a[2]-a[n-1]中的每個元素依次比較。同樣若出現a[1]>a[j],則將兩者交換,由此將倒數第二小的元素放到a[1];
依次類推。。。

程式碼實現:

public class SelectedSort {
    //測試
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] a = {2,5,1,7,4,3,6};
        sort(a);
    }
    //排序
    public static void sort(int[] a){
        //雙重迴圈,依次比較大小
        for(int i = 0;i<a.length-1;i++){
            for
(int j = i+1;j<a.length;j++){ if(a[j]<a[i]){ int tmp = a[j]; a[j] = a[i]; a[i] = tmp; } } } for(int i = 0;i<a.length;i++){ System.out.println(a[i]); } } }

2.直接選擇排序的改進
在直接選擇排序中,如在第一輪比較中,一旦找到比a[0]小的元素便進行交換,這樣導致最多需要n-1次比較和交換才能找到最小的元素,但是我們每輪比較的目的是選出最小的元素放到a[0]上,所以可以改進上訴的演算法,在找到最小元素後再跟a[0]進行交換,這樣只需要交換一次即可。

演算法實現:設定一個標記為,每次標記較小的元素所在的位置,直到找到最小的元素位置,然後直接進行交換即可。

程式碼實現:

public class SelectedSort {

    public static void main(String[] args) {
        int[] a = {2,5,1,7,4,3,6};
        sort(a);
}

    public static void sort(int[] a){
        int minI = 0;

        for(int i = 0;i<a.length-1;i++){
            minI = i;
            for(int j = i+1;j<a.length;j++){
                if(a[j]<a[minI]){
                    minI = j;
                }
            }
            if(minI!=i){
                int tmp = a[minI];
                a[minI] = a[i];
                a[i] = tmp;
            }
        }

        for(int i = 0;i<a.length;i++){
            System.out.println(a[i]);
        }   
    }
}

(1)堆
堆是一種特殊的幾乎完全二叉樹。
堆的性質:葉子結點的鍵值或索引總是小於(或者大於)它的父節點(大頂堆和小頂堆)。
這裡寫圖片描述

對堆的訪問:
根節點的位置為0;
父節點i的左子節點在位置 (2*i+1);
父節點i的右子節點在位置 (2*i+2);
子節點i的父節點在位置 (i-1)/2;
最後一個非葉子節點的下標是n/2-1 ,因為其為最後一個節點(n-1)的父節點。

堆結構對映為陣列即為如下所示:
這裡寫圖片描述

(2)堆排序(升序採用大頂堆,降序採用小頂堆)
基本思想:將待排序的陣列構造成一個大頂堆,那麼此時根節點的元素即為最大值,將其與最後一個葉子節點進行交換,即將最大元素放置在了末尾;然後將剩餘的n-1個元素重新構造成一個大頂堆,依次類推,最終完成對陣列的排序。

實現步驟:

A. 根據給定無序陣列構造一個大頂堆。
給定的無序資料如下(設長度為n,此出n=5):
這裡寫圖片描述
首先從最後一個非葉子節點開始調整,因為葉子節點需要跟著其父節點進行調整。第一個非葉子節點為n/2-1,即數值為6的節點。
對於堆結構來說,因為堆是一個完全二叉樹,所以肯定存在左子節點,所以首先判斷是否存在右子節點,若存在,則將兩個子節點進行比較,將數值大的子節點再與父節點比較,進而當子節點>父節點的情況下,父節點與子節點進行交換。
如圖,6和9進行交換。
這裡寫圖片描述

找到第二個非葉子節點4,同理將4和9交換。
這裡寫圖片描述

明顯可以看出,但是這步交換導致[4,5,6]不滿足堆結構,所以需要繼續調整。
這裡寫圖片描述
至此,成功構成一個大頂堆。

B.將根節點和最後一個葉子節點進行交換,並且需要對n-1個元素繼續調整成堆(從根節點開始調整,因為除了根節點外其他均已滿足堆結構),以此類推,不斷進行交換,重建。。。。。。
將4和9進行交換
這裡寫圖片描述

重新構建大頂堆
這裡寫圖片描述

將8和5交換
這裡寫圖片描述

以此接著調整和交換,最終得到如下有序序列
這裡寫圖片描述

堆排序基本思路總結:
a. 首先從最後一個非葉子節點,從右至左,從下至上,將無序序列構建成一個大頂堆;
b.將根節點與最後一個葉子節點進行交換,得到最大元素;
c.因為根節點發生了變化,其他父節點均滿足堆結構,所以從根節點開始調整,將剩餘元素繼續構造成大頂堆,並交換根節點和最後一個葉子節點,直到完成排序。

程式碼實現如下:

public class HeapSort1 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        int[] a = {2,3,1,4,6,0};
        sort(a);
        for(int i = 0;i<a.length;i++)
            System.out.println(a[i]);

    }

    public static void sort(int[] a){
        int n = a.length;
        int t = 0;

        //初始化大根堆,從最後一個非葉子節點開始調整
        for(int i=(a.length/2-1);i>-1;i--){
            adjustHeap(a,i,n);
        }
        //交換元素並繼續調整,從根節點開始調整
        for(int i=n-1;i>-1;i--){
            int tmp = a[i];
            a[i] = a[0];
            a[0] = tmp;
            adjustHeap(a,0,i);
        }

    }
    //對某一個父節點進行調整
    public static void adjustHeap(int[] a, int parent, int length){
        int temp = a[parent];
        int child = parent*2+1;//從左子節點開始

        while(child < length-1){
            //判斷是否存在右子葉子節點,並將大的賦值給child
            if(child+1<length && a[child]<a[child+1]){
                child++;
            }
            //判斷父節點是否比子節點大,若大於則說明當前父節點無需接著進行調整
            if(temp > a[child])
                break;
            //否則交換父節點和子節點
            a[parent] = a[child];

            //因上面的調整影響到下面節點,所以接著進行調整
            parent = child;
            child = parent *2+1;
        }
        a[parent] = temp;   
        }
}

總結:
選擇排序的基本思想就是每次都選擇出當前最大或者最小的元素直接放到相應的位置上。