排序算法(冒泡、选择、直插、希尔、堆、归并、快排)

简单排序

#include <stdio.h>
#include <stdlib.h>
#define MAXN 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};

    for(int i = 0; i < MAXN; ++i)
    {
        for(int j = MAXN-1; j > i; --j)
         if(num[j] < num[i])
            swap(num,i,j);
    }

    for(int i = 0; i < MAXN; ++i)
        printf("%d ",num[i]);

    return 0;
}

冒泡排序

普通冒泡排序
能够有效的减少swap函数的调用次数,提高速度。

#include <stdio.h>
#include <stdlib.h>
#define MAXN 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};

    for(int i = 0; i < MAXN-1; ++i)  //前面排序好了最后一个位置不用排序
    {
        for(int j = MAXN - 2; j >= i; --j) //从尾向头开始才能保证位置i为最小的数
            if(num[j] > num[j+1])
            swap(num,j+1,j);
    }

    for(int i = 0; i < MAXN; ++i)
        printf("%d ",num[i]);

    return 0;
}

优化冒泡排序
当没有发生位置互换时其实已经代表后面的数也是排序好的了,所以不用再循坏了

#include <stdio.h>
#include <stdlib.h>
#define MAXN 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[MAXN] = {9,1,5,8,3,7,4,6,2,10};
    int flag = 1;
    for(int i = 0; i < MAXN-1 && flag; ++i)
    {
        flag = 0;
        for(int j = MAXN-2; j >= i ; --j)
        {
            if(num[j] > num[j+1])
            {
                swap(num,j,j+1);
                flag = 1;
            }
        }
    }
    for(int i = 0; i < MAXN; ++i)
        printf("%d ",num[i]);

    return 0;
}

时间复杂度
最优:第一次i循坏就排序完成——O(n)
最差:需要比较 i-1 (i:2~10)的总和,为(n-1)*(n-1)/2——O(n^2)

选择排序

#include <stdio.h>
#include <stdlib.h>
#define M 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[M] = {9,1,5,8,3,7,4,6,2,10};
    int min;
    for(int i = 0; i < M-1; ++i)
    {
        min = i;
        for(int j = i+1; j < M; ++j)
            if(num[j] < num[min])
            min = j;
        if(min != i)
            swap(num,i,min);
    }
     for(int i = 0; i < M; ++i)
        printf("%d ",num[i]);

    return 0;
}

时间复杂度
需要比较 i-1 (i:2~10)的总和,为(n-1)*(n-1)/2
最优情况下交换0次,最差情况下交换n-1次
时间复杂度:O(n^2)
因为最坏只要交换n-1次所以选择排序普遍优于冒泡

直接插入排序

#include <stdio.h>
#include <stdlib.h>
#define M 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[M] = {9,1,5,8,3,7,4,6,10,2};
    int k,j;

    for(int i = 1; i < M; ++i)
    {
        k = num[i];
        j = i;
        while(j > 0 && num[j-1] > k)
        {
            num[j] = num[j-1];
            j--;
        }
        num[j] = k;
    }
     for(int i = 0; i < M; ++i)
        printf("%d ",num[i]);

    return 0;

}

算法复杂度
最优(本身有序):比较n-1次
最差:外层循坏n-1次,内层比较 i 次——O(n^2)

希尔排序

希尔排序通过一个增量将原来的数组变成一个较为有序的序列,然后再执行一次增量为1的排序(等同于最普通的插入排序)。
每次内层循坏可以看做一个根据增量的分组,再对这个分组进行插入排序。
关于增量的选择:只要最后增量能够达到1即可
(代码也表现插入排序j的两种不同写法,但原理都是相同)

#include <stdio.h>
#include <stdlib.h>
#define M 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

int main()
{
    int num[M] = {9,1,5,8,3,7,4,6,2,10};
    int increment = M;
    int j,k;
    do
    {
        increment = increment/3+1;
        for(int i = increment; i < M; ++i)
        {
            if(num[i] < num[i-increment])
            {
                k = num[i];
                for(j = i-increment; j >= 0 &&  num[j] > k; j -= increment)   //根据增量分组出一个序列并对这个序列进行插入排序
                    num[j+increment] = num[j];
                num[j+increment] = k;
            }
        }
    }while(increment > 1);
    for(int i = 0; i < M; ++i)
        printf("%d ",num[i]);

    return 0;

}

时间复杂度
O(nlogn)

堆排序

为什么用完全二叉树?因为这样第一次排序时,子结点只有一层,不用考虑忽略了最大结点在另一边的情况,底部排序好顶部的多层排序就足以完成。
这部代码充分体现了这一点。

void HeadSort(int num[],int m)
{
    for(int i = m/2-1; i >= 0; --i)
        HeapAdjust(num,i,m); //先维护底部

    for(int i = 0; i < m-1; ++i)
    {
        swap(num,0,m-1-i);
        HeapAdjust(num,0,m-i-1); 正如我所说的底部的有序使顶部的排序成为可能
    }
}

为什么是选择排序的改进?因为它维护堆的方式,犹如选择排序,找出最大(或小)的子节点,保存下来最后互换两个结点。

#include <stdio.h>
#include <stdlib.h>
#define M 10

void swap(int a[],int i,int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

void HeapAdjust(int num[],int s,int m) //将s-m维护成大顶堆
{
    int t = num[s];
    int j;

    for(j = s*2+1; j < m; j = j*2+1)
    {
        if(j < m-1 && num[j] < num[j+1])  //防止越界与找出左右孩子中的最大值
            j++;
        if(t >= num[j])
            break;
        num[s] = num[j];
        s = j;  //代表着s与j的互换在下个循坏中起效
    }

    num[s] = t;
}

void HeadSort(int num[],int m)
{
    for(int i = m/2-1; i >= 0; --i)
        HeapAdjust(num,i,m);

    for(int i = 0; i < m-1; ++i)
    {
        swap(num,0,m-1-i);
        HeapAdjust(num,0,m-i-1); 正如我所说的底部的有序使顶部的排序成为可能
    }
}

int main()
{
    int num[M] = {9,1,5,8,3,7,4,6,2,10};
    HeadSort(num,M);
     for(int i = 0; i < M; ++i)
        printf("%d ",num[i]);

    return 0;
}

时间复杂度
构建堆:至多进行两次比较(由于底层已经构建完成,完全二叉树第一次构建底部只有一层子节点比较一次),时间复杂度O(n)
重建堆:对于第i次重建堆需要循坏log(M-i-1),且共进行M-1次——O(nlogn)
总体:O(nlogn)

归并排序

注意MSort会用到边界所以t一定要代入最后一个元素位置而不是数组大小(本代码中t要代入9不能是10)
递归实现

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10
void MergeSort(int a[],int n)
{
    MSort(a,a,0,n-1);
}

void MSort(int SR[],int TR1[],int s,int t)
{
    int TR2[MAXSIZE];
    int m;
    if(s == t)
        TR1[s] = SR[s];
    else
    {
        m = (s+t)/2;
        MSort(SR,TR2,s,m);
        MSort(SR,TR2,m+1,t);
        Merge(TR2,TR1,s,m,t);
    }
}

void Merge(int SR[],int TR[],int i,int m,int n)
{
    int j,k,l;

    for(k = i,j = m+1; i <= m && j <= n; ++k)
    {
        if(SR[i] > SR[j])
            TR[k] = SR[j++];
        else
            TR[k] = SR[i++];
    }

    if(i <= m)
    {
        for(l = 0; l <= m-i; ++l)
            TR[k+l] = SR[i+l];
    }
    else if(j <= n)
    {
        for(l = 0; l <= n-j; ++l)
            TR[k+l] = SR[j+l];
    }
}

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

非递归实现

#include <stdio.h>
#include <stdlib.h>
#define MAXSIZE 10

void Merge(int SR[],int TR[],int i,int m,int t) //t为最尾元素下标
{
    int j,k,l;

    for(k = i,j = m+1; i <= m && j <= t; ++k)
    {
        if(SR[i] > SR[j])
            TR[k] = SR[j++];
        else
            TR[k] = SR[i++];
    }
    if(i <= m)
    {
        for(l = 0; l <= m-i; ++l)
            TR[k+l] = SR[i+l];
    }
    else if(j <= t)
    {
        for(l = 0; l <= t-j; ++j)
            TR[k+l] = SR[j+l];
    }
}

void MergePass(int SR[],int TR[],int s,int n) //注意n为最尾元素下标
{
    int i = 0;

    while(i <= n-2*s+1)  //能否完成下次归并的最小元素个数
    {
        Merge(SR,TR,i,i+s-1,i+2*s-1);
        i = i+2*s;
    }

    //将剩余的序列归并
    if(i < n-s+1)  //若有超过s长度的序列则可以归并
        Merge(SR,TR,i,i+s-1,n);
    else
    {
        for(int j = i; j <= n; ++j)
            TR[j] = SR[j];
    }
}

void MergeSort(int TR[],int n)  //n为数组长度
{
    int *SR = (int *)malloc(sizeof(int)*n);
    int k = 1;

    while(k < n)
    {
        MergePass(TR,SR,k,n-1);
        k *= 2;
        MergePass(SR,TR,k,n-1);
        k *= 2;
    }
}

int main()
{
    int num[MAXSIZE] = {9,1,5,8,3,7,4,6,2,10};
    MergeSort(num,MAXSIZE);
    for(int i = 0; i < 10; ++i)
        printf("%d ",num[i]);
    return 0;
}

时间复杂度
O(nlogn)

快速排序

#include <stdio.h>
#include <stdlib.h>

void q_sort(int num[],int begin,int end)
{
    if(begin >= end)
        return;

    int t = num[begin];
    int i =  begin, j = end-1;

    while(i < j)
    {
        while(i < j && num[j] >= t)
            j--;
        if(i < j)
        {
            num[i] = num[j];
            i++;
        }
        while(i < j && num[i] <= t)
            i++;
        if(i<j)
        {
            num[j] = num[i];
            j--;
        }
    }
    num[i] = t;
    q_sort(num,begin,i-1);
    q_sort(num,i+1,end);
}

int main()
{
    int num[10] = {9,1,5,8,3,7,4,6,2,10};
    q_sort(num,0,10);
    for(int i = 0; i < 10; ++i)
        printf("%d ",num[i]);
    return 0;
}

快排优化

#include <stdio.h>
#include <stdlib.h>
#define MAX_LENGTH_INSERT_SORT 7

void InsertSort(int num[],int n)
{
    int t;
    int j;
    for(int i = 1; i < n; ++i)
    {
        t = num[i];
        j = i;
        while(j > 0 && num[j-1] > t)
            {
                num[j] = num[j-1];
                j--;
            }
        num[j] = t;

       /*
       for(j = i-1; j >= 0 && num[j] > t; --j)
         num[j+1] = num[j];
       num[j+1] = t;
       */
    }
}

int Partitionl(int a[],int low,int high)
{
    int i = low, j = high;
    int t = a[i];

    while(i < j)
    {
        while(i < j && a[j] >= t)
            j--;
        if(i < j)
        {
            a[i] = a[j];
            i++;
        }
        while(i < j && a[i] <= t)
            i++;
        if(i < j)
        {
            a[j] = a[i];
            j--;
        }
    }
    a[i] = t;

    return i;
}

void QSort(int a[],int low,int high,int n)
{
    int p;
    if((high-low) > MAX_LENGTH_INSERT_SORT)
    {
        while(low < high)
        {
            p = Partitionl(a,low,high);
            QSort(a,low,p-1,n);
            low = p+1;
        }
    }
    else
        InsertSort(a,n);
}


int main()
{
   int num[10] = {9,1,5,8,3,7,4,6,2,10};
    QSort(num,0,9,10);
    for(int i = 0; i < 10; ++i)
        printf("%d ",num[i]);
    return 0;
}

时间复杂度
O(logn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值