堆的优点
-
队列
普通队列:先进先出、后进后出。
特殊队列(栈):先进后出、后进先出。
优先队列:出队顺序和入队顺序无关,与优先级有关。
-
优先队列的实现
普通数组: 入队时间O(1) 出队时间O(n)
有序数组: 入队时间O(n) 出队时间O(1)
堆 :出队时间O(lg n) 出队时间O(lg n)
证明: 2^((n+1)/2) > n -> (n+1)/2 > log n -> n+1 > 2log n 故 2*log n < n+1
实现二叉堆
-
二叉堆的结构:
二叉堆为一颗完全二叉树,每个节点最多有两个子节点,且不能只有右子节点而不能有左子节点。
-
构造二叉堆:
可以用数组来实现二叉堆。
数组从1开始时,节点n的左子节点为2*n、右子节点为2*n+1。
数组从0开始时,节点n的左子节点为2*(n+1)-1、右子节点为2*(n+1)。
-
shiftUp函数
该函数用于向堆中插入新元素
函数思路:插入元素时,先检验堆中是否有空间,有空间时将元素插入到队列末尾。此时相当于元素在堆的底部,然后和父节点比较,如果不符合堆的定义(大根堆:父节点大于左右子节点;小根堆:父节点小于左右子节点)就交换新节点和其父节点,交换后再次进行比较,直到满足定义。
template<typename Item>
void MaxHeap<Item>::shiftUp(int k)
{
for (int i = k; (i > 1) && (data[i] > data[i / 2]); i = i / 2)
swap(data[i], data[i / 2]);
//减少了调用swap函数的次数提高了时间效率
//Item item = data[k];
//while (k > 1 && item > data[k / 2])
//{
// data[k] = data[k / 2];
// k = k / 2;
//}
//data[k] = item;
}
template<typename Item>
void MaxHeap<Item>::insert(Item item)
{
assert(count + 1 <= capacity);
data[++count] = item;
shiftUp(count);
}
-
shiftDown函数
该函数用于从堆中取出根(最大或最小)节点
函数思路:取出元素时,先判断堆是否为空。不为空时将根节点取出,把末尾节点的值放置到根节点上,再比较是否大于其左右子节点。如果小于左右子节点,则将其与较大的(或较小的)那个节点进行替换,替换后再次进行比较,直到满足定义(大根堆:大于其左右子节点;小根堆:小于其左右子节点)。
template<typename Item> inline void MaxHeap<Item>::shiftDown(int k) { while (2 * k > count) { int j = 0; if (data[2 * i] > data[2 * i + 1]) j = 2 * i; else j = 2 * i + 1; if (data[j] < data[k]) break; swap(data[j], data[k]); k = j; } } template<typename Item> inline Item MaxHeap<Item>::extractMax() { assert(count > 0); Item item = data[1]; swap(data[1], data[count--]); shiftDown(int k); return item; }
-
heapPrint函数
将堆按层次输出
函数思路:定义两个变量,一个变量用于存储输出的元素个数、一个用于实现2n,当余下的不足2n时,直接输出。
template<typename Item> inline void MaxHeap<Item>::heapPrint() { int i = 0, k = 1; //每行按 2^n 输出 for (i; i < count&&(count-i)>k; i += (k/2)) { for (int j = 1; j <= k; j++) cout << data[i + j] << '-' << i+j << ' '; cout << endl; k *= 2; } //不足 2^n ,直接输出 for (i ; i < count; i++) cout << data[i] << '-' << i << ' '; }
-
基于堆的排序
有两种方法使用堆结构进行排序:-
定义一个堆,然后将数组依次输入数组中。
template<typename Item> MaxHeap(Item *arr,int n) { for(int i=0;i<n;i++) insert(arr[i]); } //时间复杂度:O(n logn);空间复杂度:O(n)
-
从数组 count/2 处开始,依次对前 count/2 个元素调用shiftDown。
template<typename Item> inline MaxHeap<Item>::MaxHeap(Item * arr, int n) { data = new Item[n + 1]; capacity = n; count = n; memcpy(data, arr, n);//#<string> for (int i = n / 2; i > 0; i--) shiftDown(i); } //时间复杂度:O(n);空间复杂度:O(n)
-
-
基础二叉堆(排序)的实现
#pragma once #include<iostream>//cout #include<algorithm> #include<assert.h>//assert #include<string>//memcpy using namespace std; template<typename Item> class MaxHeap { private: Item *data;//数组堆 int capacity;//数组容量 int count = 0;//堆内存储元素的个数 void shiftUp(int k);//向用户隐藏实现 void shiftDown(int k);//向用户隐藏实现 public: MaxHeap(int capacity);//构建一颗容量为capacity的空二叉树 MaxHeap(Item *arr, int n);//将一个长度为n的数组转换成二叉树 ~MaxHeap(); int getHeapSize();//堆中存储的个数 bool isEmpty();//堆是否为空 void insert(Item item);//向堆中添加元素 void heapPrint();//输出堆中的元素 Item extractMax();//取出堆中的最大元素 void clearHeap();//清空堆 }; template<typename Item> void MaxHeap<Item>::shiftUp(int k) { Item item = data[k]; while (k > 1 && item > data[k / 2])//此处为item与data[k/2]比较 { data[k] = data[k / 2]; k = k / 2; } data[k] = item; } template<typename Item> inline void MaxHeap<Item>::shiftDown(int k) { while (2 * k > count) { int j = 0; if (data[2 * k] > data[2 * k + 1] && 2 * k + 1 < count) j = 2 * k; else j = 2 * k + 1; if (data[j] < data[k]) break; swap(data[j], data[k]); k = j; } } template<typename Item> inline MaxHeap<Item>::MaxHeap(int capacity) { data = new Item[capacity + 1]; count = 0; this->capacity = capacity; } template<typename Item> inline MaxHeap<Item>::MaxHeap(Item * arr, int n) { data = new Item[n + 1]; capacity = n; count = n; memcpy(data, arr, n); for (int i = n / 2; i > 0; i--) shiftDown(i); } template<typename Item> inline MaxHeap<Item>::~MaxHeap() { delete[] data; } template<typename Item> inline int MaxHeap<Item>::getHeapSize() { return count; } template<typename Item> inline bool MaxHeap<Item>::isEmpty() { return count == 0; } /*Fn Insert @brief 将索引和数据项存入二叉堆 @param Item-item @return void */ template<typename Item> inline void MaxHeap<Item>::insert(Item item) { assert(count + 1 <= capacity); ++count; data[count] = item; //cout << "insert data:" << data[count] << endl; //cout << "count:" << count << endl; shiftUp(count); } /*Fn heapPrint @brief 将数据按层次输出 @param void @return void */ template<typename Item> inline void MaxHeap<Item>::heapPrint() { cout << "HeapPrint" << endl; int i = 0, k = 1; for (i; i < count&&(count-i)>k; i += (k/2)) { for (int j = 1; j <= k; j++) cout << data[i + j] << '-' << i+j << ' '; cout << endl; k *= 2; } for (i++ ; i <= count; i++) cout << data[i] << '-' << i << ' '; cout << endl; } /*Fn extractMax() @brief 将堆顶的元素输出 @param void @return void */ template<typename Item> inline Item MaxHeap<Item>::extractMax() { assert(count > 0); Item item = data[1]; swap(data[1], data[count--]); shiftDown(1); return item; } template<typename Item> inline void MaxHeap<Item>::clearHeap() { count = 0; }
二叉堆的优化
-
原地堆排序
template<typename Item> void __shiftDown(Item arr[], int n, int k) { //※※※※※※※ while (2 * k + 1 < n) { int j = 2 * k + 1; if (arr[j + 1] > arr[j] && j + 1 < n) j += 1; if (arr[k] >= arr[j]) break; swap(arr[k], arr[j]); k = j; } } template<typename Item> void heapSort(Item arr[], int n) { //先对数组进行堆排序 //从 (最后一个元素的索引值-1)/2 开始 //最后一个元素的索引 = n-1 for (int i = (n - 1 - 1) / 2; i >= 0; i--) __shiftDown(arr, n, i);//对arr数组第i个位值的元素进行shiftDown操作,数组arr共有n个元素 //每次都将前i个中的最大值(第一个元素)和到第n-i个元素交换(每次都将余下元素中的最大值移到末尾处。 for (int i = n - 1; i > 0; i--) { swap(arr[0], arr[i]); __shiftDown(arr, i, 0); } }
-
索引堆
对每一项数据都增加了索引,可以快速的根据数据的编号找到对应的值,也可以修改特定项的值。
#pragma once #include<iostream>//cout #include<algorithm> #include<assert.h>//assert #include<string>//memcpy using namespace std; template<typename Item> class IndexMaxHeap { private: Item *data;//数组堆 int *indexes;//索引列表 int capacity;//数组容量 int count = 0;//堆内存储元素的个数 void shiftUp(int k);//向用户隐藏实现 void shiftDown(int k);//向用户隐藏实现 public: IndexMaxHeap(int capacity);//构建一颗容量为capacity的空二叉树 IndexMaxHeap(Item *arr, int n);//将一个长度为n的数组转换成二叉树 ~IndexMaxHeap(); int getHeapSize();//堆中存储的个数 bool isEmpty();//堆是否为空 void insert(int index, Item item);//向堆中添加元素 void heapPrint();//按堆输出堆中的元素 void linePrint();//按行输出堆中的元素 Item extractMax();//取出堆中的最大元素 int extractMaxIndex();//取出最大元素的索引 Item getItem(int index);//根据索引取出相应的元素 void change(int index, Item item);//将index项元素的值改为item }; template<typename Item> void IndexMaxHeap<Item>::shiftUp(int k) { int index = indexes[k]; while (k > 1 && data[index] > data[indexes[k / 2]]) { indexes[k] = indexes[k / 2]; k = k / 2; } indexes[k] = index; } template<typename Item> inline void IndexMaxHeap<Item>::shiftDown(int k) { while (2 * k < count) { int j = 0; if (2 * k + 1 < count && data[indexes[2 * k]] > data[indexes[2 * k + 1]]) j = 2 * k; else j = 2 * k + 1; if (data[indexes[j]] < data[indexes[k]]) break; swap(indexes[j], indexes[k]); k = j; } } template<typename Item> inline IndexMaxHeap<Item>::IndexMaxHeap(int capacity) { data = new Item[capacity + 1]; indexes = new int[capacity + 1]; count = 0; this->capacity = capacity; } template<typename Item> inline IndexMaxHeap<Item>::IndexMaxHeap(Item * arr, int n) { data = new Item[n + 1]; indexes = new int[capacity + 1]; capacity = n; count = n; memcpy(data, arr, n); for (int i = n / 2; i > 0; i--) shiftDown(i); } template<typename Item> inline IndexMaxHeap<Item>::~IndexMaxHeap() { delete[] data; } template<typename Item> inline int IndexMaxHeap<Item>::getHeapSize() { return count; } template<typename Item> inline bool IndexMaxHeap<Item>::isEmpty() { return count == 0; } /*Fn insert @brief 将索引和数据项存入二叉堆 @param int-index Item-item int型的索引以及定义类型的数据 @return void */ template<typename Item> inline void IndexMaxHeap<Item>::insert(int index, Item item) { //对用户而言,索引从0开始 assert(count + 1 <= capacity); assert(index + 1 >= 1); assert(index + 1 <= capacity); data[++index] = item;//data数组存储item,data数组的下标是index indexes[++count] = index;//indexes数组存储index,indexes数组下标使用count+1 shiftUp(count); } /*Fn heapPrint @brief 将数据按层次输出 @param void @return void */ template<typename Item> inline void IndexMaxHeap<Item>::heapPrint() { cout << "HeapPrint" << endl; int i = 0, k = 1; for (i; i < count && (count - i)>k; i += (k / 2)) { for (int j = 1; j <= k; j++) cout << indexes[i + j] << '-' << data[indexes[i + j]] << '-' << i + j << ' '; cout << endl; k *= 2; } for (++i; i <= count; i++) cout << indexes[i] << '-' << data[indexes[i]] << '-' << i << ' ';//TARGET cout << endl; } /*Fn linePrint @brief 将堆中的元素按行输出 @param void @return void */ template<typename Item> inline void IndexMaxHeap<Item>::linePrint() { cout << "LinePrint:" << endl; cout << "data[]:" << endl; for (int i = 1; i <= count; i++) cout << data[i] << ' '; cout << endl; cout << "indexes[]:" << endl; for (int i = 1; i <= count; i++) cout << indexes[i] << ' '; cout << endl; cout << "data[indexes[]]:" << endl; for (int i = 1; i <= count; i++) cout << data[indexes[i]] << ' '; cout << endl; } /*Fn extractMax() @brief 将堆顶的元素输出 @param void @return void */ template<typename Item> inline Item IndexMaxHeap<Item>::extractMax() { assert(count > 0); Item item = data[indexes[1]]; swap(indexes[1], indexes[count--]); shiftDown(1); return item; } /*Fn extractMaxIndex @brief 取出堆顶元素的序号 @param void @return int */ template<typename Item> inline int IndexMaxHeap<Item>::extractMaxIndex() { assert(count > 0); int ret = indexes[1] - 1; swap(indexes[1], indexes[count--]); shiftDown(1); return indexes[1]; } /*Fn getItem @brief 根据索引得到值 @param int-index @return Item-item */ template<typename Item> inline Item IndexMaxHeap<Item>::getItem(int index) { return data[index + 1]; } /*Fn change @brief 改变对应索引值元素的值 @param int-index Item-item @return void */ template<typename Item> inline void IndexMaxHeap<Item>::change(int index, Item newItem) { index += 1; data[index] = newItem; //找到indexes[i]=index,i是data[index]在堆中的位值 //然后shiftUp(i)、shiftDown(i); for (int i = 1; i <= count; i++) { if (index == indexes[i]) { shiftDown(i); shiftUp(i); return; } } }
-
索引堆plus
增加反向查找数组reverse[],使得通过索引更改数值操作的时间复杂度由O(n)降到了O(log n)。
indexes[i]=j — reverse[j]=i
indexes[reverse[i]]=i — reverse[indexes[i]]=i。#pragma once #include<iostream>//cout #include<algorithm> #include<assert.h>//assert #include<string>//memcpy using namespace std; template<typename Item> class IndexMaxHeapPLUS { private: Item *data;//数组堆 int *indexes;//索引列表 int *reverse;//反向索引数组 int capacity;//数组容量 int count = 0;//堆内存储元素的个数 void shiftUp(int k);//向用户隐藏实现 void shiftDown(int k);//向用户隐藏实现 bool contain(int index);//判断索引是否存在 public: IndexMaxHeapPLUS(int capacity);//构建一颗容量为capacity的空二叉树 IndexMaxHeapPLUS(Item *arr, int n);//将一个长度为n的数组转换成二叉树 ~IndexMaxHeapPLUS(); int getHeapSize();//堆中存储的个数 bool isEmpty();//堆是否为空 void insert(int index, Item item);//向堆中添加元素 void heapPrint();//按堆输出堆中的元素 void linePrint();//按行输出堆中的元素 Item extractMax();//取出堆中的最大元素 int extractMaxIndex();//取出最大元素的索引 Item getItem(int index);//根据索引取出相应的元素 void change(int index, Item item);//将index项元素的值改为item }; template<typename Item> void IndexMaxHeapPLUS<Item>::shiftUp(int k) { while (k > 1 && data[indexes[k]] > data[indexes[k / 2]]) { swap(indexes[k], indexes[k / 2]); reverse[indexes[k]] = k; reverse[indexes[k / 2]] = k / 2; k = k / 2; } } template<typename Item> inline void IndexMaxHeapPLUS<Item>::shiftDown(int k) { while (2 * k <= count) { int j = 2*k; if (j + 1 < count&&data[indexes[j]] < data[indexes[j + 1]]) j += 1; if (data[indexes[j]] < data[indexes[k]]) break; swap(indexes[j], indexes[k]); reverse[indexes[j]] = j; reverse[indexes[k]] = k; k = j; } } template<typename Item> inline IndexMaxHeapPLUS<Item>::IndexMaxHeapPLUS(int capacity) { data = new Item[capacity + 1]; indexes = new int[capacity + 1]; reverse = new int[capacity + 1]; memset(reverse, capacity + 1, 0);//赋初值 count = 0; this->capacity = capacity; } template<typename Item> inline IndexMaxHeapPLUS<Item>::IndexMaxHeapPLUS(Item * arr, int n) { data = new Item[n + 1]; indexes = new int[capacity + 1]; capacity = n; count = n; memcpy(data, arr, n); for (int i = n / 2; i > 0; i--) shiftDown(i); } template<typename Item> inline IndexMaxHeapPLUS<Item>::~IndexMaxHeapPLUS() { delete[] data; } template<typename Item> inline int IndexMaxHeapPLUS<Item>::getHeapSize() { return count; } template<typename Item> inline bool IndexMaxHeapPLUS<Item>::isEmpty() { return count == 0; } /*Fn insert @brief 将索引和数据项存入二叉堆 @param int-index Item-item int型的索引以及定义类型的数据 @return void */ template<typename Item> inline void IndexMaxHeapPLUS<Item>::insert(int index, Item item) { //对用户而言,索引从0开始 assert(count + 1 <= capacity); assert(index + 1 >= 1); assert(index + 1 <= capacity); data[++index] = item; indexes[++count] = index; reverse[index] = count; shiftUp(count); } /*Fn heapPrint @brief 将数据按层次输出 @param void @return void */ template<typename Item> inline void IndexMaxHeapPLUS<Item>::heapPrint() { cout << "HeapPrint:" << endl; int i = 0, k = 1; for (i; i < count && (count - i)>k; i += (k / 2)) { for (int j = 1; j <= k; j++) cout << data[indexes[i + j]] << '-' << i + j << ' '; cout << endl; k *= 2; } for (++i; i <= count; i++) cout << data[indexes[i]] << '-' << i << ' '; cout << endl; } template<typename Item> inline void IndexMaxHeapPLUS<Item>::linePrint() { cout << "LinePrint:" << endl; cout << "data[]:" << endl; for (int i = 1; i <= count; i++) cout << data[i] << ' '; cout << endl; cout << "indexes[]:" << endl; for (int i = 1; i <= count; i++) cout << indexes[i] << ' '; cout << endl; cout << "reverse[]:" << endl; for (int i = 1; i <= count; i++) cout << reverse[i] << ' '; cout << endl; cout << "data[indexes[]]:" << endl; for (int i = 1; i <= count; i++) cout << data[indexes[i]] << ' '; cout << endl; } /*Fn extractMax() @brief 将堆顶的元素输出 @param void @return void */ template<typename Item> inline Item IndexMaxHeapPLUS<Item>::extractMax() { assert(count > 0); Item item = data[indexes[1]]; swap(indexes[1], indexes[count]); reverse[indexes[1]] = 1; reverse[indexes[count--]] = 0; shiftDown(1); return item; } /*Fn extractMaxIndex @brief 取出堆顶元素的序号 @param void @return int */ template<typename Item> inline int IndexMaxHeapPLUS<Item>::extractMaxIndex() { assert(count > 0); int ret = indexes[1] - 1; swap(indexes[1], indexes[count]); reverse[indexes[1]] = 1; reverse[indexes[count--]] = 0; shiftDown(1); return indexes[1]; } /*Fn contian @brief 判断是否有逆向数组 @param int-index @return bool */ template<typename Item> inline bool IndexMaxHeapPLUS<Item>::contain(int index) { assert(index + 1 >= 1 && index + 1 <= capacity); return reverse[index + 1] != 0; } /*Fn getItem @brief 根据索引得到值 @param int-index @return Item-item */ template<typename Item> inline Item IndexMaxHeapPLUS<Item>::getItem(int index) { assert(contain(index)); return data[index + 1]; } /*Fn change @brief 改变对应索引值元素的值 @param int-index Item-item @return void */ template<typename Item> inline void IndexMaxHeapPLUS<Item>::change(int index, Item newItem) { assert(contain(index)); index += 1; data[index] = newItem; int j = reverse[index]; cout << j << endl; shiftUp(j); shiftDown(j); }