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

参考:《啊哈算法》

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

**

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

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

**

树的特性

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

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

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


为了确定一棵树的形态,在树中可以指定一个特殊的结点—–根
如果一个结点没有子结点,这个结点就称为叶结点。
没有父结点的结点称为根结点。
如果一个结点既不是根结点也不是叶结点,则称为内部结点。
每个结点还有深度,深度是指从根结点到这个结点的层数(根为第一层)


二叉树

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

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

  1. 满二叉树
    所有的叶结点都有相同的深度

  2. 完全二叉树
    如果一棵二叉树除了最右边位置上有一个或者几个叶结点缺少外,其他事丰满的二叉树,就是满二叉树。
    (完全二叉树的典型应用就是堆)


堆—神奇的优先队列

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

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

完全二叉树一个性质:最后一个非叶结点是第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];//将堆的最后一个顶点赋值到堆顶
    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大的数

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值