1. 程式人生 > >選擇排序(簡單選擇排序--改進的簡單選擇排序--堆排序)

選擇排序(簡單選擇排序--改進的簡單選擇排序--堆排序)

選擇排序:每一趟從待排序序列中選一個按序所需要的記錄,放在已排好序的子序列之前或之後,保持有序,這樣,有序子序列每次增加一個記錄,直到全部記錄排完為止。

1、簡單選擇排序

基本思想:每一趟從待排記錄序列中選具有最小關鍵字值的記錄,順序放在已排好序的子序列後,直到全部記錄排完。步驟:(1)在第i(i=1,2,3,......,n-1)次挑選步驟中,在記錄r[i],r[i+1],r[n]中挑出最小值r(2)交換r和r[i]。執行完第i步後,前i個記錄r[1],r[2],......,r[i]有序。示例:初始序列:{49 27 65 97 76 12 38}
  第1趟:12與49交換:12{27 65 97 76 49 38
}
  第2趟:27不動 :12 27{65 97 76 49 38}
  第3趟:65與38交換:12 27 38{97 76 49 65}
  第4趟:97與49交換:12 27 38 49{76 97 65}
  第5趟:76與65交換:12 27 38 49 65{97 76}
  第6趟:97與76交換:12 27 38 49 65 76 97 完成
演算法分析:(1)n個記錄的序列只需n-1趟排序。按照演算法的描述,n - 1 趟排序之後陣列中的前 n - 1 個元素已經處於相應的位置,第 n 個元素也處於相應的位置上。(2)穩定性:不穩定 (比如序列【5, 5, 3】第一趟就將第一個[5]與[3]交換,導致第一個5挪動到第二個5後面)
(3)時間複雜性:比較時間:T = (n-1))+ (n -2)+(n - 3).... + 1;  ===>>  T =  [n*(n-1) ] / 2; 交換時間:最好的情況全部元素已經有序,則 交換次數為0;最差的情況,全部元素逆序,就要交換 n-1 次;所以最優的時間複雜度  和最差的時間複雜度   和平均時間複雜度  都為 :O(n^2) (4)空間複雜度:只需一個輔助空間作為交換機路用的暫存單元,O(1)程式碼:
#include <iostream>
#include <vector>
using namespace std;

void StraightSelectSort(int r[],int n)
{//對r[1]~r[n]排序
    for(int i=1;i<n;i++)//n-1趟排序
    {
        int k=i;
        for(int j=i+1;j<=n;j++)
        {
            if(r[j]<r[k]) k=j;//k記錄r[i...n]中最小值的下標
        }
        if(k!=i)
        {
            int temp=r[i];
            r[i]=r[k];
            r[k]=temp;
        }
        cout<<endl;
        for(int p=1;p<=n;p++) cout<<r[p]<<' ';
    }
}

int main()
{
    int r[]={0,49,38,65,49,76,13,27,97};
    int length=sizeof(r)/sizeof(int);
    cout<<"原陣列:"<<endl;
    for(int i=1;i<=length-1;i++)
    {
        cout<<r[i]<<' ';
    }
    cout<<endl<<"排序過程:"<<endl;
    StraightSelectSort(r,length-1);
    return 0;
}

2、改進的簡單選擇排序

由於簡單選擇排序不穩定,改進,即將要交換的兩個元素a、b不立即直接交換,而是先取出後面的元素b,將後面元素b之前的元素直到前一個元素a向後平移一個位置,再將取出的元素賦在前面空出的位置上,就可形成穩定排序。

程式碼:

#include <iostream>
#include <vector>
using namespace std;

void StraightSelectSort1(int r[],int n)
{//對r[1]~r[n]排序
    for(int i=1;i<n;i++)//進行n-1趟排序
    {
        int k=i;
        for(int j=i+1;j<=n;j++)
        {
            if(r[j]<r[k]) k=j;//k記錄r[i...n]中最小值的下標
        }
        if(k!=i)
        {
            int temp=r[k];//記錄下找到的最小值
            for(int q=k-1;q>=i;q--) r[q+1]=r[q];//後移
            r[i]=temp;
        }
        cout<<endl;
        for(int p=1;p<=n;p++) cout<<r[p]<<' ';
    }
}
int main()
{
    int r[]={0,49,38,65,49,76,13,27,97};
    int length=sizeof(r)/sizeof(int);
    cout<<"原陣列:"<<endl;
    for(int i=1;i<=length-1;i++)
    {
        cout<<r[i]<<' ';
    }
    cout<<endl<<"排序過程:"<<endl;
    StraightSelectSort1(r,length-1);
    return 0;
}

3、堆排序

1、堆:n個元素的序列k1,k2,k3,......,kn,當且僅當:ki<=k(2i)且ki<=k(2i+1),或者ki>=k(2i)且ki>=k(2i+1),i=1,2,...,n/2,稱之為堆。滿足前一組關係為最小堆(小根堆),後一組最大堆(大根堆)。                 
將待排序的記錄序列即一維結構陣列邏輯上看作一個完全二叉樹上節點自頂向下自左自右的排列。若{r1,r2,...,rn}是最大堆,則堆頂記錄對應最大值,在最小堆中,堆頂最小。2、堆排序思想:首先將n個記錄按關鍵字大小建成最大堆,堆頂就是最大的,將堆頂與最後一個交換,最大值排在最後。然後對前面n-1個記錄只需從根開始調整成堆,堆頂又成n-1箇中的最大值,與這n-1個的最後一個交換,如此反覆,即可得到有序序列。  利用大根堆進行排序演算法步驟: (1)初始建堆。將n個待排序記錄儲存的一維陣列看作一個完全二叉樹上節點自頂向下自左自右的排列,最後一個非葉結點是第n/2個,從第n/2個節點開始,依次將第n/2個,第n/2-1個節點,......,第2個,第1 個節點,作為當前節點,按照大根堆定義逐一檢查以其為根的子樹是否構成大根堆。最大者在堆頂。 (2)移走堆頂,將它與待排序中最後一個交換,將剩餘記錄再調整成堆。這樣找到次大關鍵字。 (3)重複(2),直到堆中只有一個記錄為止(n-1趟排序)。 順序表中存放的就是從小到大的有序排列。3、篩選法建堆:由於其左右孩子作為根的子樹均為堆,只需檢查該節點:取其左右孩子中較大者與其進行比較:(1)若較大者不大於臨時結構變數中關鍵字,則臨時結構變數賦給較大者雙親,演算法調整結束(2)若較大者大於臨時結構變數中關鍵字,則將較大者上移,再取上移者原左右孩子中較大者,重複(1)(2),或較大者再無孩子,則臨時結構變數賦給較大者原所在位置,演算法調整結束           
          
                               
             4、演算法分析:

堆排序的時間複雜度,主要在初始化堆過程和每次選取最大數後重新建堆的過程; 初始化建堆過程時間:O(n);堆排序的時間複雜度為:O(nlogn)

堆排序不穩定。

程式碼:
#include <iostream>
#include <vector>
using namespace std;
////大根堆調整,inout[top...n-1]
void adjustHeap(vector<int> &input,int top,int n){
    //n為堆中元素總數
    int j=2*top+1;//根節點i的左孩子
    int temp=input[top];
    while(j<n){
        if(j<n-1&&input[j]<input[j+1])
            j++;//使j指向左右孩子中較大者
        if(input[j]<=temp)
            break;
        else{//根節點中元素與左右孩子節點中較大者交換:input[j]大時向上提升
            input[top]=input[j];
            top=j;
            j=2*top+1;
        }
    }
    input[top]=temp;
}
//堆排序,從小到大,n為堆中元素總數
void heapSort(vector<int> &input,int n){
    for(int i=n/2-1;i>=0;i--)//初始化堆
        adjustHeap(input,i,n);
    cout<<"調整後的大根堆:"<<endl;
    for(int i=0;i<=input.size()-1;i++) cout<<input[i]<<' ';
    cout<<endl;
    for(int i=n-1;i>0;i--){
        int temp=input[0];
        input[0]=input[i];
        input[i]=temp;

        cout<<"第"<<input.size()-i<<"趟排序後,"<<endl;
        for(int i=0;i<=input.size()-1;i++)
            cout<<input[i]<<' ';
        cout<<endl;

        adjustHeap(input,0,i-1);
        cout<<"第"<<input.size()-i<<"次排序(重新調整後),"<<"前"<<i<<"個為堆中元素"<<endl;
        for(int i=0;i<=input.size()-1;i++)
            cout<<input[i]<<' ';
        cout<<endl;
    }
}
int main()
{
    vector<int> input;
    input.push_back(49);
    input.push_back(38);
    input.push_back(65);
    input.push_back(97);
    input.push_back(76);
    input.push_back(13);
    input.push_back(27);
    input.push_back(49);
    int length=input.size();
    cout<<"原陣列:"<<endl;
    for(int i=0;i<=length-1;i++)
    {
        cout<<input[i]<<' ';
    }
    cout<<endl;
    heapSort(input,length);
    return 0;
}