C語言八大排序算法,一文帶你弄清所有

目錄

 

C語言八大排序

1、插入排序

2、希爾排序

3、簡單選擇排序

4、堆排序

5、冒泡排序

6、快速排序

7、歸併排序

8、基數排序


C語言八大排序

前言:感謝各位老前輩的寫的算法解析,下面的圖解也是由網上很多老前輩的圖,收下我的膝蓋。
 
代碼全部由我實測,全都是能用的,不存在不能用的。
 
如果理解不了思想,代碼中我憑着自己的理解加了一些註釋,清細看,如果還不懂,建議搜索這個排序的詳解。
 
寫着這篇博客,一方面是自己搞懂八大排序的一個過程,也希望能夠幫助到目前的你
 
如果有錯,還請指正
 
 

1、插入排序

將第一個和第二個元素排好序,然後將第3個元素插入到已經排好序的元素中,依次類推(插入排序最好的情況就是數組已經有序了)
 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;


void print(int a[], int n,int i)
{
   cout<<i <<":";
   for(int j= 0; j<8; j++)
   {
       cout<<a[j] <<" ";
   }
   cout<<endl;
}
void InsertSort(int a[], int n)
{
   for(int i= 1; i<n; i++)
   {
       if(a[i] < a[i-1])  //若第i個元素大於i-1元素,直接插入。小於的話,移動有序表後插入
       {
           int j= i-1;
           int x = a[i]; //複製爲哨兵,即存儲待排序元素
           a[i] = a[i-1]; //先後移一個元素
           while(x < a[j])  //查找在有序表的插入位置
           {
               a[j+1] = a[j];//元素後移
               j--;
               if(j==-1)//這裏要跳出,不然j=-1時a[-1]會進行判斷
                   break;
               //cout<<j<<endl;
           }
           //找到小於或等於x
           //cout<<a[j+1]<<endl;
           a[j+1] = x; //插入到正確位置
       }


       print(a,n,i); //打印每趟排序的結果
   }


}


int main()
{
   int a[8] = {3,11,5,7,2,4,9,6};
   InsertSort(a,8);
   print(a,8,8);
}

 

 

2、希爾排序

因爲插入排序每次只能操作一個元素,效率低。元素個數N,取奇數k=N/2,將下標差值爲k的數分爲一組(一組元素個數看總元素個數決定),在組內構成有序序列,再取k=k/2,將下標差值爲k的數分爲一組,構成有序序列,直到k=1,然後再進行直接插入排序
 
 
以一個整數序列爲例來說明{12,45,90,1,34,87,-3,822,23,-222,32},該組序列包含N=11個數。不少已有的說明中通常舉例10個數,這裏說明一下,排序算法與序列元素個數無關!
首先聲明一個參數:增量gap。gap初始值設置爲N/2。縮小方式一般爲gap=gap/2.
第一步,gap=N/2=5,每間隔5個元素取一個數,組成一組,一共得到5組:
 
對每組使用插入排序算法,得到每組的有序數列:
 
 
至此,數列已變爲:
 
第二步,縮小gap,gap=gap/2=2,每間隔2取一個數,組成一組,共兩組:
 
同理,分別使用插入排序法,得到每組的有序數列:
 
至此,數列已變爲:
 
第三步,進一步縮小gap,gap=gap/2=1,此時只有一組,直接使用插入排序法,玩完成排序,圖略。
 
 
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;


void ShellSort(int arr[],int N)
{
    int i,j,gap;
    for(gap = N/2;gap>0;gap/=2)
    {
        // 每組進行插入排序
        for(i=0;i<N;i++)
        {
            //直插排序,找到比它小的停止
            for(j=i-gap;j>=0;j-=gap)
            {
                if(arr[i]>arr[j])
                    break;
            }
            int temp = arr[i];//及記錄這個位置
            for(int k=i;k>j;k-=gap)//
                 arr[k] = arr[k-gap];
            arr[j+gap] = temp;
        }
        // 打印當前的gap和序列狀態
        cout<<"\ngap="<<gap;
        cout<<"\ncurrent list:";
        for(int h=0;h<N;h++)
            cout<<arr[h]<<"  ";
    }
}


int main()
{
    int a[11] = {12,45,90,1,34,87,-3,822,23,-222,32};
    cout<<"原始數列:";
    for(int i=0;i<11;i++)
        cout<<a[i]<<"  ";
    cout<<endl;


    cout<<"希爾排序:";
    ShellSort(a,11);


    system("pause");
    return 0;
}

 

 
 
 
 

3、簡單選擇排序

選出最小的數和第一個數交換,再在剩餘的數中又選擇最小的和第二個數交換,依次類推
#include<bits/stdc++.h>
using namespace std;


typedef long long ll;


int main()
{
    int a[] = {2,3,7,3,40,34,6};
    int size = sizeof(a)/sizeof(int);
    for(int i=0;i<size-1;i++)
    {
        int mn=i;
        for(int j=i+1;j<size;j++)
        {
            if(a[mn]>a[j])
                mn = j;
        }
        if(mn!=i)
            swap(a[mn],a[i]);
    }
    for(int i=0;i<size;i++)
        cout<<a[i]<<" ";
    return 0;
}

 

4、堆排序

推薦慕課上
利用大根堆的性質(堆頂元素最小)不斷輸出最大元素,直到堆中沒有元素
給定一個整形數組a[]={16,7,3,20,17,8},對其進行堆排序。
首先根據該數組元素構建一個完全二叉樹,得到
 
 
 然後需要構造初始堆,則從最後一個非葉節點開始調整對應的代碼for(i=size/2;i>=1;i--),之後i--,是之後的非葉子結點,調整過程如下:
首先因爲總共有6個結點,所以第3個結點也就是最後一個非葉子結點,就是結點值爲8開始和它的左孩子和右孩子進行比較,選出最大的在父節點。之後就是結點值爲7的和它的左右孩子進行比較,以此類推
 
這樣這個堆就建立好啦
 
然後取出最大值和最小值進行交換位置,再次把這個堆重新變成最大堆,怎樣變成最大堆呢??
此時的根節點爲最小值,和它的左孩子右孩子進行比較,取最大值交換,繼續想下一層去找左孩子和右孩子去比較,直到找不到比它小爲止。
至此,完成排序
。對於n個關鍵字序列,最壞情況下每個節點需比較log2(n)次,因此其最壞情況下時間複雜度爲nlogn。堆排序爲不穩定排序,不適合記錄較少的排序。
 
 
#include<bits/stdc++.h>
using namespace std;


void HeapAdjust(int *a,int i,int size)  //調整堆
{
    int lchild=2*i;       //i的左孩子節點序號
    int rchild=2*i+1;     //i的右孩子節點序號
    int max=i;            //臨時變量,記錄父節點
    if(i<=size/2)          //如果i是葉節點就不用進行調整
    {
        //如果左子樹大於這父節點
        if(lchild<=size&&a[lchild]>a[max])
        {
            max=lchild;
        }
        //如果右子樹大於(左子樹和父節點中比較大的值)
        if(rchild<=size&&a[rchild]>a[max])
        {
            max=rchild;
        }
        //如果右孩子的
        if(max!=i)//說明該有孩子節點會比父節點的值大
        {
            swap(a[i],a[max]);
            //繼續向下遍歷如果它的左孩子和右孩子比它大,再次交換
            HeapAdjust(a,max,size);    //避免調整之後以max爲父節點的子樹不是堆
        }
    }
}


void BuildHeap(int *a,int size)    //建立堆
{
    int i;
    for(i=size/2;i>=1;i--)    //非葉節點最大序號值爲size/2,並且向上遍歷非葉子結點
    {
        HeapAdjust(a,i,size);
    }
}


void HeapSort(int *a,int size)    //堆排序
{
    int i;
    BuildHeap(a,size);
    for(i=size;i>=1;i--)
    {
        //cout<<a[1]<<" ";
        swap(a[1],a[i]);           //交換堆頂和最後一個元素,即每次將剩餘元素中的最大者放到最後面
          HeapAdjust(a,1,i-1);      //重新調整堆頂節點成爲大頂堆
    }
}


int main(int argc, char *argv[])
{
     int a[]={0,16,20,3,11,17,8};
    //int a[100];
    int size;
    size = sizeof(a)/4-1;//szie是6,sizeof進行統計的時候,一個int按4個記
    //cout<<size<<endl;
    int i;
    HeapSort(a,size);
    for(i=1;i<=size;i++)
        cout<<a[i]<<" ";
        cout<<endl;
    return 0;
}

 

 

5、冒泡排序

 

冒泡排序應該不用多說,應該都會把
相鄰的兩個數進行比較,一趟下來最大的在最右邊
進行的是n-1趟之後,便就排序好
廢話不多說上圖
 
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
    int a[] = {4,3,2,0,7,9,2};
    int size = sizeof(a)/sizeof(int);
    for(int i=0;i<size-1;i++)
    {
        for(int j=0;j<size-i-1;j++)
        {
            if(a[j]>a[j+1])
                swap(a[j],a[j+1]);//交換
        }
    }
    for(int i=0;i<size;i++)
        cout<<a[i]<<" ";
    return 0;
}

 

 

6、快速排序

 
選擇一個基準元素,比基準元素小的放基準元素的前面,比基準元素大的放基準元素的後面,這種動作叫分區,每次分區都把一個數列分成了兩部分,每次分區都使得一個數字有序,然後將基準元素前面部分和後面部分繼續分區,一直分區直到分區的區間中只有一個元素的時候,一個元素的序列肯定是有序的嘛,所以最後一個升序的序列就完成啦。
 
實現具體:選擇第一個數爲基準,安排兩個哨兵,一個最左邊,一個最右邊, 開始最右邊的向左跑,直到遇見比它小的數停止,左邊的向右跑,直到遇到比它大的數停止,兩個哨兵停止,相互交換值,直到兩個哨兵碰到一起後,基準與碰到一起的那個位置的值進行交換,之後這個兩邊的兩個區間進行相同的操作。
詳情請見:
#include <bits/stdc++.h>
using namespace std;


void quickSort(int left, int right, int arr[])


{
    if(left >= right)
        return;
    int i, j, base, temp;
    i = left, j = right;
    base = arr[left];  //取最左邊的數爲基準數
    while (i < j)
    {
        //右邊的哨兵開始向左遍歷,直到遇見比基準值小的
        while (arr[j] >= base && i < j)
            j--;
        //左邊哨兵開始向右邊遍歷,直到遇見比基準值大的
        while (arr[i] <= base && i < j)
            i++;
        if(i < j)//兩個哨兵交換值
        {
            temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    //基準數歸位
    arr[left] = arr[i];//最左邊的數(基準值)等於停止位置的值
    arr[i] = base;//停止的位置等於基準值
    quickSort(left, i - 1, arr);//遞歸左邊
    quickSort(i + 1, right, arr);//遞歸右邊
}
int main()
{


    int a[] = {10,3,4,6,9,7,4};
    int size = sizeof(a)/4;
    quickSort(0,size-1,a);
    for(int i=0;i<size;i++)
        printf("%d ",a[i]);
    return 0;
}

 

 
 

7、歸併排序

將一個無序的數列一直一分爲二,直到分到序列中只有一個數的時候,這個序列肯定是有序的,因爲只有一個數,然後將兩個只含有一個數字的序列合併爲含有兩個數字的有序序列,這樣一直進行下去,最後就變成了一個大的有序數列
 
圖解:
 
#include <stdio.h>


void MergeArr(int* src,int * tmp,int start,int mid,int end)
{
    int i = start;//前半部分
    int j = mid + 1;//後半部分
    int k = start;
    while (i != mid + 1 && j != end + 1) //進行合併
    {
        if (src[i] < src[j])
            tmp[k++] = src[i++];
        else
            tmp[k++] = src[j++];
    }
    if (i == mid + 1)
    {
        while (j != end+1)
        tmp[k++] = src[j++];
    }
    else
    {
        while (i != mid + 1)
            tmp[k++] = src[i++];
    }
    while (start <= end)//這是把排好序的數組存回去
    {
        src[start] = tmp[start];
        start++;
    }
}


void MergeSort(int* arr, int * tmp,int start,int end)  //歸
{
    if (start < end)
    {
        int mid = (start + end) / 2;
        MergeSort(arr, tmp,start,mid);//開始分割,前半部分
        MergeSort(arr, tmp, mid+1, end);//後半部分
        MergeArr(arr, tmp, start, mid, end);//進行合併
    }
}


int main()
{
    int a[8] = { 2,4,5,9,1,6,7,8 };
    int c[8] = {0};
    MergeSort(a, c, 0, 7);
    for (int i = 0; i < 8; i++)
    {
        printf("%d\t", c[i]);
    }
    return 0;
}

 

 

8、基數排序

 
(1)假設有欲排數據序列如下所示:
73  22  93  43  55  14  28  65  39  81
首先根據個位數的數值,在遍歷數據時將它們各自分配到編號0至9的桶(個位數值與桶號一一對應)中。
分配結果(邏輯想象)如下圖所示:
分配結束後。接下來將所有桶中所盛數據按照桶號由小到大(桶中由頂至底)依次重新收集串起來,得到如下仍然無序的數據序列:
 
81  22  73  93  43  14  55  65  28  39
 
接着,再進行一次分配,這次根據十位數值來分配(原理同上),分配結果(邏輯想象)如下圖所示:
分配結束後。接下來再將所有桶中所盛的數據(原理同上)依次重新收集串接起來,得到如下的數據序列:
 
14  22  28  39  43  55  65  73  81  93
 
觀察可以看到,此時原無序數據序列已經排序完畢。如果排序的數據序列有三位數以上的數據,則重複進行以上的動作直至最高位數爲止。
#include<bits/stdc++.h>
using namespace std;


#define Max_ 10      //數組個數
#define RADIX_10 10    //整形排序
#define KEYNUM_31 10     //關鍵字個數,這裏爲整形位數


// 打印結果
void Show(int  arr[], int n)
{
    int i;
    for ( i=0; i<n; i++ )
        printf("%d  ", arr[i]);
    printf("\n");
}


// 找到num的從低到高的第pos位的數據
int GetNumInPos(int num,int pos)
{
    int temp = 1;
    for (int i = 0; i < pos - 1; i++)
        temp *= 10;


    return (num / temp) % 10;
}




//基數排序  pDataArray 無序數組;iDataNum爲無序數據個數
void RadixSort(int* pDataArray, int iDataNum)
{
    int *radixArrays[RADIX_10];    //分別爲0~9的序列空間
    for (int i = 0; i < 10; i++)
    {
        radixArrays[i] = (int *)malloc(sizeof(int) * (iDataNum + 1));
        radixArrays[i][0] = 0;    //index爲0處記錄這組數據的個數
    }


    for (int pos = 1; pos <= KEYNUM_31; pos++)    //從個位開始到10位
    {
        for (int i = 0; i < iDataNum; i++)    //分配過程
        {
            int num = GetNumInPos(pDataArray[i], pos);//獲取該對應位置上的值
            int index = ++radixArrays[num][0];//所儲存在數組中的位置
            radixArrays[num][index] = pDataArray[i];//放進筒裏
        }


        for (int i = 0, j =0; i < RADIX_10; i++)    //收集
        {
            for (int k = 1; k <= radixArrays[i][0]; k++)
                pDataArray[j++] = radixArrays[i][k];//把筒裏的值倒回給原來的數組
            radixArrays[i][0] = 0;    //復位
        }
    }
}


int main()
{   //測試數據
    int arr_test[Max_] = { 8, 4, 2, 3, 5, 1, 6, 9, 0, 7 };
    //排序前數組序列
    Show( arr_test, Max_ );
    RadixSort( arr_test, Max_);
    //排序後數組序列
    Show( arr_test, Max_ );
    return 0;
}

 

 
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章