堆實現及常見面試題
阿新 • • 發佈:2019-02-11
一、堆的概念: 關鍵碼的集合按完全二叉樹的順序儲存方式儲存在一維陣列中,並滿足:對於所有節點它的父節點關鍵碼都大於子節點(或都小於它的子節點),這樣的堆稱為最大堆(或最小堆)。 二、堆相關的問題 1.簡單實現一個堆 堆的建立、放入資料、刪除資料、判斷是否為堆 2.TopK問題,求陣列中最大的K個元素 3.堆排序(實質是一個選擇排序),一個數組的升序、降序 三、下面例子中,博主是以建最大堆的方式實現的堆,在實現TopK中需要建小堆,博主又另寫了一個建小堆的函式。 ***Autually***, 這是一種較為笨的方法,可以實現一個仿函式,增加函式的複用性,這樣建小堆建大堆就不用建不同的堆了。 程式碼如下:
#pragma once
#include <iostream>
#include <vector>
#define N 10000
using namespace std;
template<class T>
class Heap
{
public:
Heap()
{}
Heap(T* a, size_t n)
{
_a.reserve(n);
for (size_t i = 0; i < n; i++)
{
_a.push_back(a[i]);
}
//建堆
for (int i = (_a.size() - 2) / 2; i >= 0; --i)//從倒數第一個非葉節點開始
{
AdjustDown(i);
}
}
HeapT(T* a, size_t k)//實現TopK建堆方式
{
_a.reserve(k);
for (size_t i = 0; i < k; i++)
{
_a.push_back(a[i]);
}
for (int i = (_a.size() - 2) / 2; i >= 0; --i)//建小堆
{
AdjustDownL(i);
}
}
void TopK(T* a)
{
for (size_t i = 100; i < N;)
{
if (_a[0] < a[i])
{
_a[0] = a[i];
AdjustDownL(0);
++i;
}
else
{
++i;
}
}
}
void HeapRiseSort(T* a, size_t k)//升序建大堆 降序降序建小堆
{
_a.reserve(k);
int end = k - 1;
Heap(a, k);//建堆
while (end > 0)
{
swap(_a[0], _a[end]);
end--;
AdjustDown(0);
//堆排序(升序),每次把堆頂資料放到陣列後面,再利用前end個元素重新建大堆
}
}
void Print()
{
for (size_t i = 0; i < _a.size(); i++)
{
cout << _a[i] << " ";
}
cout << endl;
}
void Push(const T& x)
{
_a.push_back(x);
AdjustUp(_a.size()-1);
}
void AdjustUp(int child)
{
int parent = (child - 1) >> 1;
while (child != 0)
{
if (_a[child] > _a[parent])
{
swap(_a[child], _a[parent]);
child = parent;
parent = (child - 1) >> 1;
}
else
{
break;
}
}
}
void Pop()
{
swap(_a[0], _a[_a.size() - 1]);
_a.pop_back();
AdjustDown(0);
}
void AdjustDown(int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < _a.size())
{
if (child + 1 < _a.size() && _a[child + 1] > _a[child])//選出大的孩子(存在右孩子)
{
++child;
}
if (_a[child] > _a[parent])
{
swap(_a[child], _a[parent]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
void AdjustDownL(int root)
{
int parent = root;
int child = parent * 2 + 1;
while (child < _a.size())
{
if (child + 1 < _a.size() && _a[child + 1] < _a[child])//選出大的孩子(存在右孩子)
{
++child;
}
if (_a[child] < _a[parent])//建最小堆
{
swap(_a[child], _a[parent]);
parent = child;
child = child * 2 + 1;
}
else
{
break;
}
}
}
int IsBHeap()
{
for (int i = _a.size() - 1; i >= 0; i++)
{
return _IsBHeap(i)==true;//為真返回1,假0
}
}
protected:
bool _IsBHeap(int root)
{
int child = root;
int parent = (child - 1) >> 1;
while (child<_a.size())
{
int large = child;
if (child + 1<_a.size() && _a[child + 1]>_a[child])
{
large = child + 1;
}
if (_a[large] < _a[parent])
{
return true;
}
else
{
return false;
}
}
return false;
}
vector<T> _a;
};
測試用例:
#pragma once
#include "Heap.h"
void Heaptest()
{
int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
Heap<int>h1(arr, sizeof(arr)/sizeof(arr[0]));
h1.Print();
h1.Pop();
h1.Print();
h1.Push(20);
h1.Print();
h1.Push(2);
cout << h1.IsBHeap() << endl;
}
void topKTest()
{
int arr[N] = { 0 };
for (size_t i = 0; i < N; i++)
{
arr[i] = rand() % N;
}
arr[100] = 12000;
arr[999] = 11145;
arr[888] = 10008;
arr[118] = 19999;
arr[365] = 19880;
arr[254] = 13444;
arr[4579] = 14555;
arr[1000] = 47839;
arr[9992] = 19875;
arr[1723] = 98345;
HeapT<int>h1(arr, 10);
h1.TopK(arr);
h1.Print();
}
//堆排序實質上是一種選擇排序,選出最大的與尾部資料交換,然後堆元素個數--,重新建堆,相當於沒把剛剛選出的最大的數算在重新建的堆中
void Heaptest()
{
int arr[] = { 10, 11, 13, 12, 16, 18, 15, 17, 14, 19 };
Heap<int>h1(arr, 10);
h1.HeapRiseSort(arr,10);
h1.Print();
}
堆就講到這裡啦,之後會給大家補充仿函式和堆的相關知識,拜拜~