找回密碼
 立即注冊

QQ登錄

只需一步,快速開始

搜索
查看: 3693|回復(fù): 0
收起左側(cè)

C語言排序算法詳解

[復(fù)制鏈接]
ID:399983 發(fā)表于 2018-10-11 14:57 | 顯示全部樓層 |閱讀模式

對排序做了一些詳細(xì)的介紹,需要的可以看看。

簡介
其中排序算法總結(jié)如下:
1.交換排序
  交換排序的基本思想都為通過比較兩個數(shù)的大小,當(dāng)滿足某些條件時對它進(jìn)行交換從而達(dá)到排序的目的。
1.1冒泡排序
基本思想:比較相鄰的兩個數(shù),如果前者比后者大,則進(jìn)行交換。每一輪排序結(jié)束,選出一個未排序中最大的數(shù)放到數(shù)組后面。
代碼:
#include<stdio.h>
//冒泡排序算法
void bubbleSort(int *arr, int n) {
    for (int i = 0; i<n - 1; i++)
        for (int j = 0; j < n - i - 1; j++)
        {
            //如果前面的數(shù)比后面大,進(jìn)行交換
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp;
            }
        }
}
int main() {
    int arr[] = { 10,6,5,2,3,8,7,4,9,1 };
    int n = sizeof(arr) / sizeof(int);
    bubbleSort(arr, n);
    printf("排序后的數(shù)組為:\n");
    for (int j = 0; j<n; j++)
        printf("%d ", arr[j]);
    printf("\n");
return 0;

分析:
  最差時間復(fù)雜度為O(n^2),平均時間復(fù)雜度為O(n^2)。穩(wěn)定性:穩(wěn)定。輔助空間O(1)。
升級版冒泡排序法:通過從低到高選出最大的數(shù)放到后面,再從高到低選出最小的數(shù)放到前面,如此反復(fù),直到左邊界和右邊界重合。當(dāng)數(shù)組中有已排序好的數(shù)時,這種排序比傳統(tǒng)冒泡排序性能稍好。
代碼:
#include<stdio.h>
//升級版冒泡排序算法
void bubbleSort_1(int *arr, int n) {
    //設(shè)置數(shù)組左右邊界
    int left = 0, right = n - 1;
    //當(dāng)左右邊界未重合時,進(jìn)行排序
    while (left<right) {
        //從左到右遍歷選出最大的數(shù)放到數(shù)組右邊
        for (int i =left; i < right; i++)
        {
            if (arr[ i] > arr[i + 1])
            {
                int temp = arr[ i]; arr[ i] = arr[i + 1]; arr[i + 1] = temp;
            }
        }
        right--;
        //從右到左遍歷選出最小的數(shù)放到數(shù)組左邊
        for (int j = right;j> left; j--)
        {
            if (arr[j + 1] < arr[j])
            {
                int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp;
            }
        }
        left++;
    }
}
int main() {
    int arr[] = { 10,6,5,2,3,8,7,4,9,1 };
    int n = sizeof(arr) / sizeof(int);
    bubbleSort_1(arr, n);
    printf("排序后的數(shù)組為:\n");
    for (int j = 0; j<n; j++)
        printf("%d ", arr[j]);
    printf("\n");
    return 0;
}
1.2. 快速排序
  基本思想:選取一個基準(zhǔn)元素,通常為數(shù)組最后一個元素(或者第一個元素)。從前向后遍歷數(shù)組,當(dāng)遇到小于基準(zhǔn)元素的元素時,把它和左邊第一個大于基準(zhǔn)元素的元素進(jìn)行交換。在利用分治策略從已經(jīng)分好的兩組中分別進(jìn)行以上步驟,直到排序完成。下圖表示了這個過程。
代碼:
#include<stdio.h>
void swap(int *x, int *y) {
    int tmp = *x;
    *x = *y;
    *y = tmp;
}
//分治法把數(shù)組分成兩份
int patition(int *a, int left,int right) {
    int j = left;    //用來遍歷數(shù)組
    int i = j - 1;    //用來指向小于基準(zhǔn)元素的位置
    int key = a[right];    //基準(zhǔn)元素
    //從左到右遍歷數(shù)組,把小于等于基準(zhǔn)元素的放到左邊,大于基準(zhǔn)元素的放到右邊
    for (; j < right; ++j) {
        if (a[j] <= key)
            swap(&a[j], &a[++i]);
    }
    //把基準(zhǔn)元素放到中間
    swap(&a[right], &a[++i]);
    //返回數(shù)組中間位置
    return i;
}
//快速排序
void quickSort(int *a,int left,int right) {
    if (left>=right)
        return;
    int mid = patition(a,left,right);
    quickSort(a, left, mid - 1);
    quickSort(a, mid + 1, right);
}
int main() {
    int a[] = { 10,6,5,7,12,8,1,3,11,4,2,9,16,13,15,14 };
    int n = sizeof(a) / sizeof(int);
    quickSort(a, 0,n-1);
    printf("排序好的數(shù)組為:");
    for (int l = 0; l < n; l++) {
        printf("%d ", a[l]);
    }
    printf("\n");
    return 0;
}

分析:  
  最差時間復(fù)雜度:每次選取的基準(zhǔn)元素都為最大(或最小元素)導(dǎo)致每次只劃分了一個分區(qū),需要進(jìn)行n-1次劃分才能結(jié)束遞歸,故復(fù)雜度為O(n^2);最優(yōu)時間復(fù)雜度:每次選取的基準(zhǔn)元素都是中位數(shù),每次都劃分出兩個分區(qū),需要進(jìn)行l(wèi)ogn次遞歸,故時間復(fù)雜度為O(nlogn);平均時間復(fù)雜度:O(nlogn)。穩(wěn)定性:不穩(wěn)定的。輔助空間:O(nlogn)。
  當(dāng)數(shù)組元素基本有序時,快速排序?qū)]有任何優(yōu)勢,基本退化為冒泡排序,可在選取基準(zhǔn)元素時選取中間值進(jìn)行優(yōu)化。

2. 插入排序
2.1 直接插入排序
基本思想:和交換排序不同的是它不用進(jìn)行交換操作,而是用一個臨時變量存儲當(dāng)前值。當(dāng)前面的元素比后面大時,先把后面的元素存入臨時變量,前面元素的值放到后面元素位置,再到最后把其值插入到合適的數(shù)組位置!
代碼:
#include<stdio.h>
void InsertSort(int  *a, int n) {
    int tmp = 0;
    for (int i = 1; i < n; i++) {
        int j = i - 1;
        if (a[ i] < a[j]) {
            tmp = a[ i];
            a[ i] = a[j];
            while (tmp < a[j-1]) {
                a[j] = a[j-1];
                j--;
            }
            a[j] = tmp;
        }
    }
}
int main() {
    int a[] = { 11,7,9,22,10,18,4,43,5,1,32};
    int n = sizeof(a)/sizeof(int);
    InsertSort(a, n);
    printf("排序好的數(shù)組為:");
    for (int i = 0; i < n; i++) {
        printf(" %d", a[ i]);
    }
    printf("\n");
    return 0;
}

分析:
最壞時間復(fù)雜度為數(shù)組為逆序時,為O(n^2)。最優(yōu)時間復(fù)雜度為數(shù)組正序時,為O(n)。平均時間復(fù)雜度為O(n^2)。輔助空間O(1)。穩(wěn)定性:穩(wěn)定。
2.2 希爾(shell)排序
  基本思想為在直接插入排序的思想下設(shè)置一個最小增量dk,剛開始dk設(shè)置為n/2。進(jìn)行插入排序,隨后再讓dk=dk/2,再進(jìn)行插入排序,直到dk為1時完成最后一次插入排序,此時數(shù)組完成排序。
代碼:
#include<stdio.h>
//    進(jìn)行插入排序
//    初始時從dk開始增長,每次比較步長為dk
void Insrtsort(int *a, int n,int dk) {
    for (int i = dk; i < n; ++i) {
        int j = i - dk;
        if (a[ i] < a[j]) {    //    比較前后數(shù)字大小
            int tmp = a[ i];        //    作為臨時存儲   
            a[ i] = a[j];
            while (a[j] > tmp) {    //    尋找tmp的插入位置
                a[j+dk] = a[j];
                j -= dk;
            }
            a[j+dk] = tmp;        //    插入tmp
        }
    }
}
void ShellSort(int *a, int n) {
    int dk = n / 2;        //    設(shè)置初始dk
    while (dk >= 1) {
        Insrtsort(a, n, dk);
        dk /= 2;
    }
}
int main() {
    int a[] = { 5,12,35,42,11,2,9,41,26,18,4 };
    int n = sizeof(a) / sizeof(int);
    ShellSort(a, n);
    printf("排序好的數(shù)組為:");
    for (int j = 0; j < n; j++) {
        printf("%d ", a [j]);
    }
    return 0;
}

分析:
  最壞時間復(fù)雜度為O(n^2);最優(yōu)時間復(fù)雜度為O(n);平均時間復(fù)雜度為O(n^1.3)。輔助空間O(1)。穩(wěn)定性:不穩(wěn)定。希爾排序的時間復(fù)雜度與選取的增量有關(guān),選取合適的增量可減少時間復(fù)雜度。

3. 選擇排序
3.1.直接選擇排序
基本思想:依次選出數(shù)組最小的數(shù)放到數(shù)組的前面。首先從數(shù)組的第二個元素開始往后遍歷,找出最小的數(shù)放到第一個位置。再從剩下數(shù)組中找出最小的數(shù)放到第二個位置。以此類推,直到數(shù)組有序。
代碼:
#include<stdio.h>
void SelectSort(int *a, int n) {
    for (int i = 0; i < n; i++)
    {
        int key = i;    //    臨時變量用于存放數(shù)組最小值的位置
        for (int j = i + 1; j < n; j++) {
            if (a[j] < a[key]) {   
                key = j;    //    記錄數(shù)組最小值位置
            }
        }
            if (key != i)
            {
                int tmp = a[key]; a[key] = a[ i]; a[ i] = tmp;    //    交換最小值
            }
    }
}
int main() {
    int a[] = { 12,4,15,2,6,22,8,10,1,33,45,24,7 };
    int n = sizeof(a) / sizeof(int);
    SelectSort(a, n);
    printf("排序好的數(shù)組為: ");
    for (int k = 0; k < n; k++)
        printf("%d ", a[k]);
    printf("\n");
    return 0;
}
分析:
最差、最優(yōu)、平均時間復(fù)雜度都為O(n^2)。輔助空間為O(1)。穩(wěn)定性:不穩(wěn)定。

3.2.堆(Heap)排序
  基本思想:先把數(shù)組構(gòu)造成一個大頂堆(父親節(jié)點大于其子節(jié)點),然后把堆頂(數(shù)組最大值,數(shù)組第一個元素)和數(shù)組最后一個元素交換,這樣就把最大值放到了數(shù)組最后邊。把數(shù)組長度n-1,再進(jìn)行構(gòu)造堆,把剩余的第二大值放到堆頂,輸出堆頂(放到剩余未排序數(shù)組最后面)。依次類推,直至數(shù)組排序完成。
  下圖為堆結(jié)構(gòu)及其在數(shù)組中的表示?梢灾蓝秧?shù)脑貫閿?shù)組的首元素,某一個節(jié)點的左孩子節(jié)點為其在數(shù)組中的位置*2,其右孩子節(jié)點為其在數(shù)組中的位置*2+1,其父節(jié)點為其在數(shù)組中的位置/2(假設(shè)數(shù)組從1開始計數(shù))。
下圖為怎么把一個無序的數(shù)組構(gòu)造成一個大堆頂結(jié)構(gòu)的數(shù)組的過程,注意其是從下到上,從右到左,從右邊第一個非葉子節(jié)點開始構(gòu)建的。
代碼:
#include<stdio.h>
//  創(chuàng)建大堆頂,i為當(dāng)節(jié)點,n為堆的大小
//    從第一個非葉子結(jié)點i從下至上,從右至左調(diào)整結(jié)構(gòu)
//    從兩個兒子節(jié)點中選出較大的來與父親節(jié)點進(jìn)行比較
//    如果兒子節(jié)點比父親節(jié)點大,則進(jìn)行交換
void CreatHeap(int a[], int i, int  n) {
    //    注意數(shù)組是從0開始計數(shù),所以左節(jié)點為2*i+1,右節(jié)點為2*i+2
    for (; i >= 0; --i)
    {
        int left = i * 2 + 1;    //左子樹節(jié)點
        int right = i * 2 + 2;    //右子樹節(jié)點
        int j = 0;
        //選出左右子節(jié)點中最大的
        if (right < n) {
            a[left] > a[right] ? j= left : j = right;
        }
        else
            j = left;
        //交換子節(jié)點與父節(jié)點
        if (a[j] > a[ i]) {
            int tmp = a[ i];
            a[ i] = a[j];
            a[j] = tmp;
        }
    }
}
//    進(jìn)行堆排序,依次選出最大值放到最后面
void HeapSort(int a[], int n) {
    //初始化構(gòu)造堆
    CreatHeap(a, n/2-1, n);
  //交換第一個元素和最后一個元素后,堆的大小減1
    for (int j = n-1; j >= 0; j--) {
        //最后一個元素和第一個元素進(jìn)行交換
        int tmp = a[0];
        a[0] = a[j];
        a[j] = tmp;
        int i = j / 2 - 1;
        CreatHeap(a, i, j);
    }
}
int main() {
    int a[] = { 10,6,5,7,12,8,1,3,11,4,2,9,16,13,15,14 };
    int n = sizeof(a) / sizeof(int);
    HeapSort(a, n);
    printf("排序好的數(shù)組為:");
    for (int l = 0; l < n; l++) {
        printf("%d ", a[l]);
    }
    printf("\n");
    return 0;
}

分析:
  最差、最優(yōu)‘平均時間復(fù)雜度都為O(nlogn),其中堆的每次創(chuàng)建重構(gòu)花費O(lgn),需要創(chuàng)建n次。輔助空間O(1)。穩(wěn)定性:不穩(wěn)定。

4. 歸并排序
  基本思想:歸并算法應(yīng)用到分治策略,簡單說就是把一個答問題分解成易于解決的小問題后一個個解決,最后在把小問題的一步步合并成總問題的解。這里的排序應(yīng)用遞歸來把數(shù)組分解成一個個小數(shù)組,直到小數(shù)組的數(shù)位有序,在把有序的小數(shù)組兩兩合并而成有序的大數(shù)組。
  下圖為展示如何歸并的合成一個數(shù)組。


下圖展示了歸并排序過程各階段的時間花費。

代碼:
#include <stdio.h>
#include <limits.h>
// 合并兩個已排好序的數(shù)組
void Merge(int a[], int left, int mid, int right)
{
    int len = right - left + 1;        //    數(shù)組的長度
    int *temp = new int[len];       // 分配個臨時數(shù)組
    int k = 0;
    int i = left;                   // 前一數(shù)組的起始元素
    int j = mid + 1;                // 后一數(shù)組的起始元素
    while (i <= mid && j <= right)
    {
        //    選擇較小的存入臨時數(shù)組
        temp[k++] = a[ i] <= a[j] ? a[i++] : a[j++];
    }
    while (i <= mid)
    {
        temp[k++] = a[i++];
    }
    while (j <= right)
    {
        temp[k++] = a[j++];
    }
    for (int k = 0; k < len; k++)
    {
        a[left++] = temp[k];
    }
}
// 遞歸實現(xiàn)的歸并排序
void MergeSort(int a[], int left, int right)
{
    if (left == right)   
        return;
    int mid = (left + right) / 2;
    MergeSort(a, left, mid);
    MergeSort(a, mid + 1, right);
    Merge(a, left, mid, right);
}
int main() {
    int a[] = { 5,1,9,2,8,7,10,3,4,0,6 };
    int n = sizeof(a) / sizeof(int);
    MergeSort(a, 0, n - 1);
    printf("排序好的數(shù)組為:");
    for (int k = 0; k < n; ++k)
        printf("%d ", a[k]);
    printf("\n");
    return 0;
}

分析:
  最差、最優(yōu)、平均時間復(fù)雜度都為O(nlogn),其中遞歸樹共有l(wèi)gn+1層,每層需要花費O(n)。輔助空間O(n)。穩(wěn)定性:穩(wěn)定。


完整的Word格式文檔51黑下載地址:
排序算法.docx (447.23 KB, 下載次數(shù): 9)


評分

參與人數(shù) 2黑幣 +60 收起 理由
YJGG + 10 贊一個!
admin + 50 共享資料的黑幣獎勵!

查看全部評分

回復(fù)

使用道具 舉報

您需要登錄后才可以回帖 登錄 | 立即注冊

本版積分規(guī)則

手機(jī)版|小黑屋|51黑電子論壇 |51黑電子論壇6群 QQ 管理員QQ:125739409;技術(shù)交流QQ群281945664

Powered by 單片機(jī)教程網(wǎng)

快速回復(fù) 返回頂部 返回列表