选择排序(简单选择排序,堆排序)

简单选择排序

基本思想:每一趟(第i趟)都选择数组后(n-i+1)个待排元素中最小的那个,放到位置i上。一共执行n-1趟。
code:

void s_sort(int q[]){
	int minn;
	for(int i=1;i<=n;i++){
		minn = i;
		for(int j=i+1;j<=n;j++){
			if(q[minn]>q[j]) minn = j;	
		}
		if(minn != i) swap(q[minn],q[i]);
	}
}

算法分析:
空间复杂度:O(1)
时间复杂度:O(n^2)
稳定性:不稳定。

堆排序

https://zhuanlan.zhihu.com/p/128892381
堆分为大根堆和小根堆;
大根堆就是对于一棵完全二叉树,每个节点的值都大于等于其儿子节点的值。
即(arr[i] >=max(arr[i],arr[i+1]) 反过来就是小根堆。
堆排序的基本思想(以升序为例)
  首先要建立一个初始大根堆,这样就获得了当前序列的最大值,然后把它和最后边的那个元素(arr[n])交换一下,交换后初始大根堆可能被破坏了,再对前(n-1)个元素重新维护一个大根堆。再把根节点和arr[n-1] 交换一下…。
初始大根堆的建立:
一个长度为n的数组对应一个完全二叉树(下标以1开始),i = n/2就是该二叉树最后一个非叶子节点。i2和i2+1就是该节点对应的左右儿子节点。


void headajust(int s,int m){
	//s表示当前子树的根节点下标。m表示数组长度。 
	for(int j=2*s;j<=m;j*=2){ //j表示以当前子树为根的左儿子。 
		if(j<m&&arr[j]<arr[j+1]) j++;//找出左右儿子较大的那个,注意加一后不要越界。 
		if(arr[s]>=arr[j]) break; //如果说根节点的值比儿子节点的值大,说明当前子树就是一个堆,
								  //直接break就好了。 
		swap(arr[s],arr[j]);//来到这一步说明根节点比儿子节点大,交换一下,
							//这么一交换可能会把我们之前维护好的堆结构给破坏掉,
		s=j;				//所以再以j为根,重新维护一个堆结构。 
	}
}
//构造初始堆结构,从低到高对每一个非叶子节点构造堆结构。
void creathead(){
	for(int i=n/2;i>=1;i--){
		headajust(i,n);
	}
}
void heapsort(){
	creathead();
	for(int i=n;i>=2;i--){
		swap(arr[1],arr[i]);
		heapjust(1,i-1);
	}
}

对headadjust的一个小小的优化操作
在上一个代码中,我们发现arr[s]>=arr[j]这个arr[s]一直都是一个值,然后rc来保存一下,每次只是把arr[j]赋值给当前根节点。退出循环后再把rc赋给arr[s],因为rc本来就是当前根节点的值,s又是当前根节点。

void heapjust(int s,int m){
	int rc = arr[s];
	for(int j=2*s;j<=m;j*=2){
		if(j<m&&arr[j]<arr[j+1]) j++;
		if(rc>=arr[j]) break;
		arr[s] = arr[j];
		s=j;
	}
	arr[s] = rc;
}

算法分析:
空间复杂度:O(1)
时间复杂度:O(nlog2n)
不稳定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值