堆
- 二叉堆(Binary Heap)
- 二叉堆是一颗完全二叉树
- 堆中某个节点的值总是大于等于(或小于等于)其子节点, 对应的就是最大堆和最小堆
可以用数组存储二叉堆,数组以1下标开始:
数组以下标0开始:
操作
添加元素
- 在数组的最后一个位置添加一个新元素
- 新的元素进行上浮(Sift Up), 上浮操作如下图:
取出元素
堆每次只能取出最大的元素, 具体取出元素的步骤如下 :
- 将堆的根节点与最后一个元素交换位置
- 删除最后的一个元素, 此时最后一个元素位于根节点
- 如果左右子节点不小于现在的根节点, 则将现有根节点和左右子节点中较大的那个交换位置
- 重复上一步, 直到不再需要最后的那个节点找到它应该存放的位置, 示意图如下:
初始化最大值堆
- 首先将数组当成一个完全二叉树
- 找到最后一个非叶子节点, 方法是根据最后一个元素计算其父节点
- 从最后一个非叶子节点之前的节点都执行Sift Down操作
C++代码实现
//构建最大值堆 堆排序
// 使用动态数组实现最大值堆,这里使用vector实现
// 数组下标以1开始时,父节点parent(i)=i/2 左孩子=2*i 右孩子=2*i+1
// 数组下标以0开始时,父节点parent(i)=(i-1)/2 左孩子=2*i+1 右孩子=2*i+2
// 这里使用下标0开始
class MaxHeap{
private:
vector<int> data; //动态数组保存堆元素
int getParent(int index); //返回下标index节点的父节点
int getLeftChild(int index);// 返回下标为index节点的左孩子
int getRightChild(int index);// 返回下标为index节点的右孩子
void siftUP(int index); // 下标为index节点上浮
void siftDown(int index); // 节点下沉
public:
MaxHeap(vector<int>& nums);
void insert(int value); // 向最大堆插入元素
int pop(); // 取出最大元素
bool empty(); // 最大值堆是否为空
void printHeap();
};
// 默认构建最大值堆,找到最后一个非叶子节点(最后一个节点父节点 getParent(data.size() - 1))
// 然后从最后一个非叶节点之前节点都执行下沉操作,则可以保证具有最大值性质
MaxHeap::MaxHeap(vector<int>& nums){
for(int val : nums){
data.push_back(val);
}
for(int i = getParent(data.size() - 1); i >= 0; --i){
siftDown(i);
}
}
int MaxHeap::getParent(int index){
if(index == 0){
cout<<"下标为0的节点不存在父节点!!!"<<endl;
return -1;
}
return (index - 1) / 2;
}
int MaxHeap::getLeftChild(int index){
return 2 * index + 1;
}
int MaxHeap::getRightChild(int index){
return 2 * index + 2;
}
bool MaxHeap::empty(){
return data.empty();
}
//向最大值堆插入元素:在数组最后一个位置添加新元素,然后新元素进行上浮
void MaxHeap::insert(int value){
data.push_back(value);
siftUP(data.size() - 1);
}
// 取出元素,每次只能取出最大元素:
// 将堆最后一个节点和根节点交换位置
// 删除最后一个元素,即删除最大元素
// 然后将根节点进行下沉
int MaxHeap::pop(){
if(empty()){
cout<<"当前堆为空, 无最大值!!!"<<endl;
return -1;
}
int res = data[0]; //获得最大值
swap(data[0],data[data.size()-1]);// 将最大值和最后一个叶子节点交换位置, 即用最后一个叶子节点替换了根节点
data.erase(data.end() - 1);// 删除最后一个叶子节点, 即删除了最大值
siftDown(0);//现在根节点下沉
return res;
}
// 节点上浮: 让插入的数和父节点的数值比较,当大于父节点时就和父节点交换
void MaxHeap::siftUP(int index){
while(index != 0 && (data[getParent(index)] < data[index])){
swap(data[index],data[getParent(index)]);
index = getParent(index);
}
}
// 节点下沉
void MaxHeap::siftDown(int index){
while(getLeftChild(index) < data.size()){ // 一直往下沉,直到到叶子节点
int swapindex = getLeftChild(index); // 用于和index进行比较的节点,默认先为左孩子
// swaoindex + 1 < data.size()表示还有右孩子,若右孩子比左孩子大,则使用右孩子和index比较
if((swapindex + 1 < data.size()) && data[swapindex+1] > data[swapindex]){
swapindex = getRightChild(index);
}
if(data[swapindex] > data[index]){ // 如果孩子比index大,则下沉,并使用孩子继续往下沉
swap(data[index],data[swapindex]);
index = swapindex;
}
else{return ;}
}
}
void MaxHeap::printHeap(){
for(int val : data){
cout<<val<<" ";
}
cout<<endl;
}