您的位置:首页 > 其它

《啊哈算法》第七章 神奇的树

2016-05-15 11:03 239 查看
参考:《啊哈算法》

树其实就是不包含回路的连通无向图

**

连通是说其其中任意两点之间是可达的

不包含回路就是说一定能够不会出现走一圈的情况

**

树的特性

一棵树中的任意两个结点有且仅有唯一的一条路径连通

一棵树如果有n个结点,那么它一定恰好有n-1条边

在一棵树中加一条边将会构成回路

为了确定一棵树的形态,在树中可以指定一个特殊的结点—–根

如果一个结点没有子结点,这个结点就称为叶结点。

没有父结点的结点称为根结点。

如果一个结点既不是根结点也不是叶结点,则称为内部结点。

每个结点还有深度,深度是指从根结点到这个结点的层数(根为第一层)

二叉树

严格递归定义:二叉树要么为空,要么由根结点、左子树和右子树组成,而左子树和右子树分别是一棵二叉树

二叉树中还有两种特殊的二叉树:

满二叉树

所有的叶结点都有相同的深度

完全二叉树

如果一棵二叉树除了最右边位置上有一个或者几个叶结点缺少外,其他事丰满的二叉树,就是满二叉树。

(完全二叉树的典型应用就是堆)

堆—神奇的优先队列

所有的父结点都比子结点要小的完全二叉树称为最小堆(小顶堆)

所有的父结点都比子结点要大的完全二叉树称为最大堆(大顶堆)

完全二叉树一个性质:最后一个非叶结点是第n/2个结点

建堆及堆排序的展示(小顶堆)

算法分析:先输入数据存在数组中建立一棵完全二叉树,然后从最后一个非叶结点一次向前进行调整。调整结束后就满足小顶堆,根结点就是最小值,此时记录下根结点,并将其从堆中删除,将最后一个结点送到根结点的位置,此时不满足小顶堆,我们就向下调整使其再次满足。重复步骤至完全二叉树为空

#include <iostream>
#include <cstdio>
using namespace std;
//向下调整
int h[102];
int n;
void siftdown(int i) {
int t,flag;
while(i*2<=n&&flag==0) {
if(h[i]>h[i*2]) {//先于左结点比
t=i*2;
} else {
t=i;
}
if(i*2+1<=n) {//是否有右结点
if(h[t]>h[i*2+1]) {
t=i*2+1;
}
}
if(t!=i) {
swap(h[i],h[t]);//交换
i=t;
} else {
flag=1;//满足顶堆条件的信号传出
}
}
}

//建堆
void create() {
//从最后一个非叶结点到第一个结点依次进行向下调整
for(int i=n/2; i>=1; i--) {
siftdown(i);
}
}
//删除最大元素
int deletemax() {
int t;
t=h[1];//用临时变量记录堆顶点的值
h[1]=h
;//将堆的最后一个顶点赋值到堆顶
n--;//堆元素少一
siftdown(1);//向下调整
return t;//返回记录的堆的顶点的最小值
}
int main() {
int num;
scanf("%d",&num);
for(int i=1; i<=num; i++) {
scanf("%d",&h[i]);
}
n=num;
create();
for(int i=1; i<=num; i++) {
printf("%d ",deletemax());
}
return 0;
}


测试样例

14
99 5 36 7 22 17 46 12 2 19 25 28 1 92


当然还可以通过建立大顶堆的方式进行堆排序。

虽然这里的堆排序看上去很是费时,但是要是使用在添加一个数,或者添加一个数同时删除一个数的情况时就比较的高效了。比如我们删除一组数的最小值,并新增一个数,同时求新数组的最小值时。直接将新增的数放到小顶堆的堆顶(本身这里之前是最小的数),然后调整到符合小顶堆时的堆顶就是新的最小值。

另外要是我们新增一个数,但是不进行任何删除操作,只是最快求出新的最小值时,只需要将新增的数添加到最后。然后一路向上调整至符合要求,新的堆顶就是新的最小值。

堆还经常被用作求一个数列中第k大的数,只需要建立一个大小为k的小顶堆,堆顶就是第k大的数
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: