文章目录
- 一 什么是堆
- 二 堆排序基本思想
- 三 代码实现
- 拓展 c++实现优先级队列
一· 什么是堆?
1 堆的定义
堆是一种叫做完全二叉树的数据结构,可以分为大根堆和小根堆(本文使用小根堆完成堆排序)
2 小根堆的定义
图示:
每个结点的值都小于或等于其左右孩子的值,称为最小堆。每个结点的值都大于其左右孩子的值,称为最大堆。
3 规则
如果有一棵有n个结点的完全二叉树自顶向下,同一层自左向右连续编号为0,1,2,...,n-1,然后按此结点编号将树中各结点顺序的存放在顺序表中(数组)编号为i的结点有一些规则:
(1)若i==0,则i为根结点;若i>0,则i的双亲为(i-1)/2.
(2)若2*i+1<n,则i的左子女为2*i+1;若2*i+2<n,则i的左子女为2*i+2.
(3)若i为偶数且i!=0,则其左兄弟为i-1,;若i为奇数且i!=n-1,则其右兄弟为i+1.
4 物理存储结构
9 | 17 | 65 | 23 | 45 | 78 | 87 | 53 | 31 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
二 堆排序基本思想
1 堆排序基本思想(本文为降序排序)
(1)将待排序序列构造为最小堆,此时根节点为整个序列最小值;
(2)将根节点与末尾元素交换,此时末尾为最小值;
(3)接着将剩下n-1个元素重新构造成一个最小堆,可以得到剩下n-1个元素最小值,反复执行此步骤即可完成降序排序.
2 构造小根堆
给出如下数组
53 | 17 | 78 | 9 | 45 | 65 | 87 | 23 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
根据上面的规则我们可以将其看为如图的一个堆:
从最后一个非叶子结点即结点3开始,我们比较结点3的值和他左右孩子的大小,如果结点3的值小于等于其左右孩子,那么不变,否则我们将左右孩子中较小的和结点3的值交换。
调整函数如下:
void FilterDown(int* nums, int start, int end)
{
int i = start, j = i * 2 + 1;
int tmp = nums[i];
while (j <= end)
{
if (j < end && nums[j] > nums[j + 1]) j += 1;
if (tmp <= nums[j]) break;
nums[i]=nums[j];
i = j;
j = i * 2 + 1;
}
nums[i] = tmp;
}
然后找到结点3上一个结点,递归此过程,得到的就是最小堆,接下来只需交换堆根和堆末尾元素,并重复此过程,排序就完成.
三 代码实现
void FilterDown(int* nums, int start, int end)
{
int i = start, j = i * 2 + 1;
int tmp = nums[i];
while (j <= end)
{
if (j < end && nums[j] > nums[j + 1]) j += 1;
if (tmp <= nums[j]) break;
nums[i]=nums[j];
i = j;
j = i * 2 + 1;
}
nums[i] = tmp;
}
void HeapSort(int* nums, int n)
{
if (nullptr == nums || n < 2) return;
int pos = (n - 2) / 2;
while (pos >= 0)
{
FilterDown(nums, pos, n - 1);
--pos;
}
pos = n - 1;
while (pos > 1)
{
swap(nums[0], nums[pos]);
--pos;
FilterDown(nums, 0, pos);
}
}
int main()
{
int ar[] = { 53,17,78,9,45,65,87,23,};
int len = sizeof(ar) / sizeof(ar[0]);
HeapSort(ar, len);
for (int i = 0; i < len; ++i)
{
cout << ar[i] << " ";
}
cout << endl;
}
运行结果如下
拓展:c++实现优先级队列
我们都知道队列是一种先进先出的数据类型,但是优先级队列更像一个堆,它不满足先进先出地条件,优先级队列每次出队的元素是队列中优先级最高的那个元素,而不是队首的元素。这个优先级可以通过元素的大小等进行定义,比如数据越小优先级越高,那么先出的就是队列中最小的元素。
class MinHeap
{
private:
int* data;
int cursize;
int maxsize;
public:
static void FilterDown(int* nums, int start, int end)
{
int i = start, j = i * 2 + 1;
int tmp = nums[i];
while (j <= end)
{
if (j < end && nums[j] > nums[j + 1]) j += 1;
if (tmp <= nums[j]) break;
nums[i] = nums[j];
i = j;
j = i * 2 + 1;
}
nums[i] = tmp;
}
static void FilterUp(int* nums, int start)
{
int j = start, i = (j - 1) / 2;
int tmp = nums[j];
while (j > 0)
{
if (nums[i] <= tmp) break;
nums[j] = nums[i];
j = i;
i = (i - 1) / 2;
}
nums[j] = tmp;
}
public:
MinHeap(int sz = 100)
{
cursize = 0;
maxsize = sz;
data =new int[maxsize];
}
~MinHeap()
{
delete[]data;
data = nullptr;
cursize = 0;
maxsize = 0;
}
int getsize()const { return cursize; }
bool IsEmpty()const { return getsize() == 0; }
bool IsFull()const { return getsize() == maxsize;}
void clear() { cursize = 0; }
bool push(const int& val)
{
if (IsFull()) return false;
data[cursize] = val;
cursize += 1;
FilterUp(data, cursize - 1);
return true;
}
bool remove(int& val)
{
if (IsEmpty()) return false;
val = data[0];
data[0] = data[cursize - 1];
cursize -= 1;
FilterDown(data, 0, cursize - 1);
return true;
}
};
int main()
{
MinHeap mh(100);
mh.push(12);
mh.push(34);
mh.push(25);
mh.push(8);
mh.push(56);
while (!mh.IsEmpty())
{
int x = 0;
mh.remove(x);
printf("%5d\n", x);
}
return 0;
}
结果如下:
感谢观看!