C语言常见的排序方法

这里记录了一些基础的排序方法

1.选择排序法

选排的算法步骤如下:

第一步,在未排序的n个数(a[0]~a[n-1])中找到最小数,将它与a[0]交换

第二步,在剩下的未排序的n-1个数中(a[1]~a[n-1])中找到最小数,将它与a[1]交换

。。。。。。

第n-1步,在剩下未排序的2个数中(a[n-2]~a[n-1])中找到最小数,将它与a[n-2]交换

不难发现,选择排序就是不断选出最小数放在数组前端

代码实现:

for(k=0;k<n-1;k++)//枚举排序次数
	{
		j=k;//最小值所在的下标
		for(i=k+1;i<n;i++)
		{
			if(a[i]<a[j]) j=i;//选择出了小的
		}
		tmp=a[j];
		a[j]=a[k];
		a[k]=tmp;//交换,把这个小的值放在前面
	}

时间复杂度:比较次数O(n^2),比较次数与关键字的初始状态无关,总的比较次数N=(n-1)+(n-2)+...+1=n*(n-1)/2。交换次数O(n),最好情况是,已经有序,交换0次;最坏情况交换n-1次,逆序交换n/2次。交换次数比冒泡排序少多了,由于交换所需CPU时间比比较所需的CPU时间多,n值较小时,选择排序比冒泡排序快,是一种不稳定的排序算法

2.冒泡排序法

原理:

比较相邻的元素,如果第一个比第二个大,则交换两者的值

对每一对元素作上述操作,从开始的一对到结尾的一对

针对所有的元素作上述操作,除了最后一个

这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端(升序或降序排列),就如同碳酸饮料中二氧化碳的气泡最终会上浮到顶端一样,故名“冒泡排序”。

代码实现:

void work(int a[],int n)
{
	int i,j,t;
	for(i=1;i<n;i++)
	{
		for(j=0;j<n-i;j++)//如果是a[0]则需要n-1次,如果是a[1]需要n-2次,,
		{
			if(a[j]>a[j+1])
			{
				t=a[j];
				a[j]=a[j+1];
				a[j+1]=t;
			}
		}
	}
}

时间复杂度:最好的时间复杂度为O(n),平均时间复杂度为O(n2)

3.插入排序法

原理:

基本操作是将一条记录插入到已经排序好的有序表中,从而得到一个新的,记录数量增1的有序表

首先在当前有序区a[1..i-1]中查找a[i]的正确插入位置k(1≤k≤i-1);然后将a[k..i-1]中的记录均后移一个位置,腾出k位置上的空间插入a[i]。

注意:若a[i]的关键字大于等于a[1..i-1]中所有记录的关键字,则a[i]就是插入原位置。

一种查找比较操作和记录移动操作交替地进行的方法。具体做法:

将待插入记录a[i]的关键字从右向左依次与有序区中记录a[j](j=i-1,i-2,…,1)的关键字进行比较:

① 若a[j]的关键字大于a[i]的关键字,则将R[j]后移一个位置;

②若a[j]的关键字小于或等于a[i]的关键字,则查找过程结束,j+1即为a[i]的插入位置。

关键字比a[i]的关键字大的记录均已后移,所以j+1的位置已经腾空,只要将a[i]直接插入此位置即可完成一趟直接插入排序

算法中引进的附加记录a[0]称监视哨或哨兵(Sentinel)。

哨兵有两个作用:

① 进人查找(插入位置)循环之前,它保存了a[i]的副本,使不致于因记录后移而丢失a[i]的内容;

② 它的主要作用是:在查找循环中"监视"下标变量j是否越界。一旦越界(即j=0),因为a[0].可以和自己比较,循环判定条件不成立使得查找循环结束,从而避免了在该循环内的每一次均要检测j是否越界(即省略了循环判定条件"j>=1")。

 for(i=2;i<n;i++)
    {
        a[0]=a[i];//哨兵
        j=i-1;
        while(a[0]<a[j])
        {
            a[j+1]=a[j];
            j--;
        }
        a[j + 1]=a[0];
    }

void pri_sort(int *b, int n)
{
    int i;
    for(i=0;i<n;i++)
    {
        if(i==n-1)
            printf("%d\n",b[i]);
        else
            printf("%d ",b[i]);
    }
}

时间复杂度:如果目标是把n个元素的序列升序排列,那么采用插入排序存在最好情况和最坏情况。最好情况就是,序列已经是升序排列了,在这种情况下,需要进行的比较操作需(n-1)次即可。最坏情况就是,序列是降序排列,那么此时需要进行的比较共有n(n-1)/2次。插入排序的赋值操作是比较操作的次数加上 (n-1)次。平均来说插入排序算法的时间复杂度为O(n^2)。

4.快速排序法(对冒泡排序的改进)

通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列

(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。 

(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。 

(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。 

(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。

在百度上找到的形象的模拟图

代码实现:

 
int quick_sort(int a[],int low,int high)
{
	int tmp=0,i,index,begin,end;
	if(low<high)
	{
		tmp=a[low];
		
		begin=low;
		
		end=high;
	
		while(low<high)
		{
			while(low<high&&a[high]>=tmp)
			{
				high--;
			}
			
			a[low]=a[high];
			
			while(low<high&&a[low]<=tmp)
			{
				low++;
			}
			
			a[high]=a[low];
		} 
		a[low]=tmp;
		index=low;
		quick_sort(a,begin,index-1);
		quick_sort(a,index+1,end);
	}

时间复杂度:

快速排序的一次划分算法从两头交替搜索,直到low和high重合,因此其时间复杂度是O(n);而整个快速排序算法的时间复杂度与划分的趟数有关。 

理想的情况是,每次划分所选择的中间数恰好将当前序列几乎等分,经过log2n趟划分,便可得到长度为1的子表。这样,整个算法的时间复杂度为O(nlog2n)。 

最坏的情况是,每次所选的中间数是当前序列中的最大或最小元素,这使得每次划分所得的子表中一个为空表,另一子表的长度为原表的长度-1。这样,长度为n的数据表的快速排序需要经过n趟划分,使得整个排序算法的时间复杂度为O(n2)。

5.桶排序

思想:桶排序的大体思路就是先将数组分到有限个桶中,再对每个桶中的数据进行排序,可以说是鸽巢排序的一种归纳结果(对每个桶中数据的排序可以是桶排序的递归,或其他算法,在桶中数据较少的时候用插入排序最为理想)。

待更新。。。

文献资料参考来源:

https://baike.baidu.com/item/%E5%86%92%E6%B3%A1%E6%8E%92%E5%BA%8F/4602306?fr=aladdin

https://baike.baidu.com/item/%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95/369842?fromtitle=%E5%BF%AB%E9%80%9F%E6%8E%92%E5%BA%8F&fromid=2084344&fr=aladdin#5

https://baike.baidu.com/item/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F/7214992?fr=aladdin#7

https://www.cnblogs.com/1328497946TS/p/11042804.html感谢这位大佬的博客讲解

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值