堆的定义
n个元素的序列{k1,k2,…,kn}当且仅当满足下列关系之一时,称之为堆。
情形1:ki <= k2i 且ki <= k2i+1 (最小化堆或小顶堆)
情形2:ki >= k2i 且ki >= k2i+1 (最大化堆或大顶堆)
其中i=1,2,…,n/2向下取整;
若将和此序列对应的一维数组(即以一维数组作此序列的存储结构)看成是一个完全二叉树,则堆的含义表明,完全二叉树中所有非终端结点的值均不大于(或不小于)其左、右孩子结点的值。
由此,若序列{k1,k2,…,kn}是堆,则堆顶元素(或完全二叉树的根)必为序列中n个元素的最小值(或最大值)。
例如,下列两个序列为堆,对应的完全二叉树如图:
若在输出堆顶的最小值之后,使得剩余n-1个元素的序列重又建成一个堆,则得到n个元素的次小值。如此反复执行,便能得到一个有序序列,这个过程称之为堆排序。
堆排序(Heap Sort)只需要一个记录元素大小的辅助空间(供交换用),每个待排序的记录仅占有一个存储空间。
堆的存储
一般用数组来表示堆,若根结点存在序号0处, i结点的父结点下标就为(i-1)/2。i结点的左右子结点下标分别为2*i+1和2*i+2。
(注:如果根结点是从1开始,则左右孩子结点分别是2i和2i+1。)
如第0个结点左右子结点下标分别为1和2。
如最大化堆如下:
左图为其存储结构,右图为其逻辑结构。
存储在堆内的数据由Node类来构建。
public class Node
{
public int data;
public Node(int key)
{
data = key;
}
}
通过吧节点插入到堆数组内的方式可以构造堆,二堆数组的元素就是堆的节点。
这里始终要把新节点放置在数组末尾的空元素内。问题是这样做很可能会打破堆的构造条件,因为新的节点的数据值可能会大于他上面某些节点值。为了把数组恢复到正确的堆构造条件,需要把新节点向上移动,一直要把它移动到数组内合适的位置上为止。用ShifUp来实现。
public void ShifUp(int index)
{
int parent = (index - 1) / 2;
Node bottom = heapArray[index];
while ((index > 0) && heapArray[index].data < bottom.data)
{
heapArray[index] = heapArray[parent];
index = parent;
parent = (parent - 1) / 2;
}
heapArray[index] = bottom;
}
下面是Insert方法的实现代码:
public bool Insert(int key)
{
if (currSize == maxSize)
return false;
heapArray[currSize] = new Node(key);
currSize++;
return true;
}
这里会把新节点添加到数组的末尾。这样做会立刻打破堆构造的条件,所以通过shifup方法来找到新节点在数组内的正确位置。次方法的参数就是新节点的索引。方法的第一行会计算此节点的父节点。接着方法会把新节点保存到一个名为bottom的Node变量内。随后,wihle循环会找到新节点的正确位置。方法的最后一行会把新节点从临时放置的变量bottom内复制到数组中正确的位置上。
从堆中移除掉节点始终意味着删除最大值的节点。这是很容易实现的,因为最大值始终在根节点上。问题是一旦移除根节点,堆就不完整了,就需要对其进行重组。下面这个算法用于使堆再次完整。
(1)移除根节点
(2)把最后位置上的节点移动到根上
(3)把最后的节点向下移动,知道它在底下为止。
当连续应用这个算法的时候,就会按照排列顺序把数据从堆中移除掉。下面就是Remove方法和TrickleDown方法的实现代码。
public Node Remove()
{
Node root = heapArray[0];
currSize--;
heapArray[0] = heapArray[currSize];
ShifDown(0);
return root;
}
public void ShifDown(int index)
{
int largerChild;
Node top = heapArray[index];
while (index < (int)(currSize / 2))
{
int leftChild = 2 * index + 1;
int rightChild = leftChild + 1;
if ((rightChild < currSize) && heapArray[leftChild].data < heapArray[rightChild].data)
{
largerChild = rightChild;
}
else
{
largerChild = leftChild;
}
if (top.data >= heapArray[largerChild].data)
{
break;
}
heapArray[index] = heapArray[largerChild];
index = largerChild;
}
heapArray[index] = top;
}
完整:
class Heap
{
Node[] heapArray = null;
private int maxSize = 0;
private int currSize = 0;
public Heap()
{
}
public Heap(int maxSize)
{
this.maxSize = maxSize;
heapArray = new Node[maxSize];
}
public bool InsertAt(int pos, Node nd)
{
heapArray[pos] = nd;
return true;
}
public void ShowArray()
{
for (int i = 0; i < maxSize; i++)
{
if (heapArray[i] != null)
{
System.Console.Write(heapArray[i].data+" ");
}
}
}
public void ShifUp(int index)
{
int parent = (index - 1) / 2;
Node bottom = heapArray[index];
while ((index > 0) && heapArray[index].data < bottom.data)
{
heapArray[index] = heapArray[parent];
index = parent;
parent = (parent - 1) / 2;
}
heapArray[index] = bottom;
}
public bool Insert(int key)
{
if (currSize == maxSize)
return false;
heapArray[currSize] = new Node(key);
currSize++;
return true;
}
public Node Remove()
{
Node root = heapArray[0];
currSize--;
heapArray[0] = heapArray[currSize];
ShifDown(0);
return root;
}
public void ShifDown(int index)
{
int largerChild;
Node top = heapArray[index];
while (index < (int)(currSize / 2))
{
int leftChild = 2 * index + 1;
int rightChild = leftChild + 1;
if ((rightChild < currSize) && heapArray[leftChild].data < heapArray[rightChild].data)
{
largerChild = rightChild;
}
else
{
largerChild = leftChild;
}
if (top.data >= heapArray[largerChild].data)
{
break;
}
heapArray[index] = heapArray[largerChild];
index = largerChild;
}
heapArray[index] = top;
}
public void Test()
{
const int SIZE = 9;
Heap aHeap = new Heap(SIZE);
Random RandomClass = new Random();
for (int i = 0; i < SIZE; i++)
{
int rn = RandomClass.Next(1,100);
aHeap.Insert(rn);
}
Console.Write("Random: ");
aHeap.ShowArray();
Console.WriteLine();
Console.Write("Heap: ");
for(int i=(int)SIZE/2-1;i>=0;i--)
{
aHeap.ShifDown(i);
}
aHeap.ShowArray();
for (int i = SIZE - 1; i >= 0; i--)
{
Node bigNode = aHeap.Remove();
aHeap.InsertAt(i,bigNode);
}
Console.WriteLine();
Console.Write("Sorted: ");
aHeap.ShowArray();
}
}