在文章的开始先给大家分享一下我自己认为课件里比较重要的概念性的内容,这是我当时截取的图片,放在这里供大家参考,然后具体的代码部分,包括一些编程技巧放在文章末尾了。
推荐大家用最大堆的代码去练手
这边仅是个人见解,后面的堆排序使用最大堆,提前熟练了,之后会事半功倍。
以下是代码部分:
#include<iostream>
using namespace std;
typedef int ElementType;
const int Error = -1;
const int MinData = -10000;
const int MaxSize = 1000;
typedef struct HeapStruct* MinHeap; //使用MinHeap指针可以方便地使用堆中的数据成员
//将最小堆的所有属性统一封装在HeapStruct的结构体变量当中
struct HeapStruct {
ElementType* Elements;
int capacity; //最大容量
int size; //数组当前的尺寸
};
MinHeap Create() {
MinHeap H = new HeapStruct;
H->Elements = new ElementType[MaxSize + 1];
H->capacity = MaxSize;
H->size = 0;
H->Elements[0] = MinData; //初始化哨兵元素
return H;
}
void sort(MinHeap H, int i) {
int parent, child;
ElementType tmp = H->Elements[i];// 把根节点保存起来(只要根节点的位置放好了,后面就已经有序了)
for (parent = i;parent * 2 <= H->size;parent = child) { //把child赋值给parent是为了往树的深处进行遍历
child = parent * 2;
if (child<H->size && H->Elements[child]>H->Elements[child + 1])
child++; //找出左右节点当中较小的元素
if (H->Elements[child] > tmp)break; //tmp一旦比当前节点的左右儿子节点都小了,就说明已经找到了合适的位置了
else
H->Elements[parent] = H->Elements[child]; //是把儿子赋值给父亲,因为父亲的值其实已经有备份了,在此可以直接赋值
}
H->Elements[parent] = tmp;
}
void adjust(MinHeap H) {
int i = H->size / 2; //从倒数最后一个有孩子节点的父亲开始调整最小堆
for (;i >=1;i--)
sort(H, i);
}
// 遍历
void bl(MinHeap H) {
for (int i = 1;i <= H->size;i++) {
cout << H->Elements[i] << " ";
}
cout << endl;
}
int main() {
MinHeap H;
H = Create();
int n;
cout << "请输入元素的个数:";
cin >> n;
cout << "请输入元素:";
for (int i = 0;i < n;i++)
cin >> H->Elements[++H->size];
adjust(H);
bl(H);
system("pause");
return 0;
}
先声明一点,有一些博主喜欢把常量使用宏定义的方式来进行初始化,其实这种做法并不是完美的,甚至有许多的弊端,在此不详细阐述,推荐直接使用const关键字来初始化常量。
在最小堆的调整代码中最核心的部分当属sort()排序函数,下面我们单独针对sort()函数来进行一番详细的解释。
void sort(MinHeap H, int i) {
int parent, child;
ElementType tmp = H->Elements[i];// 把根节点保存起来(只要根节点的位置放好了,后面就已经有序了)
for (parent = i;parent * 2 <= H->size;parent = child) { //把child赋值给parent是为了往树的深处进行遍历
child = parent * 2;
if (child<H->size && H->Elements[child]>H->Elements[child + 1])
child++; //找出左右节点当中较小的元素
if (H->Elements[child] > tmp)break; //tmp一旦比当前节点的左右儿子节点都小了,就说明已经找到了合适的位置了
else
H->Elements[parent] = H->Elements[child]; //是把儿子赋值给父亲,因为父亲的值其实已经有备份了,在此可以直接赋值
}
H->Elements[parent] = tmp;
}
首先按照套路定义变量parent和child,然后用tmp把根节点保存起来,然后寻找出儿子节点中较小的与tmp变量进行计较,最终目的当然是为了寻找第i个节点应当存放的合理的位置,需要注意的是我们要用Element[child]的值去覆盖Element[parent]的值,因为上面的序列是有序的而且parent已经有一个备份了,所以不用担心覆盖后会出现数据丢失的问题。还有一个注意点是,我们遍历的方向是往树的深处走的,所以循环条件中把parent=child。