7种排序 C实现

以前上学,排序就记下了一种——冒泡——简单好记

在找工作的过程中也有笔试题会问排序,都是写冒泡

 

现在找了些资料,把7种排序写一下,也不知道以后还能记得几种

在我理解,那些时间复杂度是O(nlogn)的,都是以复杂性来换取效率的。还有就是,我注释里的稳定性啊,最好、最坏情况啊,都是凭感觉写的,应该会跟其它资料有出入。大家自己甄别

 

#include"stdio.h"

void printArray(int* a,int n){
    for(int i=0;i<n;i++){
        printf("%d,",a[i]);
    }
    printf("\n");
}

/*
【直接插入排序】
把数组分成有序序列、无序序列
每次取无序序列第一个,插入到有序序列中
插入的时候循环有序序列,确定位置后,循环把元素往后移。把之前的元素插入所确定位置
*它在待排序数据基本有序并且数量较少的时候威力还是很大的
【key】这个是稳定的,时间O(n^2),空间O(1)。最好O(n),最坏O(n^2)
*/
void InsertSort(int* a,int size){
    int i,j,k,tmp=0;
    for(i=1;i<size;i++){//从无序序列中取第一个
        tmp=a[i];
        for(j=0;j<i;j++){//循环有序序列,确定新元素要插入的位置
            if(tmp<a[j]){//把小的数放前面
                for(k=i-1;k>=j;k--){//把j...i-1的元素都向后移一位,为tmp挪位置
                    a[k+1]=a[k];
                }
                a[j]=tmp;
                break;
            }
        }
    }
}
//在这个写法中能比较明显地看出:
//在数组几乎有序时,排序的时间复杂度是O(n)
void InsertSort2(int* array,int length){
    int j,k,temp=0;
    for(j=1;j<length;j++){//单独一次的插入排序
        if(array[j]<array[j-1]){
            temp=array[j];//哨兵
            k=j-1;
            //这种写法,检查有序序列的时候是从最后一个开始检查
            //之前的写法是写个循环先确定位置,再写个循环挪位置
            //这种写法,是直接挪位置,挪不动了就是所确定位置
            while(k>=0 && array[k]>temp){
                array[k+1]=array[k];
                k--;
            }
            array[k+1] = temp;
        }  
    } 
}

/*
【二分插入排序】
就是插入排序的改进
数组左边是有序序列,所以确定插入位置的时候可以用二分来确定
(这种肯定是要比插入排序快的,但改善的不明显)
【key】这个是稳定的,时间O(n^2),空间O(1)。最好,最坏
它的最好最坏是一样的,因为都要二分找下去
*/
void BinaryInsertSort(int* a,int size){
    int i,k,tmp=0;
    int low,high,mid=0;
    for(i=1;i<size;i++){
        tmp=a[i];
        low=0;
        high=i-1;
        while(low<=high){//确定插入位置(low)
            mid=(low+high)/2;
            if(tmp<a[mid])
                high=mid-1;
            else
                low=mid+1;
        }//这个while循环最后肯定是low=high,该位置就是新元素插入位置
        for(k=i-1;k>=low;k--){
            a[k+1]=a[k];
        }
        a[low]=tmp;
    }
}

/*
【冒泡排序】
这个很简单
之前的2个插入排序比这个好在:每次交换就要有3个操作,而插入排序没有用交换
【key】稳定,时间O(n^2),空间O(1)。最好O(n),最坏O(n^2)
最好是:加了变量isSwap,第一次内层循环,如果一次都没有交换过,就排序结束
*/
void BubbleSort(int* a,int size){
    int i,j,tmp=0;
    for(i=0;i<size;i++){
        for(j=i+1;j<size;j++){
            if(a[i]>a[j]){
                tmp=a[j];
                a[j]=a[i];
                a[i]=tmp;
            }
        }
    }
}

/*
【简单选择排序】
循环无序序列,选出最小的,放在无序序列的最头一个位置
执行n-1次,就完成了排序
c里面球数组长度其实不好求,所以直接给出
【key】??,时间O(n^2),空间O(1)。最好(n^2),最坏(n^2)
稳定与否要看是怎么选择的,这个是稳定的
*/
void SelectSort(int* a,int n){
    int last=n-1;
    for(int cur=0;cur<last;cur++){//cur是该次循环要替换的位置
        int index=cur;
        for(int i=cur;i<n;i++){//循环一次,取出无序序列里的最小值
            if(a[index]>a[i])
                index=i;
        }
        if(index!=cur){//把最小值交换到无线序列开头
            int tmp=a[cur];
            a[cur]=a[index];
            a[index]=tmp;
        }
    }
}

/*
【希尔排序】
发明这个排序的人叫希尔(所以这个算法跟shell这个词是没关系的)
它其实是插入排序的一种高效率的改进
改进的思路是:插入排序在序列基本有序的时候效率几乎是线性的。所以就尽量弄些有序的序列出来
代码:1.给数组分组,对各个分组进行插入排序;2.把分组变少,重复1直到只有一个分组
(一开始分组多,一个分组小,排序快;后来分组变大,但基本有序,也快)
还有一种写法是只用3个for循环的(现在这种等于是用了4个),它有一个for是把分组和插入混在一起。不提倡,现在这种思路更清晰
【key】不稳定,时间O(nlogn),空间O(1)。最好O(nlogn),最坏O(nlogn)
不稳定理由同下
时间:外层的分组一直是/2,所以外层次数是logn
*/
void ShellSort(int* a,int size){
    int i,j,k,key,gap=0;
    for(gap=size/2;gap>0;gap=gap/2){
        for(i=0;i<gap;i++){//头2个for是分组
            for(j=i+gap;j<size;j=j+gap){//从这里开始是插入排序
                //这个写法看着有点烦,其实它只是把对应元素跳着捡出来而已
                key=a[j];
                k=j-gap;
                if(key<a[k]){
                    while(k>=0&&key<a[k]){
                        a[k+gap]=a[k];
                        k=k-gap;
                    }
                    a[k+gap]=key;
                }
            }
        }
    }
}

/*
【快速排序】
一种说法是快排是冒泡的改进,因为快排也用到交换。并不认同这种说法
快排也是一种增量减小的排序。
思路:取序列头元素做标准,把序列从左到右分成<=>,3个组。这个分组用的就是交换;然后再对<>2个组做快排(递归),直到分组数是1
(这种排序感觉怪怪的,莫名其妙就出现了。公认是比较好的排序方法。有一种说法:排序可以尽量用希尔排序,无论数量,如果有性能问题,再改成快排)
【key】不稳定,时间(nlogn),空间O(logn)。最好O(nlogn),最坏O(n^2)
不稳定是因为:它不是依次循环,而是从两头向中间地循环,所以...
空间这个:猜测外层循环左右2个数组进行快排,有点像二分,所以是logn次;
*/
void QuickSort(int* a,int low,int high){
    if(low>=high) return;//这一句是python的思想,尽量少缩进

    int i=low,j=high,key=a[low],tmp=0;
    while(i<j){
        //2个while不能颠倒,因为整个序列里只有a[low]是闲值(有备份),其它都是实际值.如果先给a[J]赋值,就会有值被抹去
        //感觉这个写法还是很巧妙,有人叫"挖坑填数"
        while(i<j&&a[j]>key) j--;
        if(i<j) a[i++]=a[j];//赋值后a[j]就成了闲值(有备份)

        while(i<j&&a[i]<key) i++;
        if(i<j) a[j--]=a[i];
    }
    //执行完while后i=j
    a[i]=key;

    QuickSort(a,low,i-1);
    QuickSort(a,i+1,high);
}

/*
【堆排序】
最大堆的特点:对于随意某个结点,该结点的值大于左孩子、右孩子的值,可是左右孩子的值没有要求。——所以它只要对前一半的元素建堆就好
感觉堆排序的2个函数都很经典
【key】不稳定,时间O(nlogn),空间O(1)。最好O(nlogn),最坏O(nlogn)
不稳定是因为它建堆or堆调整的时候是跳着进行的,有可能把相同的元素放到前面了
时间:HeapAdjust是logn,外层是n。所以...
*/
//堆调整函数-在i以及i的2个孩子中,选出最大的放在i的位置
void HeapAdjust(int* a,int i,int size){
    if(i<0||i>=size) return;

    int tmp,child=0;
    for(;2*i+1<size;i=child){//要保证有孩子节点,才可以比较&交换
        child=2*i+1;

        if((child+1)<size && a[child]<a[child+1])
            child++;//挑选出孩子里面比较大的那个节点

        if(a[child]>a[i]){//有过交换的要继续在循环里检查它的子孩子
            tmp=a[i];
            a[i]=a[child];
            a[child]=tmp;
        }
        else{
            break;
        }
    }
}
//先对数组前半部分建堆,然后循环-把数组第一个元素与最后一个元素(递减)交换后,每次交换要调整堆以保证第一个元素是最大
void HeapSort(int* a,int size){
    int i,tmp=0;
    //建堆
    for(i=size/2-1;i>=0;i--)
        HeapAdjust(a,i,size);
    
    //单次交换,并调整堆
    for(i=size-1;i>0;i--){
        tmp=a[0];
        a[0]=a[i];
        a[i]=tmp;
        HeapAdjust(a,0,i);
    }
}

/*
【归并排序】
思想:把2个有序的序列合并成1个有序的序列。
一开始数组里有n个有序组,按顺序两两合并,直到只有一个有序组。
关键就是那个for循环,和怎么两两合并
(感觉不太有意思,就不写了)
【key】稳定;时间O(nlogn);空间O(n)。最好O(nlogn),最坏O(nlogn)
*/

void main(){
    //c的特点之一:变量都在开头申明,后面慢慢用

    int size=10;
    int zz[10]={4,5,7,9,11,1,2,9,0,-1};

    //插入排序
    //InsertSort(zz,size);
    //InsertSort2(zz,size);

    //二分插入排序
    //BinaryInsertSort(zz,size);

    //冒泡排序
    //BubbleSort(zz,size);

    //选择排序
    //SelectSort(zz,size);

    //shell排序
    //ShellSort(zz,size);

    //快速排序
    //QuickSort(zz,0,size-1);

    //归并排序

    //堆排序
    HeapSort(zz,size);

    printArray(zz,size);
}

当时没写MergeSort(因为感觉没用),后来因为学习java(里面有用到这个排序)。去看了下,感觉还是挺有意思。

这个链接里讲的已经很好——https://www.cnblogs.com/eudiwffe/p/6254394.html

归并排序的思想是分治,代码里面,主要是递归思想、还有就是合并的那个写法(这个排序没有排序,只有合并。最基本的是2个大小为1的有序数组合并,然后2个大小为2的有序数组合并......)。

除了基本思想外,它数组和链表的写法也很有意思。(链表写法的快慢指针可以看下)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值