堆
堆的概念:如果有一個關鍵碼的集合K={k0, k1, k2, ······,kn-1},把它的所有元素按完全二叉樹的順序存儲方式存儲在一個一維數組中,並滿足:ki <= K2*i +1 且ki<=K2*i + 2; i=0,1,2···,則稱這個堆爲最小堆(或者最大堆);其實這麼多乾乾的概念還是很不好理解的;先來說一下什麼是完全二叉樹: 如果一顆具有N個結點的二叉樹的結構與滿二叉樹的前N個結點的結構相同,稱爲完全二叉樹; 滿二叉樹: 在一顆二叉樹中,如果所有分支結點都存在左子樹和右子樹,並且所有的葉子結點都在同一層上。
從上圖,我們可以很清楚的分別最小堆和最大堆的特點:
最小堆:任一結點的關鍵碼均小於等於它的左右孩子的關鍵碼,位於堆頂結點的關鍵碼最小;
最大堆:任一結點的關鍵碼均大於等於它的左右孩子的關鍵碼,位於堆頂結點的關鍵碼最大;
堆的創建
其實堆的創建就是把所提供給自己的層序遍歷的二叉樹的結果轉化爲具有堆特性的二叉樹:
例如:給到這樣一個數組:int arr[] = {53, 17, 78, 9, 45, 65, 87, 23};轉化爲小堆:
如上圖所示,先從下向上找到第一個並不爲葉子結點的結點(i = (sizeof(a) -2)/2),然後以他爲根結點向下調整在左右孩子中找到最小的與之交換,在第①步中,不需要交換; 然後在向前走(i–),繼續以他爲根結點,找左右孩子中最小的交換,第②步中,我們就發生了交換;在第③步中,我們以17爲根結點交換,但是要保證以他爲根結點的所有子樹都滿足最小堆,所以它裏面的所有結點都必須做出調整;最後一步就是把整個樹都調整爲最小堆:
這是向下調整的代碼:
void _AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _heap.size())
{
if (child + 1 < _heap.size() && Compare()(_heap[child + 1], _heap[child]))
{
child += 1;
}
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
堆的插入
堆的插入其實就是每次在已經創建好的最小堆或者最大堆的後面插入但是插入之後,有可能會破壞堆的結構,就需要我們對堆進行重新調整以滿足堆的特性:
如上圖所示,最後要達到的結果就是這樣, 我簡單分析一下:其實就是用插入的元素和它的雙親進行比較如果是要構建小堆,那麼,如果它比雙親小,就用它代替雙親,否則就已經是最小堆了(PS:這是基於堆原來就是小堆的基礎上)
我們把這種調整的方法簡單稱爲向上調整法:(插入的結點一直在向上走啊):
void _AdjustUp(size_t child)
{
while (child != 0)
{
int parent = (child - 1) >> 1;
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
child = parent;
//parent = (child - 1) >> 1;
}
else
return;
}
}
堆的刪除
堆的刪除是:偶從堆中刪除堆頂元素。移除堆頂元素後,用堆的最後一個結點填補取走的堆頂元素,並將堆的實現元素個數減1,但用最後一個元素去掉堆頂元素之後有可能破壞堆,因此需要 將堆自頂向下調整,使其滿足最大或最小堆。
如上圖,在調整過後的樹中,並不滿足最小堆,所以需要自頂向下進行調整;其實和創建堆的向下調整是一樣的;
附 堆的完整代碼:
#pragma once
#include<vector>
#include<assert.h>
#include<iostream>
using namespace std;
//模板參數
//仿函數
template<class T>
struct Less
{
bool operator()(const T& left, const T& right)
{
return left < right;
}
};
template<class T>
struct Greater
{
bool operator()(const T& left, const T& right)
{
return left > right;
}
};
template<class T, class Compare = Less<T>>
class Heap
{
public:
//創建一個堆
Heap()
{}
Heap(const T array[], size_t size)
{
_heap.resize(size);
for (size_t idx = 0; idx < size; idx++)
{
_heap[idx] = array[idx];
}
int root = (size - 2) >> 1;
for (; root >= 0; --root)
{
_AdjustDown(root);
}
}
size_t Size()const
{
return _heap.size();
}
bool Empty()const
{
return _heap.empty();
}
void Insert(const T&data)
{
_heap.push_back(data);
size_t size = _heap.size();
size_t child = size - 1;
_AdjustUp(child);
}
void Remove()
{
assert(!Empty());
std::swap(_heap[0], _heap[_heap.size() - 1]);
_heap.pop_back();
_AdjustDown(0);
}
const T& Top()const
{
assert(!Empty());
return _heap[0];
}
private:
void _AdjustDown(size_t parent)
{
size_t child = parent * 2 + 1;
while (child < _heap.size())
{
if (child + 1 < _heap.size() && Compare()(_heap[child + 1], _heap[child]))
{
child += 1;
}
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
parent = child;
child = parent * 2 + 1;
}
else
return;
}
}
void _AdjustUp(size_t child)
{
while (child != 0)
{
int parent = (child - 1) >> 1;
if (Compare()(_heap[child], _heap[parent]))
{
std::swap(_heap[parent], _heap[child]);
child = parent;
//parent = (child - 1) >> 1;
}
else
return;
}
}
private:
std::vector<T> _heap;
};