八大排序算法的實現和分析

1.插入排序—直接插入排序(Straight Insertion Sort)

算法思想:
每次從無序表中取出第一個元素,把它插入到有序表的合適位置,使有序表仍然有序。第一趟比較前兩個數,然後把第二個數按大小插入到有序表中; 第二趟把第三個數據與前兩個數從後向前掃描,把第三個數按大小插入到有序表中;依次進行下去,進行了(n-1)趟掃描以後就完成了整個排序過程。

穩定性分析:
如果碰見一個和插入元素相等的,那麼插入元素把想插入的元素放在相等元素的後面。所以,相等元素的前後順序沒有改變,從原無序序列出去的順序就是排好序後的順序,所以插入排序是穩定的。
這裏寫圖片描述

代碼實現_C/C++

#include<iostream>
using namespace std;
int main()
{
    int a[]={98,76,109,34,67,190,80,12,14,89,1};
    int k=sizeof(a)/sizeof(a[0]); //數組長度=數組佔內存總空間,除以單個元素佔內存空間大小
    int j;
    for(int i=1;i<k;i++)//循環從第2個元素開始  //外層循環標識並決定待比較的數值。內層循環爲待比較數值確定其最終位置
    {
        if(a[i]<a[i-1])
        {
            int temp=a[i];
            for(j=i-1;j>=0 && a[j]>temp;j--)
            {
                a[j+1]=a[j];
            }
            a[j+1]=temp;//此處就是a[j+1]=temp;
        }
    }

    for(int f=0;f<k;f++)
    {
        cout<<a[f]<<"  ";
    }
    return 0;
}

效率:
時間複雜度:O(n^2)

2.插入排序—希爾排序(Shell`s Sort)

算法思想:
先取一個小於n的整數d1作爲第一個增量,把文件的全部記錄分組。所有距離爲d1的倍數的記錄放在同一個組中,先在各組內進行直接插入排序;然後,取第二個增量d2<
d1重複上述的分組和排序,直至所取的增量d =1,即所有記錄放在同一組中進行直接插入排序爲止。

穩定性分析:
由於多次插入排序,我們知道一次插入排序是穩定的,不會改變相同元素的相對順序,但在不同的插入排序過程中,相同的元素可能在各自的插入排序中移動,最後其穩定性就會被打亂,所以shell排序是不穩定的。
這裏寫圖片描述

代碼實現_java:

public static void main(String [] args)
{
    int[]a={49,38,65,97,76,13,27,49,78,34,12,64,1};
        System.out.println("排序之前:");
        for(int i=0;i<a.length;i++)
        {
            System.out.print(a[i]+" ");
        }
        //希爾排序
         int d=a.length;
        while(true){
            for(int i=0;i<d;i++){      //外層循環標識並決定待比較的數值。內層循環爲待比較數值確定其最終位置
                for(int j=i;j+d<a.length;j+=d){
                int temp;
                if(a[j]>a[j+d]){
                    temp=a[j];
                    a[j]=a[j+d];
                    a[j+d]=temp;
                    }
                }
            }
            if(d==1){break;}
            d--;
           }

            System.out.println();
            System.out.println("排序之後:");
                for(int i=0;i<a.length;i++)
                {
                    System.out.print(a[i]+" ");
                }
    }

3. 選擇排序—簡單選擇排序(Simple Selection Sort)

基本思想:

在要排序的一組數中,選出最小(最大)的一個數與第1個位置的數交換;然後在剩下的數當中再找最小(或者最大)的與第2個位置的數交換,依次類推。知道第n-1個元素(倒數第二個數)和第n個數(最後一個數)比較爲止。

這裏寫圖片描述

簡單排序處理流程

(1) 從待排序序列中,找到關鍵字最小的元素;

(2) 如果最小元素不是待排序序列的第一個元素,將其和第一個元素互換;

(3) 從餘下的 N - 1 個元素中,找出關鍵字最小的元素,重複(1)、(2)步,直到排序結束。

穩定性分析:

舉個例子,序列5 8 5 2 9,我們知道第一遍選擇第1個元素5會和2交換,那麼原序列中兩個5的相對前後順序就被破壞了,所以選擇排序是一個不穩定的排序算法。直接選擇排序的平均時間複雜度爲O(n^2)

代碼實現:

  public static void main(String [] args) {
    int[]list={49,38,65,97,76,13,27,49,78,34,12,64,1};
    // 需要遍歷獲得最小值的次數
    // 要注意一點,當要排序 N 個數,已經經過 N-1 次遍歷後,已經是有序數列
    for (int i = 0; i < list.length - 1; i++) {
        int temp = 0;
        int index = i; // 用來保存最小值得索引
        // 尋找第i個小的數值
        for (int j = i + 1; j < list.length; j++) {
            if (list[index] > list[j]) {
                index = j;
            }
        }
        // 將找到的第i個小的數值放在第i個位置上
        temp = list[index];
        list[index] = list[i];
        list[i] = temp;
        System.out.print(list[i]+" ");
    }
   }

與冒泡排序的區別:

1、選擇排序 :a[0]與a[1] 比較,如果 a[0]大於a[1], 記錄最小數a[1]的位置1,然後j++,變成了a[2]與當前最小的數a[1]比較,慢慢往後循環,記錄最小的數,最好將最小的數與第一個數a[0]交換位置

2、冒泡排序:首先第一個數 a[0]與第二個數a[1] 比較(從小到大),然後第二個數 a[1]與第三個數a[2] 比較;第三個數 a[2]與第四個數a[3]…… 第一內層循環結束;

4. 選擇排序—堆排序(Heap Sort)

            //詳細見下一篇博客

5. 交換排序—冒泡排序(Bubble Sort)

算法思想:
在要排序的一組數中,對當前還未排好序的範圍內的全部數,自上而下對相鄰的兩個數依次進行比較和調整,讓較大的數往下沉,較小的往上冒。即:每當兩相鄰的數比較後發現它們的排序與排序要求相反時,就將它們互換。

過程:
這裏寫圖片描述

代碼實現_C++:

void bubbleSort(int a[], int n){  
    for(int i =0 ; i< n-1; ++i) {  
        for(int j = 0; j < n-i-1; ++j) {  
            if(a[j] > a[j+1])  
            {  
                int tmp = a[j] ; a[j] = a[j+1] ;  a[j+1] = tmp;  
            }  
        }  
    }  
}  

6. 交換排序—快速排序(Quick Sort)

基本思想:

通過一趟排序將待排記錄分割成獨立的兩部分,其中一部分記錄的關鍵字均比另一部分記錄的關鍵字小,則可繼續對這兩部分記錄分別進行排序,以達到整個序列有序。

方法步驟:

    在快速排序中首先要考慮的是如何選取序列中一個元素的關鍵字的值作爲界值(該關鍵字設爲T)。一般選取第一個元素、中間元素、終端元素的值。
    以選取序列中第一個元素的值爲界值。(設爲P(k),並將P(k)賦值給T)。快排使用兩頭向中間掃描的辦法,同時交換與基準記錄逆序的記錄。
    (1)設置兩個指針left和right,(分別爲i和j),分別從表的兩端向中間移動,將界值元素放入一個暫存單元。
    (2)將指針right對序列自右向左掃描,直到找到一個關鍵字小於界值的元素,將該元素移至left指針所指的位置。(j--,直到發現一個P(j)<T爲止,將P(j)移動到P(i)的位置)。
    (3)將指針left對序列自左向右掃描,直到找到一個關鍵字大於或等於界值的元素,將該元素移至right指針所指的位置。(i++,直到發現一個P(i)>T爲止,將P(i)移動到P(j)的位置)。
    (4)重複(2)(3)部,直到指針left和right相遇,此時,指針所在位置的左邊元素關鍵字值均小於界值,右邊所有元素關鍵字值均大於界值。將界值元素放入指針相遇的位置。(直到i=j爲止,此時將T移動到P(i)的位置上)
    (5)對產生的兩個子序列,再進行如上所述的劃分,直到所有子序列中只有一個元素爲止。排序過程結束。

一趟排序過程:

這裏寫圖片描述

排序的全過程:

這裏寫圖片描述

代碼實現_C:

    void sortint *a, int left, int right){
        if (left>=right){/*如果左邊索引大於或者等於右邊的索引就代表已經整理完成一個組了*/
            return;
            }
        int i = left ;
        int j = right;
        int key = a[left];
        while(i<j){  /*控制在當組內尋找一遍*/
        while(i < j && key <= a[j]){//使循環找到一個a[j]<key爲止
          j--; //right指針向前尋找
        }
        a[i] = a[j];   //將找到的a[j]移到a[i](left指針)的位置

        while(i<j&&key>=a[i]){ //使循環找到一個a[i]>key爲止
        i++; //left指針向後尋找
      }
      a[j] = a[i]; //將找到的a[i]移到a[j](right指針)的位置
}
      a[i] = key; /*當在當組內找完一遍以後就把中間數key迴歸*/
    sort(a, left, i - 1);/*用同樣的方式對分出來的左邊的小組進行快排*/
    sort(a, i + 1, right);/*用同樣的方式對分出來的右邊的小組進行快排*/
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章