數據結構之排序算法(C語言)


一.冒泡排序

冒泡排序是最簡單的排序之一了,其大體思想就是通過與相鄰元素的比較交換來把小的數交換到最前面。這個過程類似於水泡向上升一樣,因此而得名。舉個栗子,對5,3,8,6,4這個無序序列進行冒泡排序。首先從前向后冒泡,5和3比較,換數,序列變成3,5,8,6,4。同理5和8比較,不用交換,還是3,5,8,6,4。8和6比較,交換,變成3,5,6,8,4。8最后和4比較,交換,得到3,5,6,4,8。這樣一次冒泡就完了,把最大的數8排到最后面了。對剩下的序列依次冒泡就會得到一個有序序列。冒泡排序的時間復雜度為O(n^2)。

代碼實現:

/*冒泡排序*/
#include<stdio.h>

void bubble_sort(int *a,int len)
{
int i,j,t;
for(i=0;i<len-1;i++)//整體比較次數 (比如兩個數只需要比較一次)所以要減一 
    for(j=0;j<len-i-1;j++)//每一次需要哪幾個數來比,減掉1個(比如兩個數只需要比較一次),再減掉已經排好了的數. 
        if(a[j]>a[j+1])
        {
            t = a[j];
            a[j] = a[j+1];
            a[j+1] = t;
        }
}

int main()
{
    int a[5] = {5,3,8,6,4};
    bubble_sort(a,5);
    for(i=0;i<5;i++)
        printf("%d  ",a[i]);
}

二.選擇排序

選擇排序的思想其實和冒泡排序有點類似,都是在一次排序后把最小的元素放到最前面。但是過程不同,冒泡排序是通過相鄰的比較和交換。而選擇排序是通過對整體的選擇。舉個栗子,對5,3,8,6,4這個無序序列進行簡單選擇排序,首先要選擇5以外的最小數來和5交換,也就是選擇3和5交換,一次排序后就變成了3,5,8,6,4.對剩下的序列一次進行選擇和交換,最終就會得到一個有序序列。其實選擇排序可以看成冒泡排序的優化,因為其目的相同,只是選擇排序只有在確定了最小數的前提下才進行交換,大大減少了交換的次數。選擇排序的時間復雜度為O(n^2)。

代碼實現:

/*選擇排序,av18176082*/ #include<stdio.h>
void elect_sort(int *a,int len) { int i,j,t,k; for(i=0;i<len-1;i++)//一共要拿len-1個數出來和其他數比 
 { k=i;//讓k記住這個數 
        for(j=i+1;j<len;j++)//讓k這個數和其他未排序的數比 
            if(a[k]>a[j]) { t = a[k]; a[k] = a[j]; a[j] = t; } } } int main() { int a[5] = {5,3,8,6,4},i; elect_sort(a,5); for(i=0;i<5;i++) printf("%d ",a[i]); return 0; } 
            
/*
void sort(int *a,int len)
{
    int i,j,t,k;
    for(i=0;i<len-1;i++)
        for(j=i+1;j<len;j++)
        {
            if(a[i]<a[j])
            {
                t = a[i];
                a[i] = a[j];
                a[j] = t;
            }
        }
        
}
*/

三.插入排序

插入排序不是通過交換位置而是通過比較找到合適的位置插入元素來達到排序的目的的。相信大家都有過打撲克牌的經歷,特別是牌數較大的。在分牌時可能要整理自己的牌,牌多的時候怎么整理呢?就是拿到一張牌,找到一個合適的位置插入。這個原理其實和插入排序是一樣的。舉個栗子,對5,3,8,6,4這個無序序列進行簡單插入排序,首先假設第一個數的位置時正確的,想一下在拿到第一張牌的時候,沒必要整理。然后3要插到5前面,把5后移一位,變成3,5,8,6,4.想一下整理牌的時候應該也是這樣吧。然后8不用動,6插在8前面,8后移一位,4插在5前面,從5開始都向后移一位。注意在插入一個數的時候要保證這個數前面的數已經有序。簡單插入排序的時間復雜度也是O(n^2)。

代碼實現:

#include <stdio.h>

void insert_sort(int *a,int len) 
{
    int i,j,tmp;
    for(i=1;i<len;i++)//拿這么多個數來插入,第一個數本身就是有序序列,不需要比較
    {
        tmp = a[i];
        j = i-1;//j是有序序列最后一個數的下標 
        while(j>=0&&tmp<a[j])//升序 
        {    
            a[j+1] = a[j];//不是插入位置,數往后移動
            j--; 
        } 
        a[j+1] = tmp; 
    }
}
int main(int argc, char *argv[])
{
    int a[5]={5,3,8,6,4},i;
    insert_sort(a,5);
    for(i=0;i<5;i++)
        printf("%d  ",a[i]);
    return 0;
}
/*
void sort(int *a,int len)
{
    int i,j,tmp;
    for(i=1;i<len;i++)
    {
        tmp = a[i];
        for(j=i-1;j>=0&&a[j]>tmp;j--)
            a[j+1] = a[j];
        a[j+1] = tmp;
    }
    
}
*/

四.快速排序

快速排序一聽名字就覺得很高端,在實際應用當中快速排序確實也是表現最好的排序算法。快速排序雖然高端,但其實其思想是來自冒泡排序,冒泡排序是通過相鄰元素的比較和交換把最小的冒泡到最頂端,而快速排序是比較和交換小數和大數,這樣一來不僅把小數冒泡到上面同時也把大數沉到下面。

舉個栗子:對5,3,8,6,4這個無序序列進行快速排序,思路是右指針找比基准數小的,左指針找比基准數大的,交換之。

5,3,8,6,4 用5作為比較的基准,最終會把5小的移動到5的左邊,比5大的移動到5的右邊。

5,3,8,6,4 首先設置low,high兩個指針分別指向兩端,high指針先掃描(思考一下為什么?)4比5小停止。然后low掃描,8比5大停止。交換low,high位置。

5,3,4,6,8 然后j指針再掃描,這時j掃描4時兩指針相遇。停止。然后交換4和基准數。

4,3,5,6,8 一次划分后達到了左邊比5小,右邊比5大的目的。之后對左右子序列遞歸排序,最終得到有序序列。

上面留下來了一個問題為什么一定要j指針先動呢?首先這也不是絕對的,這取決於基准數的位置,因為在最后兩個指針相遇的時候,要交換基准數到相遇的位置。一般選取第一個數作為基准數,那么就是在左邊,所以最后相遇的數要和基准數交換,那么相遇的數一定要比基准數小。所以j指針先移動才能先找到比基准數小的數。

快速排序是不穩定的,其時間平均時間復雜度是O(nlgn)。

image

實現代碼:


#include <stdio.h>

void quick_sort(int *a,int low,int high);
int find_pos(int *a,int low,int high);

int main(int argc, char *argv[])
{
    int a[5] = {5,3,8,6,4},i;
    quick_sort(a,0,4);
    for(i=0;i<5;i++)
        printf("%d ",a[i]);
    return 0;
}
void quick_sort(int *a,int low,int high)
{
    int pos;
    if(low<high)
    {
        pos = find_pos(a,low,high);
        quick_sort(a,low,pos-1);
        quick_sort(a,pos+1,high);
    }
}
int find_pos(int *a,int low,int high)//尋找第一個數的准確位置
{
    int val = a[low];
    while(low<high)
    {
        while(low<high && a[high]>=val)//(low<high)這個條件不能少
            high--;
        a[low] = a[high];
        while(low<high && a[low]<=val)
            low++;
        a[high] = a[low];
    }
    //循環完后high = low
    a[high] = val;
    return high;
}

//視頻講解:av6159200 p76

五.希爾排序

希爾排序是插入排序的一種高效率的實現,也叫縮小增量排序。簡單的插入排序中,如果待排序列是正序時,時間復雜度是O(n),如果序列是基本有序的,使用直接插入排序效率就非常高。希爾排序就利用了這個特點。基本思想是:先將整個待排記錄序列分割成為若干子序列分別進行直接插入排序,待整個序列中的記錄基本有序時再對全體記錄進行一次直接插入排序。

實現代碼:


#include <stdio.h>

void shell_sort(int *a,int len)
{
    int i,j,D,tmp;
    for(D=len/2;D>0;D=D/2)//控制增量序列,循環里面其實就是一個插入排序 
    {                      //直接插入排序的間隔為1,這里是D,也就是是在將間隔為D的數列進行排序  
        for(i=D;i<len;i++)
        { 
            {
                tmp = a[i];
                for(j=i-D;j>=0&&a[j]>=tmp;j=j-D)//j為有序序列的最后一個元素的下標 
                    a[j+D] = a[j];
            }
            a[j+D] = tmp;
        } 
    }
} 
int main(int argc, char *argv[])
{
    int a[5] = {5,3,8,6,4}; 
    shell_sort(a,5);
    int i;
    for(i=0;i<5;i++)
        printf("%d  ",a[i]); 
    return 0;
}


六.堆排序

堆排序是利用二叉樹來進行排序,將數組的下標來作為二叉樹的下標.0下標無效,不存放有效數.然后將二叉樹排成一個最大堆或者最小堆,排好后將根節點(最大值或最小值)和數組的最后一個元素依次進行替換;這樣數組就變成了一個有序序列.

堆排序的時間復雜度為O(nlogn)  構建堆的過程的時間復雜度為n,調堆的時間復雜度為logn

實現代碼:

#include<stdio.h>

void swap(int *a,int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}
void percdown(int a[],int node,int len)
{
    int i;
    int temp = a[node];
    for(i=2*node;i<=len;i*=2)
    {
        if(i<len&&a[i]<a[i+1])//兩個孩子相比較 
            i++;//i指向最大的孩子 
        if(a[i]>temp)//最大的孩子和雙親進行比較 
        {
            a[node] = a[i];//讓最大的孩子的值賦值給雙親結點 
            node = i;//node就指向換數的那個孩子 
            /*這兩句最難理解,但是又很好理解其實就是一個換數的過程*/ 
        }
    }
    a[node] = temp;
}
void heap_sort(int *a,int len)
{
    int i;
    for(i=len/2;i>0;i--)//len/2:最后一個雙親結點  *從下往上,從右往左* 
        percdown(a,i,len);//len:當前堆里的元素個數  
    for(i=len;i>1;i--)
    {
        swap(&a[1],&a[i]);
        percdown(a,1,i-1);
    }
    
}
int main()
{
    int a[5] = {5,3,8,6,4};
    heap_sort(a,5);
    int i;
    for(i=0;i<5;i++)
        printf("%d  ",a[i]);
    return 0; 
}




免責聲明!

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



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