1. 数据结构中 堆 的基本概念
内容来自视频 https://www.bilibili.com/video/av31763085?from=search&seid=12254491620082291737
用途: 经常用堆做 优先队列 和 排序(全部插入,全部取出,排序完成)。
完全二叉树:依次排列,1-2-3-4-5-6-7-8-9-10-11-12, 中间都存在节点
不完全二叉树: 依次排列,1-2-3-4-5-6-7-8- * -10-11-12, 中间第9个节点缺失
完全二叉树可以用数组来完成,按照节点顺序依次存储在数组中。
堆分为大顶堆(最大堆)和小顶堆(最小堆)两种,不分左右子节点,只要都>或<父节点就行
2. 堆的一些操作, push, pop, top
对于最大堆
- 添加新节点(push):先把节点插入到最后,然后向上依次比较,满足则向上冒泡,否则停止。找到父节点的方法,
fatherIndex = ( childIndex - 1 ) / 2
- 删除根节点(pop):先把最后的节点放到根节点,然后依次和子节点比较,满足则向下冒泡,否则停止。
2.1 添加新节点流程(push):
2.1.1添加的是最大的数
2.1.2 添加的不是最大的数
2.2 删除根节点的流程(pop)
3. 代码
max_heap.h
#ifndef __MAX_HEAP_H
#define __MAX_HEAP_H
// 数据结构中的堆, 完全二叉树结构,用数组实现
// 这里以最大堆为例, 实现push 和 pop 和 top 三个操作
/*
== <--- top
==
== <--- current
==
*/
template<typename T>
void swap(T& a, T& b)
{
T t = b;
b = a;
a = t;
}
template<class T>
class maxHeap
{
public:
maxHeap(const int maxsize = 20)
: arryHeap(new T[maxsize]), maxSize(maxsize), current(0)
{ }
~maxHeap() { delete[] arryHeap; }
void Push(const T item); // 添加节点
void Pop(); // 删除根节点
T Top() const;
void show() const;
private:
void bubUp(); // 向上冒泡, 在插入节点时依次和 父节点比较
void bubDown(); // 向下冒泡, 在删除节点时依次和 子节点比较
T* arryHeap;
int maxSize;
int current;
};
template<class T>
void maxHeap<T>::Push(const T item)
{
if(current == maxSize) { std::cerr << "Heap has fulled.\n"; return; }
arryHeap[current] = item; // 先把数据放到最后
if(current) // 如果不是空的 才开始向上冒泡
{
bubUp();
}
current++; // 只要添加了节点就要++
}
template<class T>
void maxHeap<T>::bubUp()
{
int positin = current; // 从最末尾开始向上冒泡
int fatherP = (positin - 1) >> 1; // 获取父节点位置 position为1时fatherP为0 position为0时fatherP为-1
// 在当前节点不是根节点的前提下, 只要当前节点比父节点大 都要交换, 否则就停止,返回
while(fatherP >= 0 && arryHeap[positin] > arryHeap[fatherP])
{
swap(arryHeap[positin], arryHeap[fatherP]);
positin = fatherP; // 刷新 当前位置
fatherP = (positin - 1) >> 1; // 刷新 父节点位置
}
}
template<class T>
void maxHeap<T>::Pop()
{
if(!current) { std::cerr << "Heap is empty.\n"; return; }
// 如果完全二叉树不为空
arryHeap[0] = arryHeap[--current]; // 先把最后的节点 赋给 根节点, 然后丢弃根节点, ***current-1才是最后节点的数据***
bubDown();
}
template<class T>
void maxHeap<T>::bubDown()
{
int position = 0;
int lchild = (position << 1) + 1;
int rchild = lchild + 1;
while(lchild < current) // 只要存在左节点, 当只有根节点时不操作
{
// 如果比 左右子节点都小, 比较两个子节点大小, 然后跟大的交换
// (就算右节点不存在了,通过Top找不到,但是数据还算存在的,所以在比较时不会出错)
if(arryHeap[position] < arryHeap[lchild] && arryHeap[position] < arryHeap[rchild])
{
if(arryHeap[lchild] > arryHeap[rchild])
{
swap(arryHeap[position], arryHeap[lchild]);
position = lchild; // 记录向下冒泡的位置
}
else
{
swap(arryHeap[position], arryHeap[rchild]);
position = rchild; // 记录向下冒泡的位置
}
}
else if(arryHeap[position] < arryHeap[lchild]) // 只比左子节点的小
{
swap(arryHeap[position], arryHeap[lchild]);
position = lchild;
}
else if(arryHeap[position] < arryHeap[rchild]) // 只比右子节点小
{
swap(arryHeap[position], arryHeap[rchild]);
position = rchild;
}
else return; // 既不比左子节点小 也不必右小, 完成, 结束
lchild = (position << 1) + 1; // 继续查找
rchild = lchild + 1; // 继续查找 ***** rchild - lchild == 1 *******
}
}
template<class T>
T maxHeap<T>::Top() const // 返回根节点
{
if(!current)
{
std::cerr << "Heap is empty.\n";
return 9999;
}
return arryHeap[0];
}
template<class T>
void maxHeap<T>::show() const
{
int i = 0;
std::cout << "--------------------------\n";
while(i < current) std::cout << arryHeap[i++] << " ";
std::cout << "nums:" << current << std::endl;
std::cout << "--------------------------\n";
}
#endif
main.cpp
#include <iostream>
#include "max_heap.h"
using namespace std;
int main()
{
maxHeap<int> heap(30);
heap.Push(10);
heap.Push(5); // cout << heap.Top() << endl;
heap.Push(21); // cout << heap.Top() << endl;
heap.Push(55);
heap.Push(7);
heap.show();
heap.Pop(); heap.show();
heap.Pop(); heap.show();
heap.Pop(); heap.show();
heap.Pop(); heap.show();
heap.Pop();
// 用堆来 排序
maxHeap<int> heap1(100);
int a[10] = {2, 33, 44, 1, 3, 12, 54, -5, 22, 0};
for(int i = 0; i < 10; i++) heap1.Push(a[i]); // 压入堆
for(int i = 0; i < 10; i++)
{
a[i] = heap1.Top(); // 取出
heap1.Pop(); // 删除
}
for(int i = 0; i < 10; i++) cout << a[i] << " ";
cout << endl;
/* output:
--------------------------
55 21 10 5 7 nums:5
--------------------------
--------------------------
21 7 10 5 nums:4
--------------------------
--------------------------
10 7 5 nums:3
--------------------------
--------------------------
7 5 nums:2
--------------------------
--------------------------
5 nums:1
--------------------------
54 44 33 22 12 3 2 1 0 -5
*/
return 0;
}