堆
堆 Heap 這種數據結構其實就是一顆 “完全二叉樹”,每個節點的值總是不大於或不小於其父節點的值:一棵深度爲k的有n個結點的二叉樹,對樹中的結點按從上至下、從左到右的順序進行編號,如果編號爲i(1≤i≤n)的結點與滿二叉樹中編號爲i的結點在二叉樹中的位置相同,則這棵二叉樹稱爲完全二叉樹。一棵深度爲k且有 個結點的二叉樹稱爲 “滿二叉樹”。
堆的分類:
- 大頂堆:每個節點的值都大於或等於其子節點的值
- 小頂堆:每個節點的值都小於或等於其子節點的值
大頂堆和小頂堆都可以用來“從小到大”或“從大到小”排序,一般使用大頂堆。
堆的代碼實現
堆就是一顆完全二叉樹,中間沒有空洞,因此可以使用內存連續的數組或動態數組來實現。以大頂堆爲例。
先將堆中結點的序號和數組索引進行對應:(與層次遍歷類似)
顯然,每對父子結點在數組中的索引存在數學關係:
- 索引爲 的結點,其父結點索引爲
- 索引爲 的結點,其左孩子索引爲 ,右孩子索引爲
插入操作:push
- 先將元素插入到最後一個結點
- 由此逐層向上滲透:若比父結點大,就交換
刪除操作:pop
- 先將根節點刪除,替換爲最後一個結點
- 由此逐層向下滲透:若比左右結點小,就和bigger交換
模板類代碼:
#pragma once
#include <vector>
template<typename T>
class MaxHeap
{
public:
MaxHeap() {}
~MaxHeap() {}
void push(T& x);
void pop();
bool empty() const;
const T& top() const;
//private:
std::vector<T> heap;
};
template<typename T>
inline void MaxHeap<T>::push(T& x)
{
// 添加元素
heap.push_back(x);
// 向上滲透
int index = heap.size() - 1;
while (index > 0)
{
int parent = (index - 1) / 2;
if (heap[parent] >= x) break;
heap[index] = heap[parent];
index = parent;
}
heap[index] = x;
}
template<typename T>
inline void MaxHeap<T>::pop()
{
// 替換根結點
T x = heap[0] = heap[heap.size() - 1];
heap.pop_back();
if (heap.empty()) return;
// 向下滲透
int index = 0;
while (index * 2 + 1 < heap.size()) // 左子結點存在
{
int larger_child;
int l_child = index * 2 + 1;
int r_child = index * 2 + 2;
if (r_child < heap.size() && heap[r_child] >= heap[l_child]) // 右子結點存在
{
larger_child = r_child;
}
else
{
larger_child = l_child;
}
if (x > heap[larger_child]) // 截止
break;
heap[index] = heap[larger_child];
index = larger_child;
}
heap[index] = x;
}
template<typename T>
inline bool MaxHeap<T>::empty() const
{
return heap.empty();
}
template<typename T>
inline const T& MaxHeap<T>::top() const
{
return heap[0];
}
堆排序
堆排序是利用堆這種數據結構而設計的一種排序算法,堆排序是一種選擇排序,它的最壞、最好、平均時間複雜度均爲O(nlogn),是不穩定排序。
基本思想:
- 構造一個空的大頂堆;
- 將待排序序列
push
進入大頂堆,此時,整個序列的最大值就是堆頂的根節點 - 將大頂堆的根節點反覆讀出、刪除即得到有序序列
堆排序測試代碼:
#include <iostream>
#include "MaxHeap.h"
using namespace std;
int main(int argc, char* argv[])
{
int a[10] = { 11,3,25,177,299,0,52,74,86,308 };
int n = 10;
MaxHeap<int> heap;
for (auto x : a)
{
heap.push(x);
}
cout << "從小到大:";
for (int i = n-1; i >=0; i--)
{
a[i] = heap.top();
heap.pop();
}
//cout << "從大到小:";
//for (int i = 0; i < n; i++)
//{
// a[i] = heap.top();
// heap.pop();
//}
// 輸出排序結果
for (int i = 0; i < n; i++)
cout << a[i] << ", ";
cout << endl;
return 0;
}