小菜鸟的算法导论学习笔记——最大堆排序

        (二叉)堆是一个数组,可以被看成一个近似的完全二叉树。加入数的根节点是i(从1开始计算),那么它的父节点、左孩子和右孩子的下标则为i/2,2i,2i+1。需要注意,在编程语言中,一般是从0开始计算,因此此时某个根节点的父节点、左孩子和右孩子则为(i-1)/2,2i+1,2i+2。

        二叉堆可以分为最大堆和最小堆。最大堆是指除了根以外的所有节点均小于根节点,即最上层的节点是最大值,根节点一定大于其子节点。最小堆则反之。

        上述二者有着不同的用途:在堆排序算法中用最大堆;在构造优先队列中使用最小堆。本文介绍的是使用最大堆实现堆排序算法。

        堆排序算法集合了先前所介绍的归并排序与插入排序二者的优点。一方面,堆排序的时间复杂度与归并排序相同,为O(nlogn);另一方面,堆排序与插入排序一样具有空间原址性,即任何时候都只需要常数个额外元素空间存储临时的数据。

        在最大堆排序中,包含以下三种函数:

        1、max_heapify(A,i):维护堆,维护最大堆的性质。

                思路:从一个给定的下标i开始,让i节点符合最大堆的性质(在根节点和子结点中找出更大的值),逐级往下,直到子节点达到(或超过)堆元素的上限时停止。

        2、build_max_heap(A):建堆,自底向上构建一个堆。

                思路:一般从(n-1)/2(数组从0开始计数,此时该节点是从下往上的第一个根节点,往后全都是叶节点)开始到0,每次都执行一次维护堆函数。

        3、heapsort(A):堆排序算法。

                思路:完成建堆后,从最右下角的叶节点开始,与0号根节点交换位置。此时最右下角的元素是最大的元素,将其删除(pop),对0号根节点使用维护堆函数,新的0号根节点又是最大的数。一直循环直到只剩一个元素为止,此时将删除的元素排列起来,即为从大到小的排序。

        实现代码如下:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

//维护堆:维护最大堆的性质
void max_heapify(vector<int> &A,int i) {
	int l = 2 * (i+1)-1;
	int r = 2 * (i+1);
	int largest;
	if (l < A.size() && A[l] > A[i])
	{
		largest = l;
	}
	else largest = i;
	if (r < A.size() && A[r] > A[largest])
	{
		largest = r;
	}
	if (largest != i)
	{
		int temp;
		temp = A[i];
		A[i] = A[largest];
		A[largest] = temp;
		max_heapify(A, largest);
	}
}

//建堆,从下向上第一个非叶节点开始,对堆进行进行维护
void build_max_heap(vector<int>& A)
{
	int heap_size = A.size();
	for (int i = (heap_size-1) / 2; i >= 0; --i)
	{
		max_heapify(A, i);
	}
}

//堆排序算法
void heapsort(vector<int>& A, vector<int>& B)
{
	build_max_heap(A);
	int heapsize = A.size();
	for (int i = heapsize - 1; i >= 1; --i)
	{
		int temp;
	    temp = A[0];
		A[0] = A[i];
		A[i] = temp;
		B.push_back(A[i]);
		A.pop_back();
		max_heapify(A, 0);
	}
	B.push_back(A[0]);
}

int main()
{
	int num;
	cout << "输入子数组的元素数目" << endl;
	cin >> num;
	vector<int> vector_test(num);
	vector<int> answer;
	cout << "依次输入子数组的各元素" << endl;
	for (int i = 0; i < num; ++i)
	{
		cin >> vector_test[i];
	}
	cout << "以下是堆排序算法后的结果" << endl;
	heapsort(vector_test,answer);
	for (int i = 0; i < answer.size(); ++i) { cout << answer[i] << endl; }
	return 0;
}


注意事项:

        1、维护堆的if语句中要先判断l或者r是否超出A的范围,再进行两元素的大小比较,否则会因为内存溢出的现象,导致不存在A[l/r]而报错!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值