选择排序、堆排序

一、选择排序

【算法】每次从余下待排序记录中通过比较找到最小的,通过交换,放在待排序记录的首位。直到全部记录排好序。

具体如下:

(1)第1趟,从a[0..n-1]中找到最小的记录,与a[0]交换;

(2)第2趟,从a[1..n-1]中找到最小的记录,与a[1]交换;

……

(n-1)第n-1趟,从a[n-2..n-1]中找到最小的记录,与a[n-2]交换。

【稳定性】不稳定。

【时间复杂度】O(n^2)

不管什么情况,都需要做(n-1)+(n-2)+...+1=n*(n-1)/2次比较。是性能最差的排序方法。

【示例代码】

void selectsort(int n){
	for(int i=0;i<n-1;i++){
		int k=i;
		for(int j=i+1;j<n;j++)
			if(a[k]>a[j]) k=j;
		if(k!=i){//三行代码交换a[i]与a[k] 
			a[i]=a[i]+a[k];
			a[k]=a[i]-a[k];
			a[i]=a[i]-a[k];
		}
	}
}

二、堆排序

【堆的相关知识】

小根堆定义:小根堆是一棵完全二叉树,每个节点值小于等于它的儿子值(如果有),所以根结点一定是最小值,但左、右儿子大小不一定。

大根堆定义:大根堆是一棵完全二叉树,每个节点值大于等于它的儿子值(如果有),所以根结点一定是最大值,但左、右儿子大小不一定。

有三种操作需要掌握:

1、插入(put):

(1)在堆尾加入一个元素,并把这个结点作为当前结点。

(2)比较当前结点与它的父结点。

如果当前结点值小于父结点,则交换,并把父结点作为当前结点。转到(2)。

否则,结束。

2、读取并删除(get)(涉及向下调整):

(1)取出堆的根结点的值。

(2)把堆的最后一个结点放到根的位置上,堆长度减1。

(3)把根结点作为now结点。

(4)如果now结点没有子结点,是结束。否则,比较左、右儿子结点值(如果有右儿子),记小的为next结点。

(5)如果now结点值小于等于next结点值,则结束。否则,交换now结点与next结点,并把next结点作为now结点,回到(4)。

3、调整(adjust)(涉及向上调整):

(1)把最后一个元素作为now结点。

(2)如果 now结点是根结点,则结束。

(3)记now结点为son结点。

(4)如果son结点值小于它的父结点值,则交换,并将父结点作为son结点,如果新的son结点是根结点,则转到(5),否则回到(4)。

(5)将now结点前一个结点作为新的now结点,如果新的now结点是根结点,则结束,否则回到(4)。

【稳定性】不稳定。

【时间复杂度】O(nlogn)。最好、最坏情况区别在于有没有交换及比较次数有不同,但时间复杂度都是O(nlogn),

【示例代码】

1、边读记录边建小根堆,建好后,再边读取边输出。

#include<cstdio>
#include<algorithm>
using namespace std;
int n,d,heap[100],heap_size=0;
void heap_put(int d){//插入元素。涉及向上调整
	int now,next;
	heap[++heap_size]=d;
	now=heap_size;
	while(now>1){
		next=now>>1;
		if(heap[now]>=heap[next])break;
		swap(heap[now],heap[next]);
		now=next;
	}
}
int heap_get(){//读取堆根元素并删除,同时维护小根堆。涉及向下调整
	int now,next,res;
	res=heap[1];
	heap[1]=heap[heap_size--];
	now=1;
	while(now*2<=heap_size){
		next=now*2;
		if(next<heap_size&&heap[next+1]<heap[next])next++;
		if(heap[now]<=heap[next])break;
		swap(heap[now],heap[next]);
		now=next;
	}
	return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&d);
		heap_put(d);
	}
	for(int i=1;i<=n;i++) printf("%d ",heap_get());
    return 0;
}

2、用数组保存待排序记录后,调整成小根堆,再边读取边输出。

#include<cstdio>
#include<algorithm>
using namespace std;
int n,d,heap[100],heap_size=0;
void adjust_heap(int now){//涉及向上调整
	if(now*2>heap_size) return;//叶子结点则返回。也可以写成if(now>heap_size/2)return;
	int next;
	while(now*2<=heap_size){
		next=now*2;
		if(next<heap_size&&heap[next+1]<heap[next])next++;
		if(heap[now]<=heap[next])break;
		swap(heap[now],heap[next]);
		now=next;
	}
}
int heap_get(){//涉及向下调整
	int now,next,res;
	res=heap[1];
	heap[1]=heap[heap_size--];
	now=1;
	while(now*2<=heap_size){
		next=now*2;
		if(next<heap_size&&heap[next+1]<heap[next])next++;
		if(heap[now]<=heap[next])break;
		swap(heap[now],heap[next]);
		now=next;
	}
	return res;
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&heap[i]);
	heap_size=n;
	for(int i=n;i>=1;i--) adjust_heap(i);
	for(int i=1;i<=n;i++) printf("%d ",heap_get());
	return 0;
}

【参考文献】

1、https://blog.csdn.net/K346K346/article/details/50791102

2、c++信息学奥赛一本通

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值