八種排序方法


一.直接(選擇)插入排序

有兩種方式:升序和降序 我使用升序

直接(簡單)插入排序:

每次向已經排序好的隊列里面找個合適的位置,將值插入

  

//筆試和面試:

//1.算法的描述 2.算法的實現 3.效率(時間復雜度和空間復雜度和穩定性)

//穩定性定義:如果兩個關鍵值A和A`,如果一開始A就在A`前面,你排序后A還在A`前面,我們就認為

是穩定的

//怎么看穩定性:看有沒有跳躍交換

直接插入排序:

如果數組基本有序,我們就用直接插入排序,越有序,時間復雜度越小,極端情況下為O(n)

時間復雜度O(n^2) 空間復雜度O(1) ,穩定的

為什么不用從前向后找:如果數組有序,則時間復雜度太大

具體代碼實現:

#include <stdio.h>
#include <assert.h>

void InsertSort(int arr[], int len)
{
    //循環多少次 個數-1
    //用臨時量tmp保存關鍵值,從后向前找,比它小的或者走到了頭,就將關鍵值放到下一個位置上
    assert(arr != NULL);
    if (NULL == arr)
        return;
    int count = 0;

    int tmp = 0;
    int j = 0;
    for (int i = 1; i < len; i++)//控制揭牌后需要排序的次數
    {
        tmp = arr[i];

        for (j = i - 1; j >= 0; j--)//從后向前找
        {
            if (arr[j] > tmp)//比關鍵值大,則向后移動
            {
                arr[j + 1] = arr[j];
                count++;
            }
            else
            {
                break;//找到了比它小的值 退出
            }
        }
        arr[j + 1] = tmp;
    }

    printf("count %d\n", count);
}


void Show(int* arr, int len)
{
    assert(arr != NULL);
    if (NULL == arr)
        return;


    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main()
{
    int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 };
    InsertSort(arr, sizeof(arr) / sizeof(arr[0]));
    Show(arr, sizeof(arr) / sizeof(arr[0]));
    return 0;
}

希爾shell排序:

就是一種特殊的直接插入排序,只不過調用了很多次直接插入排序,

按增量分組

要求:增量最后一個必須為1, 增量保持互素

時間復雜度O(n^1.3~1.5),空間復雜度O(1) ,穩定性:發生了跳躍交換,所以不穩定

例如:
分成5組,處理之后的值:
分成3組,處理之后的值: 
最后分成1組,處理之后的值:

具體代碼實現:

#include <stdio.h>
#include <assert.h>


static void Shell(int arr[], int len, int gap)//gap 分成多少組(間隔)
{
    int tmp = 0;
    int j = 0;
    int count = 0;
    for (int i = gap; i < len; i++)//i開始的位置
    {
        tmp = arr[i];
        for (j = i - gap; j >= 0; j = j - gap)
        {
            if (arr[j] > tmp)
            {
                arr[j + gap] = arr[j];
                count++;
            }
            else
            {
                break;
            }
        }
        arr[j + gap] = tmp;
    }
    printf("%d count %d\n", gap, count);
}

void ShellSort(int arr[], int len)
{
    assert(arr != nullptr);
    if (NULL == arr)
        return;
    int dk[] = { 5, 3, 1 };
    for (int i = 0; i < sizeof(dk) / sizeof(dk[0]); i++)
    {
        Shell(arr, len, dk[i]);
    }
}
void Show(int* arr, int len)
{
    assert(arr != NULL);
    if (NULL == arr)
        return;


    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}


int main()
{
    int arr2[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 };
    ShellSort(arr2, sizeof(arr2) / sizeof(arr2[0]));
    Show(arr2, sizeof(arr2) / sizeof(arr2[0]));
    return 0;
}

二.交換排序

冒泡(沉石)排序:

兩兩比較,大的向后挪,小的向前挪

時間復雜度O(n^2) 空間復雜度O(1) 穩定的

具體代碼實現:

//冒泡排序:兩兩比較, 大的向后挪
void BubbleSort(int arr[], int len)
{
    //assert
    /*int count=0;
    bool tag = true;*/
    int tmp = 0;
    for(int i=0; i<len-1; i++)//次數  
    {
        //tag = true;
        for(int j=0;j+1<len-i; j++)//兩兩比較,大的向后挪
        {
            if(arr[j] > arr[j+1])
            {
                tmp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = tmp;
                //tag = false;
            }
            //count++;
        }

        /*if(tag)
        {
            break;
        }*/
    }
    //printf("count = %d\n", count);
}
int main()
{
    int arr[] = {2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88}; BubbleSort(arr, sizeof(arr)/sizeof(arr[0])); Show(arr, sizeof(arr)/sizeof(arr[0])); return 0; }

快速排序法

每次找到基准值,以基准值為分界線,將數據分成兩塊,左邊的數據都比基准值小,右邊的數據都比基准值大快速排序,越有序越慢,

規則:
1.從右向左找比基准值小的數據,找到后,向左挪動
2.從左向右找比基准值大的數據,找到后,向右挪動
3.重復1,2,直到left == right,結束,將基准值放到arr[left] 或者arr[right]內
 
缺點:越有序越慢,不穩定

 

具體實現代碼:

#include<stdio.h>
#include<assert.h>
static int Partition(int arr[], int left, int right) 
{
    int tmp = arr[left]; 
    while (left < right)//進來保證有兩個值 
    { 
        while(left < right && arr[right] > tmp) 
            right--; 
        if(left == right) 
        { 
            break; 
        }
        arr[left] = arr[right]; 

        while(left < right && arr[left] <= tmp) 
            left++; 
        if(left == right) 
        { 
            break;
        }
        arr[right] = arr[left]; 
    }
    arr[left] = tmp;//arr[right] = tmp;

    return left;//return right; 因為此時left == right 
}

static void Quick(int arr[], int left, int right)
{ 
    if(left < right) 
    { 
        //第一種優化:如果有效個數特別少,直接調用直接插入排序 
        /*
         if(right-left+1<20 ) //自己設置有效個數
        {  
            Insertsort(arr,left, high) 
            return;
        } // Insertsirt表示一個插入排序類
        */

        //第二種優化:三數取中 
        /*
        GetMiddleNumber(arr,left,mid,right);
        */

        //第三種優化:防止完全有序,自己打亂一下 
       /*
        Swap(arr,start,rand()%(right-left+1)+start;
       */

       //第四種
       /*if(left < right) 
       {
        int midindex = Partition(arr, left, right); 
        if(left < midindex-1) 
        { 
            Quick(arr, left, midindex-1);
        }
        if(midindex+1 < right) 
        {
            Quick(arr, midindex+1, right);
        }
    }*/
    
        int midindex = Partition(arr, left, right); 
        Quick(arr, left, midindex-1); 
        Quick(arr, midindex+1, right); 
    } 
}

//用棧
static void Quick_stack(int arr[], int left, int right)
{
    stack<int> st;
    if (left < right)
    {
        int midindex = Partition(arr, left, right);
        if (left < midindex - 1)
        {
            st.push(left);
            st.push(midindex - 1);
        }
        if (midindex + 1 < right)
        {
            st.push(midindex + 1);
            st.push(right);
        }
    }

    while (!st.empty())
    {
        int q = st.top();
        st.pop();
        int p = st.top();
        st.pop();
        int midindex = Partition(arr, p, q);
        if (p < midindex - 1)
        {
            st.push(p);
            st.push(midindex - 1);
        }
        if (midindex + 1 < q)
        {
            st.push(midindex + 1);
            st.push(q);
        }
    }
}

void QuickSort(int arr[], int len)//時間復雜度O(nlogn) 空間復雜度O(1) 不穩定 
{ 
    //assert 
    Quick_stack(arr, 0, len-1); 
}

第一種優化代碼:

void InsertSort(int arr[], int left, int right)
{
    int tmp = arr[left];
    for (int i = left + 1; i <= right; i++)
    {
        tmp = arr[i];
        int j = i - 1;
        while (j >= right && arr[i] > tmp)
        {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = tmp;
    }
}

第二種優化代碼:

void GetMiddleNumber(int arr[], int left, int right)
{
    if (arr[left] > arr[right])
    {
        Swap(arr, left, right);//交換左右端數據,保證左端較小
    }
    if (arr[mid] > arr[right])
    {
        Swap(arr, left, right);//交換中間和右邊,保證中間較小
    }
    if (arr[mid] > arr[left])
    {
        Swap(arr, left, right);//交換中間和左端數據,保證左邊不是最小的那一個
    }
}

第三種優化代碼:

Swap(arr, left, rand() % (end - start + 1) + start);//取一個小於有效長度隨機值+最左端值的下標作為隨機基准值的下標與start進行交換

三.選擇排序

直接選擇(簡單選擇排序):

每次從待排序隊列中找到最小值,和待排序隊列的第一位交換即可
時間復雜度O(n^2) 空間復雜度O(1) 不穩定的 

 具體實現代碼:

#include<stdio.h>
#include<assert.h>
void SelectSort(int arr[], int len)
{
    assert(arr != NULL);
    if (NULL == NULL)return;
    int tmp = 0;
    for (int i = 0; i < len - 1; i++)
    {
        int m= i;//存放最小值下標
        for (int j = i + 1; j < len ; j++)
        {
            if (arr[j] <arr[m])
            {
                m = j;
            }
        }
        if (m != i)//if判斷可省略
        {
            tmp = arr[m];
            arr[m] = arr[i];
            arr[i] = tmp;
        }
    }
}
void Show(int* arr, int len)
{
    assert(arr != NULL);
    if (NULL == arr)
        return;
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main()
{
    int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 };
    SelectSort(arr, sizeof(arr) / sizeof(arr[0]));
    Show(arr, sizeof(arr) / sizeof(arr[0]));
    return 0; 
}

堆排序:

堆排序的時間復雜度O(nlogn) 空間復雜度O(1) 不穩定

什么是堆?
堆分為兩種:大頂堆和小頂堆 兩個統稱為堆
大頂堆:一個二叉樹,父節點的值大於子節點的值
小頂堆:一個二叉樹,父節點的值小於子節點的值
什么是樹形結構:
二叉樹,樹根,深度,葉子結點,左孩子,右孩子,完全二叉樹,滿二叉樹
深度怎么求:log2n+1
大頂堆和小頂堆的關系,和兄弟節點的值無關,只和父子節
點有關
調整2個要點:1.從最后一個非葉子節點子樹開始從后向前調整
                        2.調整的時候順序是從上向下
                        3.升序(大頂堆),降序(小頂堆)

  

 具體實現代碼:

#include<stdio.h>
#include<assert.h>
static void HeapAdjust(int arr[], int start, int end)//時間復雜度O(log2n) 空間 復雜度O(1) 
{ 
    int tmp = arr[start]; 
    
    for(int i=2*start+1; i<=end; i=i*2+1)//i? 堆排序效率高體現在這里i=i*2+1 
    { 
        //1.左右孩子都存在 
        //2.只有左孩子,沒有右孩子 
        
        if(i<end && arr[i] < arr[i+1])//通過i<end保證右孩子存在,且arr[i] <arr[i+1]保證左孩子小於右孩子 
        { 
            i++;//這時候讓i指向較大的右孩子下標 
        }
        //if判斷失敗的話,要么沒有右孩子,要么有右孩子但是左孩子比右孩子值大,所以i不需要 改變 
        if(arr[i] > tmp)//判斷較大孩子節點的值是否比父節點的值大,大的話向上覆蓋,不然 就找到了合適位置 
        { 
            arr[start] = arr[i]; 
            start = i; 
        }
        else 
        { 
            break;//左右孩子中較大的孩子值小於tmp 
        } 
    }
    arr[start] = tmp;//有兩種情況執行到這一行代碼:1.觸底 2.找到放tmp的合適位置 
}

//堆排序的時間復雜度O(nlog2n) 空間復雜度O(1) 不穩定 
void HeapSort(int arr[], int len) 
{ 
    //assert 
    //2.調整為大頂堆 
    for(int i=(len-1-1)/2; i>=0; i--)//O(nlog2n) 
    { 
        HeapAdjust(arr, i, len-1);// 
    }
    
    //第一個for循環走出來,這時已經為大頂堆了 
    int tmp = 0; 
    for(int j=0; j<len-1; j++)//j指的是循環的次數(頂部數據和最后一個節點交換的次 數)//O(nlog2n) 
    {
        //3.將頂部數據和最后一個節點進行了交換 
        tmp = arr[0]; 
        arr[0] = arr[len-1-j]; 
        arr[len-1-j] = tmp;//已經將頂部數據和最后一個節點進行了交換 //4.重復2.3操作 
        HeapAdjust(arr, 0, (len-1-j)-1); 
    } 
}

void Show(int* arr, int len)
{
    assert(arr != NULL);
    if (NULL == arr)
        return;
    for (int i = 0; i < len; i++)
    {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main()
{
    int arr[] = { 2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88 };
    HeapSort(arr, sizeof(arr) / sizeof(arr[0]));
    Show(arr, sizeof(arr) / sizeof(arr[0]));
    return 0;
}

四.二路歸並排序

二路歸並排序,非遞歸形式:

將兩個有序的段合並成一個有序的段,直到全部數據在同一個段內有序,則完成有序

時間復雜度O(n log2n) 空間復雜度O(1) 穩定的

 

 

具體代碼實現:

//一次歸並 以gapgap合並
static void Merge(int arr[], int len, int gap)//gap 標志幾幾合並
{
    int *brr = (int*)malloc(sizeof(int) * len);
    assert(brr != NULL);
    int low1 = 0;
    int high1 = low1 + gap -1;
    int low2 = high1 + 1;
    int high2 = low2+gap-1<len ? low2+gap-1 : len-1;//H2 有可能越界     若小於則low2+gap-1,不是則len-1
    int i = 0;


    while(low2 < len)//有兩個有序段 
    {
        while(low1 <= high1 && low2 <= high2)//兩個段內頭指針都沒走到尾巴
        {
            if(arr[low1] <= arr[low2])
            {
                brr[i++] = arr[low1++];
            }
            else
            {
                brr[i++] = arr[low2++];
            }
        }
       //左邊的段走到尾,那直接將右邊的段內數據向下拷貝到brr內即可
        while(low2 <= high2)
        {
            brr[i++] = arr[low2++];
        }
       //右邊的段走到尾,那直接將左邊的段內數據向下拷貝到brr內即可
        while(low1 <= high1)
        {
            brr[i++] = arr[low1++];
        }
       
        //更改L1L2 H1H1,讓指向接下來的兩個有序段即可 
        low1 = high2 + 1;
        high1 = low1+gap-1;
        low2 = high1 + 1;
        high2 = low2+gap-1<len ? low2+gap-1 : len-1;
    }
    //只有一個有序段
    while(low1 < len)
    {
        brr[i++] = arr[low1++]; 
    }

    //將brr里的全部值拷貝到arr里面,然后將brr釋放
    for(int j=0; j<len; j++)
    {
        arr[j] = brr[j];
    }
    free(brr);
    brr = NULL;
}

void MergeSort(int arr[], int len)//控制合並次數
{
    assert(arr != NULL);
    if(NULL == arr)
        return;

    for(int i=1; i<len; i*=2)
    {
        Merge(arr, len, i);
    }
}
int main()
{
    int arr[] = {2,4,6,8,23,98,76,56,74,36,1,3,5,7,99,66,77,88};
    MergeSort(arr, sizeof(arr)/sizeof(arr[0]));
    Show(arr, sizeof(arr)/sizeof(arr[0]));
    return 0;
}

五.基數排序

 又稱桶排序

低位優先,所有數據從低位(個)位開始,依次放入到對應的十個桶內(隊列中),再依次從桶中將數據依次取出(出隊),直到所有數據全部有序。

循環次數和最大位數有關

時間復雜度o(n) , 空間復雜度o(n)

 

 

 

 

 此時完全有序

具體實現代碼:

#include<stdio.h>
#include<assert.h>
//基數排序
//獲取數組中最大值的位數 
static int Get_Figure(int arr[], int len) 
{ 
    int max = 0; 
    for(int i=0; i<len; i++) 
    { 
        if(arr[i] > max) 
        { 
            max = arr[i]; 
        }
    }
    int count = 0; 
    while(max != 0) 
    { 
        count++; 
        max /= 10;
    }
    return count; 
}
//獲取n的第fin位的值 
//1234,2 = 2 
//234,0 = 4 
//12345,4 = 1 
//12345,7 = 0 
static int Get_num(int n, int fin) 
{ 
    for(int i=0; i<fin; i++) 
    { 
        n = n / 10;
    }
    return n % 10; 
    
    //return n/(int)(pow((double)10, fin)) % 10; 
}

//用二維數組調用
static void Radix(int arr[], int len, int fin)
//二維數組 fin判斷的依據,到底是 以什么位排序//時間復雜度O(n) 空間復雜度O(n) 

{ 
    int bucket[10][20] = {0};//
    int num[10] = {0};//對應的桶中有多少個有效值 
    
    //所有的數據都以fin位為依據,放到了桶內 
    for(int i=0; i<len; i++)//數組的下標 從0開始放 
    { 
        int index = Get_num(arr[i], fin);//獲取arr[i]的fin位的值,找到對應的桶 
        bucket[index][num[index]] = arr[i];//放到對用的桶中第num[index]位上 
        num[index]++;//對應的桶中有效個數++
    }
    
    //從0-9桶內依次取值到arr里 
    int k = 0; 
    for(int i=0; i<10; i++)//幾號桶 
    { 
        for(int j=0; j<num[i]; j++)//j桶中有效值個數 
        { 
            arr[k++] = bucket[i][j];
        } 
    } 
}

//用鏈式隊列調用 
static void Radix_queue(int arr[], int len, int fin) 
//時間復雜度O(n) 空間復 雜度O(n) 
{ 
    LQueue queArr[10]; 
    for(int i=0; i<10; i++) 
    { 
        InitLQueue(&queArr[i]); 
    }
    for(int i=0; i<len; i++)
    {
        int index = Get_num(arr[i], fin);
        Push(&queArr[index], arr[i]); 
    }
    int k = 0; 
    for(int i=0; i<10; i++) 
    { 
        while(!IsEmpty(&queArr[i])) 
        { 
            Pop(&queArr[i], &arr[k++]); 
        }
    }
    for(int i=0; i<10; i++) 
    { 
        Destroy(&queArr[i]); 
    } 
}
void RadixSort(int arr[], int len)//時間復雜度O(dn) 空間復雜度(n)穩定
{
    //assert
    int count = Get_Figure(arr, len); 
    for(int i=0; i<count; i++)//循環的趟數,低位優先 
    { 
        Radix_queue(arr, len, i); 
    }
}

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM