堆中插入、删除元素
先看图解:
堆中插入元素
插入元素总是插入在数组中的最后一个位置,然后从其父节点依次向上调整堆,执行元素“上浮”操作。
//在最小堆中加入新的数据nNum
void addNumber(int[] a, int n, int nNum)
{
a[n] = nNum;
fixup(a, n);
}
// 新加入i结点 其父结点为(i - 1) / 2
void fixup(int[] a, int i)
{
int j, temp;
j = (i - 1) / 2; //父结点
while (j >= 0 && i != 0)
{
if (a[j] <= a[i])
break;
//父节点比当前节点大
swap(a,i,j); //当前节点与它的父节点交换,上浮
i = j; //继续向上调整堆
j = (i - 1) / 2;
}
}
//更简洁的写法:
void fixup2(int[] a, int i)
{
for (int j = (i - 1) / 2;
(j >= 0 && i != 0)&& a[i] > a[j];
i = j, j = (i - 1) / 2)
swap(a, i, j);
}
堆中删除元素
删除元素时,总是删除堆顶元素,后将堆中最后一个元素放置对顶,从堆顶到叶子节点依次向下调整堆,执行元素“下沉操作”。
//在最小堆中删除数
void deleteNumber(int[] a, int n)
{
swap(a[0], a[n - 1]);
fixdown(a, 0, n - 1);
}
// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
void fixdown(int[] a, int i, int n)
{
int j, temp;
temp = a[i];
j = 2 * i + 1;
while (j < n)
{
if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
j++;
if (a[j] >= temp)
break;
swap(a,i,j); //与左右孩子中最小的交换,下沉
i = j; //继续向下调整堆
j = 2 * i + 1;
}
}
堆的初始化
有了堆的插入和删除后,再考虑下如何对一个数据进行堆化操作。
从最后一个非叶子结点,直至根节点,自底向上,对以这些节点为根节点的子树逐渐执行元素下沉操作。
//建立最小堆
void MakeMinHeap(int[] a, int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
fixdown(a, i, n);
}
堆排序的完整代码
void minHeapSort(int[] a, int n)
{
makeMinHeap(a,n);
for (int i = n - 1; i >= 1; i--)
{
swap(a[i], a[0]);
fixdown(a, 0, i);
}
}
void makeMinHeap(int[] a, int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
fixdown(a, i, n);
}
void fixdown(int[] a, int i, int n)
{
int j, temp;
temp = a[i];
j = 2 * i + 1;
while (j < n)
{
if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
j++;
if (a[j] >= temp)
break;
swap(a,i,j); //与左右孩子中最小的交换,下沉
i = j; //继续向下调整堆
j = 2 * i + 1;
}
}