您的位置:首页 > 理论基础 > 数据结构算法

数据结构之堆和堆排序

2017-08-07 19:13 344 查看

堆的概念

如果有一个数的集合,把它的所有元素按完全二叉树额顺序存储方式存储在一个一维数组中,称为堆。
最小堆:任一结点的的值均小于它的左右孩子的值,位于堆顶结点的值最小。
最大堆:任一结点的的值均大于它的左右孩子的值,位于堆顶结点的值最大。



观察图中大堆和小堆。小堆:从根节点到任何一个叶子结点都是递减的。大堆:从根节点到任何一个叶子结点都是递增的。

创建堆

一般都用数组来表示堆,i 结点的父结点下标就为(i-1)/2,它的左右子结点下标分别为2*1+1和2*1+2。比如第0个结点的左右子结点下标分别是1和2。

创建思路:

先给一个普通的完全二叉树,然后进行调整,以大堆为例:
1、设堆的大小是Size,从结点(Size-1)/2开始调整,将每一棵子树都调整成一棵最大堆结构。
2、取该结点的左右孩子中较大的结点和父结点比较,如果比父结点大就交换,否则该子树符合大堆的要求。
3、取该结点的前一个结点,也就是下标减一,然后继续进行第2步。
4、检测每次调整是否会影响调整过的子树不符合大堆的要求,如果影响则需继续调整该子树。直到整体符合大堆要求为止。

void Adjustdown(int *array, size_t root, size_t size)
{
size_t parent = root;//最后一个非叶子节点
size_t child = root * 2 + 1;//该节点的左孩子
while (child < size)//孩子存在
{
if (child + 1 < size && array[child] > array[child + 1])//左右孩子比较大小
{
child += 1;//标记较大的孩子
}
if (array[parent] > array[child])//孩子小于双亲
{
std::swap(array[child], array[parent]);//孩子与双亲交换
parent = child;//重新标记双亲节点
child = child * 2 + 1;
}
else{
break;
}
}
}

堆排序

要进行堆排序,首先要创建堆。按递减序列排序时,要创建最大堆。

然后循环执行如下过程,直到数组为空:

1、把堆顶array[0]元素和当前最大堆的最后一个元素交换。

2、最大堆元素个数减1。

3、由于第1步后根节点不再满足最大堆定义,向下调整跟结点。

把一棵完成二叉树调整为堆,以及每次堆顶元素交换后进行调整的时间复杂度均为O(lg(n)),所以堆排序的时间复杂度为O(n*lg(n))。堆排序是一种不稳定的排序算法。

void HeapSort(int *array, size_t size)
{
for (int idx = (size - 2) / 2; idx >= 0; --idx)//创建堆
{
Adjustdown(array, idx, size);
}
int index = size - 1;//堆最后一个节点
while (index > 0)
{
std::swap(array[0], array[index]);
Adjustdown(array, 0, index);
index--;
}
for (int i = 0; i < 8; ++i)
{
cout << array[i] << " ";
}
cout << endl;
}
测试用例 

int main()
{
int array[8] = { 53, 17, 78, 9, 45, 65, 87, 23 };
HeapSort(array, 8);
system("pause");
return 0;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: