[Algorithm_Learn_03]原地排序之堆排序

代码实现可到此处免费下载http://download.csdn.net/detail/elcarim/6513393


堆排序的核心是使用了最大(最小)堆这样一种数据结构,最大/最小堆可以用这样一句简单的话来描述:永远把最大(最小)的元素放在堆的顶部,子女一定比祖先小(大)。下面基于最大堆进行堆排序的讨论,即按升序排序。

1 原理说明

还是通过例子和图来解释:

规则. 最大堆的叶子节点必定集中在左边。


因此,对于这样一组数据{8,2, 5, 9, 7, 3, 7, 6},有如下图所示的堆结构。

步骤1. 建堆(Heap Build)

如上结构的堆并非一个最大堆,无法完成对数据的排序操作。因此我们首先需要建立一个最大二叉堆,方法如下图。


图(a)中绿色的的节点为非叶子节点,建堆的基本思想为:使用递归的方法将每个非叶子节点下的子堆转换为最大堆(该操作成为Heaptify)。

如图(b)所示,最后一个非叶子节点为图中黄色的节点9,其索引为4((数组长度/2)取下界,8/2取下界=4)。将虚线框内的子堆转换为最大堆(该子堆已经是最大堆,无需节点交换)。

图(c)~图(f),重复上述步骤,将整个堆转换为最大堆。

可以看到,通过建堆操作,可以获得一个最大堆,但是最大堆并不等于已经完成了排序工作。如上图最后所示,我们并没有得到一个按升序排列的数组。因此还需要进行下面的步骤。

步骤2. 排序(Heap Sort)

有了最大堆,进行排序其实非常简单。最大堆的堆顶(即树根)一定是最大的元素,因此排序的基本思想为:交换堆顶和堆尾部的元素,并将堆尾元素从堆中摘除。由于交换元素破坏了最大堆的性质,顾需要对堆顶进行一次Heaptify操作,是堆维持最大堆的性质。

如下图,最终我们可以得到按升序排列的数组。


2 代码实现

enum SortType{
	ST_ASC = 0,
	ST_DSC,
	ST_BUTT
};

class Heap{
private:
	int * m_data;
	int m_size;

public:
	int Sort(int type, int size, int * data);

private:
	void Bulid(int type);
	void Heaptify(int type, int index);
	void MaxHeaptify(int index);
	void MinHeaptify(int index);
	int LeftChild(int parent)
	{
		return parent << 1;
	}
	int RightChild(int parent)
	{
		return (parent << 1) + 1;
	}
};

int Heap::Sort(int type, int size, int * data)
{
	if (NULL == m_data || size <= 0)
	{
		printf("invalid data!");
		return FAILURE;
	}

	m_size = size;
	m_data = data;

	Bulid(type);
	for (int i = m_size; i >= 2; --i)
	{
		Swap(&m_data[0], &m_data[i - 1]);
		//PrintIntArray(15, m_data);
		--m_size;
		Heaptify(type, 1);
	}

	
	return SUCCESS;
}

void Heap::Bulid(int type)
{
	int last_inner = m_size >> 1;
	for (int i = last_inner; i >= 1; --i)
	{
		Heaptify(type, i);
	}
}

void Heap::Heaptify(int type, int index)
{
	switch (type)
	{
	case ST_ASC:
		MaxHeaptify(index);
		break;

	case ST_DSC:
		MinHeaptify(index);
		break;

	default:
		break;
	}
}

void Heap::MaxHeaptify(int index)
{
	int left  = LeftChild(index);
	int right = RightChild(index);

	int largest = index;
	if (left <= m_size && m_data[left - 1] > m_data[index - 1])
		largest = left;

	if (right <= m_size && m_data[right - 1] > m_data[largest - 1])
		largest = right;

	if (largest != index)
	{
		Swap(&m_data[index - 1], &m_data[largest - 1]);
		MaxHeaptify(largest);
	}
}

void Heap::MinHeaptify(int index)
{
	int left  = LeftChild(index);
	int right = RightChild(index);

	int smallest = index;
	if (left <= m_size && m_data[left - 1] < m_data[index - 1])
		smallest = left;

	if (right <= m_size && m_data[right - 1] < m_data[smallest - 1])
		smallest = right;

	if (smallest != index)
	{
		Swap(&m_data[index - 1], &m_data[smallest - 1]);
		MinHeaptify(smallest);
	}
}

3 问题讨论

Ø 与归并排序不同,堆排序是一种原地排序方法,即在排序过程中不需要额外申请内存,仅仅使用原有的数组空间即可。

Ø 堆排序不容易出现栈溢出的问题,因为Heaptify过程中,递归调用的最大深度为lgn(n为数组大小, 2^32 = 4,294,967,296)。

Ø 堆排序的基本原理和实现都不复杂,不过还是有很多东西值得深入研究的。这里只做最基本的讨论。下一次将讨论快速排序。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值