本练习主要做了几个工作:
1.给定一个数组来初始化二叉堆,第一种方法是通过不断插入,时间复杂度是O(nlgn),第二种方法是先把数组填入二叉堆,再从下标为H->SIZE/2的节点开始下滤,这是因为只有下标小于为H->SIZE/2才有孩子,从而可以用线性时间完成二叉堆的初始化。
2.二叉堆的下滤和上滤,对二叉堆关键字的操作诸如删除最小,增加某关键字值,减少某关键字值等会可能改变堆性质的操作,必须用上滤和下滤来保证二叉堆的性质,比如增加了某关键字的值,该关键字可能往下走,因此对该节点采用下滤,与之相反,减少时则要上滤。
3.删除操作是把要删除关键字的位置先求出来,用二叉树最后一个数字来覆盖这个位置的值,并使堆的大小减1,此时要比较要删除的值与该位置新值,根据大小关系来采用上滤或下滤。
4.给定一个x,用O(K)的时间求出小于x的关键字,主要用了层序遍历的思想,从上往下一层层地走,遇到比x小的把它儿子入栈,循环至栈空。
5.用至多3N/4的时间找出任意一个关键字x的位置,主要是先比较倒数第二层的节点,看x和这层节点的关系,若x大于这层所有的节点,这只需要检查所有叶节点,而叶节点的数量不会超过N/2,倒数第二层的节点不会超过N/4,故此时总的比较次数不会超过3N/4。若x小于这层所有的节点,则只需要检查剩下的N/2个节点即可,满足题意。
// youxianduilie_lianxi.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include<stack>
using namespace std;
struct HeapStruct;
typedef struct HeapStruct *PriorityQueue;
#define MinData -65536
#define notExist -1
struct HeapStruct
{
int Capacity;
int Size;
int *Elements;
};
PriorityQueue Initialize(int MaxElement) //初始化堆
{
PriorityQueue H = (PriorityQueue)malloc(sizeof(HeapStruct)); //1.申请一个堆
if (H == NULL) cout << "out of space";
H->Elements = (int *)malloc(sizeof(int)*(MaxElement + 1)); //2.申请一片地址空间用于存放数组
if (H->Elements == NULL) cout << "out of space";
H->Capacity = MaxElement; //3.其余值初始化
H->Size = 0;
H->Elements[0] = MinData;
return H;
}
bool isFull(PriorityQueue H)
{
if (H->Size >= H->Capacity)
return 1;
else
return 0;
}
void Insert(int x, PriorityQueue H)
{
int i;
if (isFull(H)) //1.先检查堆有没有满
{
cout << "Priority queue is full";
return;
}
for (i = ++H->Size; H->Elements[i / 2] > x; i /= 2) //循环,在Size+1的位置创建一个空穴,
H->Elements[i] = H->Elements[i / 2]; //通过x不断与其父节点比较,空穴不断上移,直到父节点小于X;
H->Elements[i] = x; //此时i的值就是x所插入的位置。
}
PriorityQueue InitByInsert(int MaxElement,const int Given[],int num)
{
PriorityQueue H = Initialize(MaxElement);
for (int i = 0; i < num; i++)
Insert(Given[i], H);
return H;
}
void PercDown(PriorityQueue H, int i) //下滤操作
{
int Tmp;
int child;
for (Tmp = H->Elements[i]; 2 * i <= H->Size; i = child)
{
child = 2 * i;
if (H->Elements[child + 1] && H->Elements[child + 1] < H->Elements[child]) //第一次比较,如果右儿子存在而且比较小
child += 1;
if (Tmp > H->Elements[child]) //i位原来的值是Tmp,当儿子比他还小时就下滤,循环直到儿子都比他大
H->Elements[i] = H->Elements[child]; //每次这样赋值后,child位会被空出来,结合循环i=child
else
break;
}
H->Elements[i] = Tmp;
}
void PercUp(PriorityQueue H, int i) //上滤操作跟下滤操作基本是对偶的
{
int Tmp;
int parent;
for (Tmp = H->Elements[i]; i > 0; i = parent)
{
parent = i >> 1;
if (Tmp < H->Elements[parent])
H->Elements[i] = H->Elements[parent];
else
break;
}
H->Elements[i] = Tmp;
}
PriorityQueue InitByGiven(int MaxElement, const int Given[], int num)
{
PriorityQueue H = Initialize(MaxElement);
for (int i = 1; i <= num; i++)
H->Elements[i] = Given[i - 1];
H->Size = num;
/*下滤的循环
for (int i = H->Size / 2; i > 0; i--) //二叉树节点的数量最多为 H->Size / 2,比如一个满二叉树有一半是叶子
PercDown(H, i);
*/
//上滤的循环
for(int i=2;i<=H->Size;i++)
PercUp(H, i);
return H;
}
int DeleteMin(PriorityQueue H) //删除并返回最小值
{
int i, child;
int minElement, LastElement;
if (H->Size==0) { //先检查是否为空
cout << "is Empty";
return H->Elements[0];
}
minElement = H->Elements[1]; //最小的在堆顶
LastElement = H->Elements[H->Size--]; //删除后Size-1;
H->Elements[1] = LastElement; //堆顶的元素取走以后,留下一个空穴,把堆的最后一个元素放在堆顶上,对其进行下滤即可。
PercDown(H, 1);
return minElement;
}
void Delete(PriorityQueue H, int key) //直接删除
{
int i;
for (i = 1; i <= H->Size; i++) //先找到位置
if (H->Elements[i] == key) break;
H->Elements[i]= H->Elements[H->Size--]; //用最后一个元素覆盖要删除位置的值,再下滤
PercDown(H, i);
}
void rewrite(int oldkey, int newkey, PriorityQueue H)
{
int i;
for (i = 1; i <= H->Size; i++) //先找到位置
if (H->Elements[i] == oldkey) break;
H->Elements[i] = newkey;
if(newkey>oldkey) //比较新旧值,比旧值大的话就要下滤,相当于IncreaseKey操作,比旧值小的话就要上滤,相当于DecreaseKey操作
PercDown(H, i);
else
PercUp(H, i);
}
void lessThan(int x, PriorityQueue H) //用O(K)时间打印出堆中小于x的数
{
if (H->Elements[1] >= x) cout << H->Elements[0];
else
{
int i = 1;
stack<int> reme; //类似层序遍历,当父节点的值大于x时,就不再去检查子节点
reme.push(i);
while (!reme.empty())
{
if (H->Elements[reme.top()] < x)
{
i = reme.top();
cout << H->Elements[reme.top()]<<'\t';
reme.pop();
if (2 * i <= H->Size) reme.push(2 * i);
if (2 * i+1 <= H->Size) reme.push(2 * i+1);
}
else
reme.pop();
}
}
}
int quickFind(PriorityQueue H, int x)
{
int left, right;
for (int i = H->Size / 2; i >= H->Size / 4; i--) //此循环用于检查叶节点的上一层
if (H->Elements[i] < x)
{
left = 2 * i;
right = 2 * i + 1;
if (left <= H->Size&&H->Elements[left] == x)
return left;
if (right <= H->Size&&H->Elements[right] == x)
return right;
}
for (int i = H->Size / 2; i > 0; i--) //如果上面在叶节点检查出来了就不会执这个循环
{
if (x == H->Elements[i])
return i;
}
return notExist;
}
void print(PriorityQueue H)
{
for (int i = 1; i <= H->Size; i++)
cout << H->Elements[i] << '\t';
cout << endl;
}
int main()
{
int Given[] = { 10,12,1,14,6,5,8,15,3,9,7,4,11,13,2 };
//PriorityQueue H=InitByInsert(100, Given, 15);
PriorityQueue H = InitByGiven(100, Given, 15);
print(H);
//lessThan(6, H);
cout << quickFind(H, 12);
//int min = DeleteMin(H);
//cout << min << endl;
//print(H);
//Delete(H, 3);
//print(H);
//rewrite(2,77,H);
//print(H);
while (1);
return 0;
}