标签:存储 heap 路径 show temp 最小 code 建堆 说明
推排序中的小顶堆的建立,需要注意的是,哪怕是相同的数,不同的插入顺序最终建立堆都不一样。
将一系列给定数字插入一个初始为空的小顶堆H[i]
。随后对任意给定的下标i
,打印从H[i]
到根结点的路径。
组测试第1行包含2个正整数N和M(≤1000),分别是插入元素的个数、以及需要打印的路径条数。下一行给出区间[-10000, 10000]内的N个要被插入一个初始为空的小顶堆的整数。最后一行给出M个下标。
对输入中给出的每个下标i
,在一行中输出从H[i]
到根结点的路径上的数据。数字间以1个空格分隔,行末不得有多余空格。
5 3
46 23 26 24 10
5 4 3
24 23 10
46 23 10
26 10
完全二叉树,所以显然使用数组进行存储。
前言也提到了,就算是相同的数组序列,不同的插入顺序,最终出来的堆都可能不一样。所以不能直接对完整序列进行处理,需要一个个插入操作。
小顶堆的插入是这样的:
访问指定下标的路径,且从底到上的方法:
因为使用数组存储,所以很方便的就可以找到某个结点的孩子结点,或者,某个结点的父结点。对于某个下标为i的结点来说,i/2向下取整就是该结点的父结点的位置。
注意,用数组存储二叉树时最好从下标1开始存储,因为可以方便的里面上述性质。
#include<iostream>
#include<iostream>
using namespace std;
const int MaxSize = 1001;
void myInsert(int a[], int p, int key); //插入结点并执行调整程序
void myBuildHeap(int a[], int n); //对整棵树进行调整,即对所有非叶结点进行调整操作
void myAdjust(int a[], int low, int high); //对单个非叶结点进行调整操作
void myShowPath(int a[], int p); //找到并显示路径
int main() {
int small_heap[MaxSize] = { 0, };
int N, M;
cin >> N >> M;
for (int i = 1; i < N + 1; i++) { //注意从1开始存储
int temp;
cin >> temp;
myInsert(small_heap, i, temp); //边输入就开始调整了
}
//到这里已经建堆完成了
for (int i = 0; i < M; i++) {
int p;
cin >> p;
myShowPath(small_heap, p);
}
return 0;
}
void myInsert(int a[], int p, int key) {
a[p] = key;
myBuildHeap(a, p);
}
void myBuildHeap(int a[], int n) {
for (int i = n / 2; i > 0; i--) //从最后一个非叶结点开始调整,这里注意对于一个结点数为n的CT来说,非叶结点有n/2个
myAdjust(a, i, n);
}
void myAdjust(int a[], int low, int high) { //调整单个非叶结点
int i = low;
int j = 2 * i; //从左子结点开始
while (j <= high) { //选取所有非叶结点
if (j < high && a[j] > a[j + 1]) //双结点,先找出子结点里面最小的,不管单双结点,j最终指向两个子结点中的最小值
j++;
if (a[i] > a[j]) { //判断是否要进行更换
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i = j;
j = 2 * i;
}
else
break;
}
}
void myShowPath(int a[], int p) { //找到路径
int flag = 0;
while (p != 0) {
if (flag == 0)
flag = 1;
else
cout << " ";
cout << a[p];
p /= 2;
}
cout << endl;
}
绝对的核心算法,这里有以下几个点需要注意的:
void myAdjust(int a[], int low, int high) { //调整单个非叶结点
int i = low;
int j = 2 * i; //从左子结点开始
while (j <= high) { //选取所有非叶结点
if (j < high && a[j] > a[j + 1]) //双结点,先找出子结点里面最小的;单结点,就不变。不论单双结点,j最终指向子结点中的最小值。
j++;
if (a[i] > a[j]) { //将父结点与值最小的子结点判断是否要进行更换
int temp = a[i];
a[i] = a[j];
a[j] = temp;
i = j;
j = 2 * i;
}
else
break; //如果这轮不发生更换的话,那说明整棵树已经调整好了,因为从下往上调整的,所以底下的肯定也是调整好的
}
}
都说到小顶堆的建立了,怎么能不加上推排序呢。
小顶堆的性质是根结点的数必定是整个树中最小的。
推排序就是每次将根结点的数与末尾的数提取出来,并将最后一个叶结点的数填充到根结点的位置再对根结点进行调整使其再次称为小顶堆,直至最后将所有元素按照顺序提取出来,就是一个有序序列了。
所以在建好堆之后加一个提取过程就行了
排序顺带输出了
void myHeapSort(int a[], int n) {
for (int i = 1; i < n + 1; i++) {
cout << a[1] <<" ";
mySwap(a, 1, n + 1 - i); //交换
myAdjust(a, 1, n - i); //对根结点进行调整
}
cout << endl;
}
void mySwap(int a[], int x, int y) {
int temp = a[x];
a[x] = a[y];
a[y] = temp;
}
标签:存储 heap 路径 show temp 最小 code 建堆 说明
原文地址:https://www.cnblogs.com/Za-Ya-Hoo/p/12731423.html