完全二叉树
首先让我们回顾一下完全二叉树的两个性质:
性质1:具有n个结点的完全二叉树的深度为[logn](向下取整)+1。
性质2:若对含 n 个结点的完全二叉树从上到下且从左至右进行 1 至 n 的编号,则对完全二叉树中任意一个编号为 i 的结点:
(1) 若 i=1,则该结点是二叉树的根,无双亲,否则,编号为 [i/2](向下取整)的结点为其双亲结点;
(2) 若 2i>n,则该结点无左孩子,否则,编号为 2i 的结点为其左孩子结点;
(3) 若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。
数组与完全二叉树
从上图可以看出, 如果完全二叉树从上到下且从左至右进行从0至n-1进行编号,则对完全二叉树的性质需要修改如下:
(1) 若 i=0,则该结点是二叉树的根,无双亲,否则,编号为 [(i-1)/2](向下取整)的结点为其双亲结点;
(2) 若 2i+1>n,则该结点无左孩子,否则,编号为 2i+1 的结点为其左孩子结点;
(3) 若 2i+2>n,则该结点无右孩子结点,否则,编号为2i+2 的结点为其右孩子结点。
大顶堆与小顶堆
堆是满足下列性质的数列{r1, r2, …,rn}:
(小顶堆)
(大顶堆)
大顶堆的设计
template <typename Type>
class MaxHeap
{
public:
MaxHeap(int _maxSize = 10);
virtual ~MaxHeap();
bool isEmpty() const;
void push(const Type &item);
void pop();
const Type &top() const;
private:
//将堆的容量增大两倍
void resize();
//向上渗透
void trickUp(int index);
//向下渗透
void trickDown(int index);
private:
Type *heapArray;
int maxSize;
//currentSize有两个含义:
// 1.代表当前堆中已经存储的元素数
// 2.代表当前完全二叉树的第一个空位
int currentSize;
};
大顶堆的实现
//构造
template <typename Type>
MaxHeap<Type>::MaxHeap(int _maxSize)
: maxSize(_maxSize),currentSize(0)
{
if (maxSize < 1)
throw std::length_error("heap size must >= 1");
heapArray = new Type[maxSize];
}
//析构
template <typename Type>
MaxHeap<Type>::~MaxHeap()
{
delete []heapArray;
heapArray = NULL;
currentSize = 0;
}
//判空
template <typename Type>
bool MaxHeap<Type>::isEmpty() const
{
return currentSize == 0;
}
堆顶元素
//查看堆顶元素
template <typename Type>
const Type &MaxHeap<Type>::top() const
{
if (isEmpty())
throw std::underflow_error("heap is empty");
return heapArray[0];
}
插入元素
//插入
template <typename Type>
void MaxHeap<Type>::push(const Type &item)
{
//如果堆已满, 则需要对堆进行扩充
if (currentSize == maxSize)
{
resize();
}
//将元素插入到堆的第一个空位置上
heapArray[currentSize] = item;
//维护堆的性质:向上渗透
trickUp(currentSize);
++ currentSize;
}
//向上渗透, 将刚刚插入的元素移动到合适的位置上
template <typename Type>
void MaxHeap<Type>::trickUp(int index)
{
//根据完全二叉树的性质, 找到父节点
int parent = (index-1)/2;
Type bottom = heapArray[index];
//循环终止条件
// 1. index = 0 //bottom元素插入到根节点
// 2. heapArray[parent] >= bottom //bottom插入到parent节点的一个子节点上
while ((index > 0) && (heapArray[parent] < bottom))
{
//父节点下移
heapArray[index] = heapArray[parent];
index = parent;
parent = (parent-1)/2;
}
//插入
heapArray[index] = bottom;
}
//将堆的大小加倍
template <typename Type>
void MaxHeap<Type>::resize()
{
int newSize = std::max(maxSize*2, 1);
Type *newHeap = new Type[newSize];
for (int i = 0; i < currentSize; ++i)
{
newHeap[i] = heapArray[i];
}
delete []heapArray;
heapArray = newHeap;
maxSize = newSize;
}
删除堆顶元素
//删除
template <typename Type>
void MaxHeap<Type>::pop()
{
if (isEmpty())
throw std::underflow_error("heap is empty");
//只有一个元素
if (currentSize == 1)
{
heapArray[0].~Type();
currentSize = 0;
return ;
}
//显示释放堆顶元素
heapArray[0].~Type();
//直接将最有一个元素放到堆顶,
//并且currentSize-1
heapArray[0] = heapArray[-- currentSize];
//此时如果破坏了堆的性质:向下渗透
trickDown(0);
}
//向下渗透
template <typename Type>
void MaxHeap<Type>::trickDown(int index)
{
int largeChild;
Type top = heapArray[index];
// 只需到达完全二叉树的最后一层
// 的上一层即可
// 循环的终止条件:
// 1. index已经到达了最后一层(index >= currentSize/2 :找到了一个比较合适的位置)
// 2. 在堆的中间找到了一个合适的位置(top >= heapArray[largeChild])
while (index < currentSize/2)
{
int leftChild = (index*2)+1;
int rightChild = leftChild + 1;
//如果有右儿子节点, 而且右儿子节点还大于左儿子节点
if (rightChild < currentSize && heapArray[rightChild] > heapArray[leftChild])
largeChild = rightChild;
else
largeChild = leftChild;
if (top >= heapArray[largeChild])
break;
//不然的话, 则需继续向下渗透
heapArray[index] = heapArray[largeChild];
// index需要向下搜索
index = largeChild;
}
//插入
heapArray[index] = top;
}
堆排序
堆排序就是将元素一个一个插入到堆中, 然后再一个一个的取出来;
//堆排序
template <typename Type>
void heapSort(Type *begin, Type *end)
{
int size = end-begin;
MaxHeap<Type> maxHeap(size);
//存入堆中
for (Type *current = begin; current < end; ++ current)
maxHeap.push(*current);
//从堆中取出
while (begin != end)
{
*begin = maxHeap.top();
++ begin;
maxHeap.pop();
}
}
template <typename Type>
void heapSort(Type *array, int arraySize)
{
return heapSort(array, array+arraySize);
}
附-测试代码:
int main()
{
int array[20];
for (int i = 0; i < 20; ++i)
{
array[i] = rand()%100;
cout << array[i] << ' ';
}
heapSort(array, 20);
cout << endl;
for (int i = 0; i < 20; ++i)
{
cout << array[i] << ' ';
}
cout << endl;
return 0;
}