排序算法(選擇、冒泡、插入、快速、希爾、歸併、堆排序)

1、選擇排序

算法原理

  • 比較未排序區域的元素,每次選出最大或最小的元素放到排序區域。
  • 一趟比較完成以後,再從剩下未排序的元素開始比較。
  • 反覆執行以上步驟,只到排序完成。

 

時間複雜度

圖示

 

 

 

代碼:ios

void quick_pow(int n,int a[])
{
    for(int i=0;i<n;i++)
    {
        int pos=i;
        for(int j=i+1;j<n;j++)
        {
            if(a[j]<a[pos])
                pos=j;
        }
        int temp=a[i];
        a[i]=a[pos];
        a[pos]=temp;
    }
}

 

 

2、冒泡排序

算法原理

  1. 比較 相鄰的元素。若是第一個比第二個大,就交換他們兩個。
  2. 對每一對相鄰元素作一樣的工做,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
  3. 針對全部的元素重複以上的步驟,除了最後一個。
  4. 持續每次對愈來愈少的元素重複上面的步驟,直到沒有任何一對數字須要比較。

這個算法的名字由來是由於越大的元素會經由交換慢慢「浮」到數列的頂端(升序或降序排列),就如同碳酸飲料中二氧化碳的氣泡最終會上浮到頂端同樣,故名「冒泡排序」。算法

 

時間複雜度

圖示

 

代碼

void quick_pow(int n,int a[])
{
    for(int i=0;i<n-1;i++)//n個元素進行n-1次排序
    {
        for(int j=0;j<n-i-1;j++)//每趟排序比較的次數
        {
            if(a[j]>a[j+1])
            {
                int temp=a[j];
                a[j]=a[j+1];
                a[j+1]=temp;
            }
        }
    }
}

 

3、插入排序

算法原理

插入排序的基本操做就是將一個數據插入到已經排好序的有序數據中,從而獲得一個新的、個數加一的有序數據,算法適用於少許數據的排序數組

 

時間複雜度

圖示

代碼

void quick_pow(int n,int a[])
{
    for(int i=1;i<n;i++)//從第二個元素開始插入,[0,i-1]都是有序數據
    {
        int temp=a[i];//要插入的元素
        int j;
        for(j=i-1;j>=0&&temp<a[j];j--)
            a[j+1]=a[j];//交換位置
        a[j+1]=temp;
    }
}

 

 

4、快速排序

算法原理

快速排序的本質就是把基準數大的都放在基準數的右邊,把比基準數小的放在基準數的左邊,這樣就找到了基準在數組中的正確位置.
之後採用遞歸的方式分別對前半部分和後半部分排序,當前半部分和後半部分均有序時該數組就天然有序了。優化

 

時間複雜度

 

圖示

 

代碼

int find(int le,int ri,int a[])//找基準元素位置
{
    int base=a[le];//基準元素
    while(le<ri)
    {
        while(le<ri&&a[ri]>=base)//從序列右端開始處理,大於基準的不變
            ri--;
        a[le]=a[ri];//小於基準的交換到左邊

        while(le<ri&&a[le]<=base)//處理左端,小於基準的不變
            le++;
        a[ri]=a[le];//大於基準的交換到右邊
    }
    //當左邊的元素都小於base,右邊的元素都大於base時,此時base就是基準元素,le或ri就是基準元素的位置
    a[le]=base;
    return le;
}
void quick_pow(int le,int ri,int a[])
{
    if(le>=ri)
        return;
    int pos=find(le,ri,a);
    quick_pow(le,pos-1,a);
    quick_pow(pos+1,ri,a);
}

①先從隊尾開始向前掃描且當low < high時,若是a[high] > tmp,則high–,但若是a[high] < tmp,則將high的值賦值給low,即arr[low] = a[high],同時要轉換數組掃描的方式,即須要從隊首開始向隊尾進行掃描了
②同理,當從隊首開始向隊尾進行掃描時,若是a[low] < tmp,則low++,但若是a[low] > tmp了,則就須要將low位置的值賦值給high位置,即arr[low] = arr[high],同時將數組掃描方式換爲由隊尾向隊首進行掃描.
③不斷重複①和②,知道low>=high時(實際上是low=high),low或high的位置就是該基準數據在數組中的正確索引位置.

ui

5、希爾排序(優化後的插入排序)

算法原理

希爾排序是記錄按下標的必定增量分組,對每組使用直接插入排序算法排序;隨着增量逐漸減小,每組包含的關鍵詞愈來愈多,當增量減至1時,整個文件恰被分紅一組,算法便終止。spa

 

時間複雜度

 

圖示

 

 

 

 

 

代碼

void quick_pow(int n,int a[])
{
    for(int k=n/2;k>0;k=k/2)//k是分組距離
    {
        for(int i=k;i<n;i++)//能夠分紅n-k組,輪流對每一個分組進行插入排序
        {
            int temp=a[i];//
            int j;
            for(j=i-k;j>=0&&a[j]>temp;j=j-k)
                a[j+k]=a[j];//前面的元素大於右邊,交換位置,
            a[j+k]=temp;
        }
    }
}

 

6、歸併排序

算法原理

歸併排序的核心思想是將兩個有序的數列合併成一個大的有序的序列。經過遞歸,層層合併,即爲歸併。3d

 

時間複雜度

圖示

先把總體二分到一,在合併成有序總體code

 

代碼

void merge(int le,int mid,int ri,int a[],int temp[])//將數組a的左右兩部分合並
{
    int i=le,j=mid+1;
    int m=mid,n=ri;
    int k=0;
    while(i<=m&&j<=n)//a數組兩端更小的先加進temp數組
    {
        if(a[i]<a[j])
            temp[k++]=a[i++];
        else
            temp[k++]=a[j++];
    }
    //把a數組剩下的部分加入temp數組
    while(i<=m)
        temp[k++]=a[i++];
    while(j<=n)
        temp[k++]=a[j++];

    for(int i=0;i<k;i++)//把有序的temp數組放回a數組
        a[le+i]=temp[i];
}

void quick_pow(int le,int ri,int a[],int temp[])
{
    if(le>=ri)
        return ;
    int mid=(le+ri)/2;
    quick_pow(le,mid,a,temp);//使左邊有序
    quick_pow(mid+1,ri,a,temp);//右邊有序
    merge(le,mid,ri,a,temp);//左右有序的部分合並
}

 

7、堆排序

算法原理

堆排序能夠說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:blog

  • 大頂堆:每一個節點的值都大於或等於其子節點的值,在堆排序算法中用於升序排列;排序

  • 小頂堆:每一個節點的值都小於或等於其子節點的值,在堆排序算法中用於降序排列;

圖示

  1. 建立一個堆 H[0……n-1],按照大頂堆的性質從左往右建立;

  2. 建立完成以後,先把堆首元素(最大值)拿走,再把堆尾元素(同一層按從右往左順序)放到堆首位置;

  3. 堆尾元素放到堆首以後,判斷是否比左右節點元素大,交換使堆頂元素大於左右節點;

  4. 重複步驟 2,直到堆的尺寸爲 1。

 


代碼

#include<iostream>
#include<algorithm>
#include<math.h>
#include<string.h>
#define ll long long
#define M 0x3f3f3f3f3f
using namespace std;

void quick_pow(int a[],int pos,int len)
{
    int root=a[pos];//頂點
    int le=pos*2+1;//由於pos使從0開始,因此是左孩子
    while(le<len)
    {
        int ri=le+1;
        if(ri<len&&a[ri]>a[le])//使左孩子大於右孩子
            le=ri;
        if(root<a[le])//使頂點大於左孩子
        {
            a[pos]=a[le];
            pos=le;
            le=pos*2+1;
        }
        else
            break;
    }
    a[pos]=root;
}
void init(int n,int a[])
{
    for(int i=n/2-1;i>=0;i--)//將數組a初始化爲大頂堆
        quick_pow(a,i,n);

    for(int i=n-1;i>=0;i--)//將堆頂元素和堆尾元素互換,並維護大頂堆的性質
    {
        int temp=a[0];//堆頂元素
        a[0]=a[i];
        a[i]=temp;
        quick_pow(a,0,i);
    }
}
int main()
{
    int n;
    int a[105],temp[105];
    cin>>n;
    for(int i=0;i<n;i++)
        cin>>a[i];
    init(n,a);
    for(int i=0;i<n;i++)
        cout<<a[i]<<' ';
    cout<<endl;
    return 0;
}

 




 

 

 

 

相關文章
相關標籤/搜索