排序算法

Posted cl0ud

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了排序算法相关的知识,希望对你有一定的参考价值。

学习数据结构和算法相关的知识,参考的书籍是《啊哈!算法》这本书籍,很多算法的书籍只适合当做工具书来查阅,(例如我的算法导论,现在还在垫桌角),所以找到一本简单易懂的算法书籍是非常重要的,当然很多算法还有深入的部分,这些都可以在自己心里面有了算法的基本概念之后再看工具书进行扩展的学习。

书里面的排序算法讲了三种:简易版桶排序(在本文后面我们都简称为桶排序),冒泡排序,快速排序。

首先的问题条件是让计算机随机读入五个数字之后将这五个数字从大到小进行输出,数字的范围是0–>10分。桶排序在这里的思想是利用大小为11的一维数组储存用户输入的数字,将数字大小所对应的下标,即1对应的一维数组就是a[1],6对应的一维数组就是a[6],输入相应的数字就将数组相应+1,然后我们想要从小到大或者从大到小都可以轻易做到了。

#include<stdio.h>
int num[11];
int main(){
	int n,temp;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&temp);
		num[temp]+=1;
	}
	//从小到大 
	for(int i=1;i<=10;i++){
		for(int j=0;j<num[i];j++){
			printf("%d ",i);
		}
	}
	printf("
");
	//从大到小 
	for(int i=10;i>=1;i--){
		for(int j=0;j<num[i];j++){
			printf("%d ",i);
		}
	}
	return 0;
}

桶排序的是非常快的排序算法,但是它牺牲了空间来交换时间,想象一下我们需要输入的五个数字的范围是0–>100000000,虽然还是五个数字,但是却需要数组的空间为100000000,在空间上造成了极大的浪费。

第二个排序算法是冒泡排序,冒泡排序的基本思想是:每次比较两个相邻的元素,如果他们的顺序错误就把他们交换过来。

简单来说,从小到大排序,我们从左边第一个数字开始,跟旁边的数字进行比较,如果比右边的数字大,我们就让这两个数字进行交换。接着我们比较第二个和第三个数字,将其中更大的一个数字交换到两个数字的右边去,这样一轮循环下来,就会将五个数字中最大的那个数字放在数组的最右边去。

然后我们进行第二轮循环,这次比较的范围是第一个数字到第四个数字(因为第五个数字已经是最大的了,他就没有再进行比较的必要),在这一轮比较之后,我们将数组中第二大的数字放在了数组的倒数第二个位置,以此类推,最终排出正确的顺序。

下面是实现的代码;

#include<stdio.h>
int num[100];
struct student{
	char name[30];
	int source;
};
//添加结构体之后可以对其进行成绩和姓名的同时排序 
int main(){
	struct student a[100];
	int n,temp;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&a[i].source);
	}
	//对于n个数字来说就需要有n-1次的比较
	//每次比较了一次之后就会找到相对大的数字 
	for(int i=0;i<n-1;i++){
		for(int j=0;j<n-i-1;j++){
			if(a[j].source>a[j+1].source){
				//这里的if决定是从大到小还是从小到大 
				temp=a[j].source;
				a[j].source=a[j+1].source;
				a[j+1].source=temp;
			}
		}
	}
	for(int i=0;i<n;i++){
		printf("%d ",a[i].source);
	}
	return 0;
}

冒泡排序的核心是双重嵌套循环,而其冒泡排序的时间复杂度是O(N^2),虽然空间上占了优势,但是时间复杂度又比较高。

这个时候大家就会想有没有在空间和时间上都比较有优势的算法呢,这就是我们最后要讲到的快速排序。

快速排序的原理是将最左边的数字定为基准数,然后从数组最右端先开始寻找比基准数小的数字,找到了之后从最左端寻找比基准数大的数字,若两者没有相遇,则将两数字进行交换,接着继续进行寻找,直到两者相遇,则代表在基准数视角下左右两边排序已成功,接着我们将基准数回归到两者相遇位置,对于基准数左边和右边的数字分别进行排序,也是按照刚才相同的思想。

而快速排序的最坏的结果就是左右两端的数字每寻找一次就要进行交换,这样的话就跟冒泡排序是一样的了,但实际上不会这么倒霉的,所以它的平均时间复杂度为O(NlogN),因为它相对于冒泡排序,少了交换的那部分时间。而很明显在我们进行第二次排序的时候,将原来的数字序列分成了两个新的数字序列进行排序,这里又运用到了一种叫做二分的思想。

上代码

#include<stdio.h>
int num[101];
void quicksort(int left,int right){
	int i,j,t;
	int temp=num[left];
	if(left>right){
		return ;
	}
	i=left;
	j=right;
	while(i!=j){
		while(num[j]>=temp&&i<j){
			j--;
		}
		while(num[i]<=temp&&i<j){
			i++;
		}
		if(i<j){
			t=num[i];
			num[i]=num[j];
			num[j]=t;
		}
		
	}
	num[left]=num[i];
	num[i]=temp;
	quicksort(left,i-1);
	quicksort(i+1,right);
}
int main(){
	int n;
	scanf("%d",&n);
	for(int i=0;i<n;i++){
		scanf("%d",&num[i]);
	}
	quicksort(0,n-1);
	for(int i=0;i<n;i++){
		printf("%d ",num[i]);
	}
	return 0;
}

最后,书上第一章最后还有一个练习的习题,我直接复制过来了:

 小哼的学校要建立一个图书角,老师派小哼去找一些同学做调查,看看同学们都喜欢读 
哪些书。小哼让每个同学写出一个自己最想读的书的 ISBN 号(你知道吗?每本书都有唯一 
的 ISBN 号,不信的话你去找本书翻到背面看看)。当然有一些好书会有很多同学都喜欢, 
这样就会收集到很多重复的 ISBN 号。小哼需要去掉其中重复的 ISBN 号,即每个 ISBN 号只 
保留一个,也就说同样的书只买一本(学校真是够抠门的)。然后再把这些 ISBN 号从小到 
大排序,小哼将按照排序好的 ISBN 号去书店买书。请你协助小哼完成“去重”与“排序” 
的工作。 
输入有 2 行,第 1 行为一个正整数,表示有 n 个同学参与调查(n≤100)。第 2 行有 n 
个用空格隔开的正整数,为每本图书的 ISBN 号(假设图书的 ISBN 号在 1~1000 之间)。 
输出也是 2 行,第 1 行为一个正整数 k,表示需要买多少本书。第 2 行为 k 个用空格隔 
开的正整数,为从小到大已排好序的需要购买的图书的 ISBN 号。 
例如输入: 
10  
20 40 32 67 40 20 89 300 400 15  
则输出: 
8  
15 20 32 40 67 89 300 400  

 

大家做了之后可以在添柴-编程学习网 进行验证,书上的链接已经改了:

https://www.acoj.com/problems/12001

 

 

下面是AC代码,我回车换行那里没有进行处理。

#include<stdio.h>
int book[1001];
int main(){
	int n;
	int count=0;
	int temp;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&temp);
		book[temp]+=1;
	}
	int i=1;
	while(i<1001){
		if(book[i]){
			count++;
		}
		i++;
	}
	printf("%d
",count);
	for(int i=1;i<1001;i++){
		if(book[i]){
			printf("%d ",i);
		}
	}
	printf("
");
	return 0;
}

 

冒泡排序

#include<stdio.h>
int book[1001];
int main(){
	int n;
	int count=0;
	int temp;
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&book[i]);
	}
	for(int i=1;i<n;i++){
		for(int j=1;j<=n-i;j++){
			if(book[j]>book[j+1]){
				temp=book[j];
				book[j]=book[j+1];
				book[j+1]=temp;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(book[i]!=book[i-1]){
			count++;
		}
	}
	printf("%d
",count);
	for(int i=1;i<=n;i++){
		if(book[i]!=book[i-1]){
			printf("%d ",book[i]);
		}
	}
	return 0;
	
}

以上是关于排序算法的主要内容,如果未能解决你的问题,请参考以下文章

算法排序之堆排序

快速排序-递归实现

从搜索文档中查找最小片段的算法?

在第6731次释放指针后双重免费或损坏

TimSort算法分析

以下代码片段的算法复杂度