1. 程式人生 > >排序演算法 及其穩定性解釋

排序演算法 及其穩定性解釋

排序演算法的穩定性是指在待排序的序列中,存在多個相同的元素,若經過排序後這些元素的相對詞序保持不變,即Xm=Xn,排序前m在n前,排序後m依然在n前,則稱此時的排序演算法是穩定的。下面針對常見的排序演算法做個簡單的介紹。

1.氣泡排序

public static void bubbletSort(int[] list){
    for(int k=1;k<list.length;k++){
        for (int i=0;i<list.length-k;i++){
            if (list[i] > list[i+1])
                swap list[i] with list[i+1];
        }
    }
}
從上述氣泡排序的演算法可以看出,其原理就是從左往右相鄰的元素兩兩對比,將大的元素往後移直到排序完成。如果兩個元素相等,是不會交換其位置的,因此氣泡排序是穩定的。注:如果將交換條件改為“list[i]>=list[i+1]”就不是穩定的了。

2.歸併排序

public static void mergeSort(int[] list){
    if (list.length>1){
        mergeSort(list[0...list.length/2]);
        mergeSort(list[list.length/2+1... list.length]);
        merge list[0...list.length/2] with list[list.length/2+1... list.length];
    }
}
public static int[] merge(int[] list1,int[] list2){
    int[] temp = new int[list1.length+list2.length];
    int current1 = 0,current2 =0,current3 = 0;
    while(current1<list1.length && current2<list2.length) {
        if (list1[current1] <= list2[current2])
            temp[current3++] = list1[current1++];
        else
            temp[current3++] = list2[current2++];
    }
    while (current1<list1.length)
        temp[current3++] = list1[current1++];
    while (current2<list2.length)
        temp[current3++] = list2[current2++];
    return temp;
}
從歸併演算法可以看出,其原理是將待排序列遞迴地劃分為短序列,指導每部分都只包含一個元素,然後再合併,合併時如果兩個元素相等也會按照元素之前的順序,把下標小的元素先放入結果列表中,依然沒有破環相同元素之間原本的順序,因此歸併演算法也是穩定的。注:如果在合併中,判定條件改為“if(list1[current1] < list2[current2])”則不穩定。

3.選擇排序

public static void selectionSort(int[] list){
    for (int i=0;i<list.length - 1;i++){
        int currentMin = list[i];
        int currentMinIndex = i;
        for(int j=i+1;j<list.length;j++){
            if (currentMin > list[j]){
                currentMin = list[j];
                currentMinIndex = j;
            }
        }
        if (currentMinIndex != i){
            list[currentMinIndex] = list[i];
            list[i] = currentMin;
        }
    }
}
從選擇排序演算法可以看出,其原理是對待排序列從左到右,為每個位置尋找當前最小的元素,比如為list[0]選擇list中最小的元素與原來list[0]出的元素交換,為list[1]從剩下元素中選擇最小的與其交換,依次類推。但這個演算法是不穩定的,比如“4 8 3 4 2 9 7”,為第一個位置選擇最小的元素2,與第一個位置的4交換,這破環了原來兩個4之間的順序。

4.插入排序

public static void insertionSort(int[] list){
    for (int i=0;i<list.length;i++){
        int currentElement = list[i];
        int k;
        for(k=i-1; k>=0 && list[k] > currentElement; k--){
            list[k+1] = list[k]; //如果當前值大於已排好序列表的k元素,則k元素後移
        }
        list[k+1] = currentElement;
    }
}
從插入排序可以看出,其原理是在一個已經排好序的序列中依次插入一個新的元素。如果碰到相等的元素,就把新元素插入相等元素的後面,即他們原來的順序沒有變化,因此插入排序是穩定的。

5.快速排序

public static void quickSort(int[] list){
    if(list.length > 1){
        select a pivot;
        //一般預設主元為第一個元素,同時從兩邊向中間比較
        //前向找到一個大於主元的元素,後向找到一個小於主元的元素,交換這兩個元素
        //直到前向元素下標大於後向元素下標,把主元與此時後向元素交換,即主元前的元素
        //都小於主元,主元后的元素都大於主元。
        partition list into list1 and list2 such that
            all elements in list1 <= pivot and
            all elements in list2 >pivot;
        quickSort(list1);
        quickSort(list2);
    }
}
快速排序是不穩定的,如“ 5 3 3 4 3 8 9 10 11”第一次切分,主元5要和元素3交換,即改變了3和另兩個相等元素之間的順序。

6.堆排序

堆可以儲存在一個數組中,對於位置i的節點,它的左孩子在位置2i+1處,右孩子在2i+2處,它的父節點在(i-1)/2處。對堆排序分兩步:新增新節點和刪除跟節點。

新增新節點

Let the last node be the current node;
while(the current node is greater than its parent){
swap the current node with its parent;
Now the current node is one level up;
}
新增新節點後每個節點,堆中的元素已排好序;

刪除跟節點:(即降序從堆中取出所有元素)

Remove the root node;
Move the last node to replace the root;
Let the root be the current node;
重新對堆排序;
Heap類:
public class Heap<E extends Comparable> {
    private ArrayList<E> list = new ArrayList<E>();
    public Heap(){
    }
    public Heap(E[] objects){
        for (int i=0;i<objects.length;i++){
            add(objects[i]);
        }
    }
    //新增新的節點
    public void add(E newObject){
        list.add(newObject);
        int currentIndex = list.size()-1;
        while (currentIndex>0){
            int parentIndex = (currentIndex-1)/2;
            if (list.get(currentIndex).compareTo(list.get(parentIndex))>0){
                E temp = list.get(currentIndex);
                list.set(currentIndex, list.get(parentIndex));
                list.set(parentIndex,temp);
            }else
                break;
            currentIndex = parentIndex;
        }
    }
    //刪除跟節點
    public E remove(){
        if (list.size() ==0) return null;
        E removedObject = list.get(0);
        list.set(0,list.get(list.size()-1));
        list.remove(list.size()-1);

        int currentIndex = 0;
        while(currentIndex < list.size()){
            int leftChildIndex = 2*currentIndex+1;
            int rightChildIndex = 2*currentIndex+2;
            if (leftChildIndex >= list.size()) break;
            int maxIndex = leftChildIndex;
            if (rightChildIndex < list.size()){
                if (list.get(maxIndex).compareTo(list.get(rightChildIndex)) < 0){
                    maxIndex = rightChildIndex;
                }
            }
            if (list.get(currentIndex).compareTo(list.get(maxIndex))<0){
                E temp = list.get(maxIndex);
                list.set(maxIndex,list.get(currentIndex));
                list.set(currentIndex,temp);
                currentIndex = maxIndex;
            }else
                break;
        }
        return removedObject;
    }
    public int getSize(){
        return list.size();
    }
}

新增新節點不會破壞相同元素的順序,但刪除根節點獲取會破壞,因此堆排序不是穩定的排序演算法。

7.基數排序

基數排序是按照低位先排序,然後收集;再按照高位排序,然後再收集;依次類推,直到最高位。有時候有些屬性是有優先順序順序的,先按低優先順序排序,再按高優 先級排序,最後的次序就是高優先順序高的在前,高優先順序相同的低優先順序高的在前。基數排序基於分別排序,分別收集,所以其是穩定的排序演算法。

8.希爾排序

希爾排序是按照不同步長對元素進行插入排序,當剛開始元素很無序的時候,步長最大,所以插入排序的元素個數很少,速度很快;當元素基本有序了,步長很小, 插入排序對於有序的序列效率很高。所以,希爾排序的時間複雜度會比o(n^2)好一些。由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元 素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,所以shell排序是不穩定的。

綜上所述:

穩定排序演算法 不穩定排序演算法
氣泡排序、插入排序、歸併排序、基數排序 選擇排序、快速排序、希爾排序、堆排序


相關推薦

排序演算法 及其穩定性解釋

排序演算法的穩定性是指在待排序的序列中,存在多個相同的元素,若經過排序後這些元素的相對詞序保持不變,即Xm=Xn,排序前m在n前,排序後m依然在n前,則稱此時的排序演算法是穩定的。下面針對常見的排序演算法做個簡單的介紹。 1.氣泡排序 public static void

常見排序演算法及其穩定性

首先,排序演算法的穩定性大家應該都知道,通俗地講就是能保證排序前2個相等的數其在序列的前後位置順序和排序後它們兩個的前後位置順序相同。在簡單形式化一下,如果Ai = Aj,Ai原來在位置前,排序後Ai還是要在Aj位置前。       其次,說一下穩定性的好處。排序演算法如果

排序演算法穩定性及其彙總

1 快速排序(QuickSort)快速排序是一個就地排序,分而治之,大規模遞迴的演算法。從本質上來說,它是歸併排序的就地版本。快速排序可以由下面四步組成。(1) 如果不多於1個數據,直接返回。(2) 一般選擇序列最左邊的值作為支點資料。(3) 將序列分成

排序演算法穩定性及其意義

穩定性的定義       假定在待排序的記錄序列中,存在多個具有相同的關鍵字的記錄,若經過排序,這些記錄的相對次序保持不變,即在原序列中,ri=rj,且ri在rj之前,而在排序後的序列中,ri仍在rj之前,則稱這種排序演算法是穩定的;否則稱為不穩定的。

排序演算法及其演算法

排序演算法及其子演算法 各類排序演算法 插入排序(insertion sort) 融合排序(Merge Sort) merge two sorted array 各類排序演算法 插入

演算法 排序演算法穩定性

排序與搜尋 排序演算法(英語:Sorting algorithm)是一種能將一串資料依照特定順序進行排列的一種演算法。 排序演算法的穩定性 穩定性:穩定排序演算法會讓原本有相等鍵值的紀錄維持相對次序。也就是如果一個排序演算法是穩定的,當有兩個相等鍵值的紀錄R和S,且在原本的列表中R出現在

C語言八大排序演算法(包括穩定性、時間複雜度等)(收藏)

C語言八大排序演算法 概述 排序有內部排序和外部排序,內部排序是資料記錄在記憶體中進行排序,而外部排序是因排序的資料很大,一次不能容納全部的排序記錄,在排序過程中需要訪問外存。我們這裡說說八大排序就是內部排序。 當n較大,則應採用時間複雜度為O(nlog2n)的排序方法:快速排序、堆排

歸併排序演算法及其C語言具體實現

本節介紹一種不同於插入排序和選擇排序的排序方法——歸併排序,其排序的實現思想是先將所有的記錄完全分開,然後兩兩合併,在合併的過程中將其排好序,最終能夠得到一個完整的有序表。 例如對於含有 n 個記錄的無序表,首先預設表中每個記錄各為一個有序表(只不過表的長度都為 1),然後進行兩兩合併,使 n 個有序表變為

單鏈表的快速排序演算法及其實現

今天聽同學面友錄說道單鏈表是否可以用快速排序演算法,想起自己面百度一面的時候面試官也面到這個問題,由於本人是個小菜鳥,所以花了一個下午的時間整理了一下。 演算法思想:對於一個連結串列,以head節點

【整理】常見排序演算法及其時間複雜度總結

原文出處: 本篇主要整理了氣泡排序,直接插入排序,直接選擇排序,希爾排序,歸併排序,快速排序,堆排序七種常見演算法,是從上面三篇博文中摘抄整理的,非原創。 一、氣泡排序 主要思路是: 通過交換相鄰的兩個數變成小數在前大數在後,這樣每次遍歷後,最大的數就“沉”到最後面了。重複N次即可以使陣列有序。 氣泡

各種排序演算法及其複雜度

穩定的   氣泡排序(bubble sort) — O(n^2)   雞尾酒排序(Cocktail sort,雙向的氣泡排序) — O(n^2)   插入排序(insertion sort)— O(n^2)   桶排序(bucket sort)— O(n); 需要 O

排序演算法穩定性時間複雜度分析)

1、  選擇排序、快速排序、希爾排序、堆排序不是穩定的排序演算法,        氣泡排序、插入排序、歸併排序和基數排序是穩定的排序演算法。 2、研究排序演算法的穩定性有何意義?   首先,排序演算法的穩定性大家應該都知道,通俗地講就是能保證排序前兩個相等的資

十大排序演算法及其實現(C++ & Python)

經典的幾大排序演算法,網上各種版本程式碼質量層次不齊。在此想自己做個總結,一方面希望通過這次總結加深自己對幾種排序演算法的認識和記憶,另一方面也希望能寫下來與大家分享。 每個演算法力求給出普通解法和最優解法,當前部分排序演算法還沒有給出最優解,待後續的

排序演算法穩定性分析(含java程式碼)

首先,排序演算法的穩定性大家應該都知道,通俗地講就是能保證排序前2個相等的數其在序列的前後位置順序和排序後它們兩個的前後位置順序相同。在簡單形式化一下,如果Ai = Aj,Ai原來在位置前,排序後Ai還是要在Aj位置前。 其次,說一下穩定性的好處。排序演算

深入學習排序演算法穩定性、比較次數、交換次數探討

        在學習排序演算法時,出於效率考慮,經常容易看到演算法的穩定性、比較次數及交換次數研究。特別是考試或者公司筆試題,經常出現這樣的題目。由於排序演算法有很多種,平時提出大家才能說出個大概,

幾種常見排序演算法及其效率

介紹了幾種交換排序的演算法 1。氣泡排序(Bubble Sort)是一種簡單的排序演算法。它重複地走訪過要排序的數列,一次比較兩個元素,如果他們的順序錯誤就把他們交換過來。走訪數列的工作是重複地進行直到沒有再需要交換,也就是說該數列已經排序完成。這個演算法的名字由來是因為越小的元素會經由交換慢慢“浮”到數列

判斷各種排序演算法穩定性

排序演算法穩定性   如果在一個待排序的序列中,存在2個相等的數,在排序後這2個數的相對位置保持不變,那麼該排序演算法是穩定的;否則是不穩定的。 舉個例子   對五個同學(A,B,C,D,E)進行成績排序,他們的成績分別為:90,88,79,88,92,按

各種排序演算法及其C++程式碼實現

概念一:排序演算法是否是穩定的 給定序列{3,15,8,8,6,9} 若排序後得到{3,6,8,8,9,15}, 則該排序演算法是不穩定的,原本後面的8(加粗)現在位於前面了。 概念二: 內部排序、外部排序 內部排序是指將待排序的資料存放在記憶體中進行排序的過程。 外部排序是指待排序

七種排序演算法及其複雜度

1.氣泡排序 時間複雜度:平均:O(N^2),最壞:O(N^2),最好:O(N) 思想:從後往前開始,相鄰的兩兩進行比較,將小的逐漸移到前面。(也可以從前往後開始兩兩相鄰的進行比較) 程式碼: void BubbleSort(int *a, int N) { if (a

資料結構與演算法:常見排序演算法及其python實現

0、 綜合分析 0.1 排序演算法的種類及時間限制 常見排序演算法一般分為非線性時間比較類排序和線性時間非比較類排序。 比較類排序演算法時間複雜度的下限為O(nlog⁡n)O(n\log n)O(nlogn),非比較類排序演算法不受比較式排序演算法的時間下