详细解释堆排序流程

掌握堆排序的主要步骤分为两个点,首先第一步要学会如何建堆(大根堆或者小根堆,此处我以大根堆为例),第二步如何将一个建好的堆进行排序。

一、建堆

建大根堆

void heap_Insert(vector<int>& heap, int index) {
	while (heap[index] > heap[(index - 1) / 2]) {//如果孩子比父亲大
		Swap(heap, index, (index - 1) / 2);//交换父子节点
		index = (index - 1) / 2;//更新子节点,继续往上和父亲比
	}
}

核心思想就是给你一个无序数组,假设用户每次只给你一个数,每次都把这个数插入到堆中,使其成为大根堆。所以,每次新来的数都要去和自己的父亲比较,如果,我比父亲大,则我和父亲交换,我作为新的子节点,继续往上和父亲交换,直到我成为最大的根节点。比如给你一个数组[1,3,2],用户给的第一个数是1,它作为根节点,然后第二个数为3,它比父亲节点1大,所以交换,然后3作为新的根节点,接着又来了2,2的父亲是3,比3小,所以不用交换。所以最后变成[3,1,2]大根堆的形式。

二、heapify-堆排序关键

现在假设给你一个大根堆,且大小为s。你如何对他进行排序呢?思路是这样:因为是大根堆,所以数组的第一个元素必然是最大的,因此,我们将首尾元素交换,那么交换一次,则最大的元素就跑到数组的最后面了,然后我们对前s-1个数调堆(因为交换一次后,前s-1个数就不满足大根堆了),再调成大根堆,然后再重复交换->调堆->交换->调堆.....最后我们将实现数组的有序排列。这应该是很好理解吧。上面这一大段话其实就是heapify的操作。下面代码展示如何实现这一操作

void heapify(vector<int>& heap,int index,int heapsize) {
	int left = 2 * index + 1;//左孩子
	while (left<heapsize) {
		//选取左右孩子中最大的那个,所以得和右孩子比较
		//1.得有有孩子 2.右孩子比左孩子大 
		int max = left + 1 < heapsize && heap[left + 1] > heap[left] ? left + 1 : left;
		//再将左右孩子中大的那个和父亲节点比较
		max = heap[index] > heap[max] ? index : max;

		if (max == index) break;//如果max等于index,即说明父亲比孩子都大,所以,不需要调堆
		//不break,说明有孩子比父亲大,所以需要交换
		Swap(heap, index, max);
		//继续往下看
		index = max;
		left = 2 * index + 1;
	}

}

它的核心思想就是,从自己的左右孩子中找出大的那个作为根节点,交换父亲和较大孩子,再更新新的index,继续往下找较大的孩子,依次类推实现调堆。看代码中的注释应该也能理解。

三、排序

在理解上述两步之后,你应该能明白如何进行堆排序了,第一步建堆,第二步heapify即可。

下面给出完整代码

#include<iostream>
#include<vector>
using namespace std;
void heap_Insert(vector<int>&, int);
void Swap(vector<int>&, int, int);
void heapify(vector<int>&, int, int);
void heapSort(vector<int>& );
//建大根堆
void heap_Insert(vector<int>& heap, int index) {
	while (heap[index] > heap[(index - 1) / 2]) {
		Swap(heap, index, (index - 1) / 2);
		index = (index - 1) / 2;
	}
}
void Swap(vector<int>& heap, int i, int j)
{
	int temp = heap[i];
	heap[i] = heap[j];
	heap[j] = temp;
}
//heapify
void heapify(vector<int>& heap,int index,int heapsize) {
	int left = 2 * index + 1;//左孩子
	while (left<heapsize) {
		//选取左右孩子中最大的那个,所以得和右孩子比较
		//1.得有有孩子 2.右孩子比左孩子大 
		int max = left + 1 < heapsize && heap[left + 1] > heap[left] ? left + 1 : left;
		//再将左右孩子中大的那个和父亲节点比较
		max = heap[index] > heap[max] ? index : max;

		if (max == index) break;//如果max等于index,即说明父亲比孩子都大,所以,不需要调堆
		//不break,说明有孩子比父亲大,所以需要交换
		Swap(heap, index, max);
		//继续往下看
		index = max;
		left = 2 * index + 1;
	}

}
void heapSort(vector<int>& heap) {
	
	if (heap.size()< 2) return;
	//首先,建一个大根堆
	for (int i = 0; i <heap.size(); i++) {
		heap_Insert(heap, i);
	}
	//int lastnode = heap.size() - 1;
	//int parent = (lastnode - 1) / 2;
	//for (int i = parent; i >= 0; i--) {
	//	heapify(heap, i, heap.size());
	//}这是建堆的优化操作,就是从叶子节点的孩子开始从下往上heapify也可以实现调堆,并且时间会比heap_Insert优化一点

	//建好堆后,交换首尾数字,然后每次只对【去掉最后一个元素的数组】heapify,也就是每次heapsize要减一
	for (int i = heap.size()-1; i >0; i--) {//这里heapsize始终和i一直,所以也就同时执行了heapsize-1的操作
		Swap(heap, 0, i);
		heapify(heap, 0, i);
	}

	

}

int main() {
	vector<int> data;
	data.push_back(1);
	data.push_back(4);
	data.push_back(3);
	data.push_back(5);
	data.push_back(2);
	//data.push_back(6);
	//data.push_back(8);
	
	//for (int i = 0; i < data.size(); i++) {
	//	heap_Insert(data, i);
	//}
	heapSort(data);
	//int size = (int)data.size();
	for (int i = 0; i < data.size(); i++) {
		cout << data[i] << " ";
		cout << endl;
	}
	
	system("pause");
	return 0;
}

输出:

1
2
3
4
5

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值