堆排序

概念:

父结点、子结点、叶子结点、非叶子结点

1、堆分为:(完全二叉树)

(1)大根堆:在整个堆中,左子树、右子树的数据都比父结点的数据小。

(2)小根堆:在整个堆中,左子树、右子树的数据都比父结点的数据大。

2、给出父结点n求子结点

左子树:2*n+1

右子树:2*(n+1)

3、给出子结点n求父结点

(n-1)/2

举例及思想、步骤:

举例:生序排列-调整成大根堆,过程如下: (一般升序采用大根堆,降序采用小根堆)

(1)将初始待排序关键字序列(R1,R2....Rn)构建成大顶堆,此堆为初始的无序区

(2)初始化大根堆:

从最后一个非叶子结点开始从下至上进行调整。

比较大小:从父节点、左孩子节点、右孩子节点三者中选择最大者 跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,∴交换后哪一路分支变化了,就继续调整,这时是从顶点开始往下调整)。

有了初始堆之后就可以进行排序了。

(3)将堆顶元素R[1]与末尾元素R[n]交换,将最大元素"沉"到数组末端,交换后堆长度减1,此时得到新的无序区(R1,R2,......Rn-1)和新的有序区(Rn)

(4)由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,......Rn-1)调整为新堆,

然后再次将堆顶元素R[1]与当前无序区最后一个元素交换,得到新的无序区(R1,R2....Rn-2)和新的有序区(Rn-1,Rn)。

不断重复此“调整+交换”过程直到有序区的元素个数为n-1,则整个排序过程完成

 

代码实现:

void headAdjust(int arr[], int fNode, int pos)
//		    数据    开始的父结点  最多改的下标值(边界控制)
{
	for (int maxNode = 2 * fNode + 1; maxNode <= pos; maxNode = 2 * maxNode + 1)
      //左右结点中较大的结点 左子结点				下一个左子结点
	{
		if (maxNode < pos && arr[maxNode] < arr[maxNode + 1])
			//判断右子结点	 左<右子结点的值
		{
			maxNode++;
			//较大,结点改为右子结点
		}
		//左、右子结点中较大的<父结点的值,不交换值
		if (arr[maxNode] <= arr[fNode])break;
		//存在左、右子结点中较大的>父结点的值, 交换maxNode
		int tmp = arr[maxNode];
		arr[maxNode] = arr[fNode];
		arr[fNode] = tmp;

		fNode = maxNode;//赋新的父结点
	}
}
void heapSort(int arr[], int len)
{
	int index = len - 1;//最后的结点编号
	for (int i = (index - 1) / 2; i >= 0; --i)
		//i 最后一个父结点,从非叶子结点开始,从下往上调整
	{
		headAdjust(arr, i, index);
		/*index
			精确:调整到最后一个叶子结点
			粗:index/len-1∵最后一个叶子结点就算有子结点,值也>len-1
		*/
	}
	for (int j = index; j >= 0; --j)
	{
		//待排序列与arr[0]交换
		int tmp = arr[0];
		arr[0] = arr[j];
		arr[j] = tmp;
		headAdjust(arr, 0, j - 1);
							//len-1-i-1
	}
}
void heapShow(int arr[], int len)
{
	for (int i = 0; i < len; ++i)
	{
		printf("%d ", arr[i]);
	}
	printf("\n");
}
int main()
{
	int arr[] = {26,1,56,6,5,69,2,37,88,42};
	int len = sizeof(arr) / sizeof(arr[0]);
	heapShow(arr, len);
	heapSort(arr, len);
	heapShow(arr, len);
	return 0;
}

总结:

时间复杂度:平均O(nlog2n),最好O(nlog2n),最坏O(nlog2n)

空间复杂度:O(1)

不稳定(当有跳跃的交换时,∵ 父子交换数据)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值