陈越《数据结构》第三讲 树(上)

3.1 树与树的表示


3.1.1 引子:查找


分层次组织在管理上具有更高的效率!


1. 定义
根据某个给定 关键字K ,从 集合R 中找出关键字与K 相同的记录。


2.分类:
- 静态查找:集合中 记录是固定 的;
- 动态查找: 集合中 记录是动态变化的。


3.静态查找的方法

  • 方法1: 顺序查找(时间复杂度为 O(n) );
    //在数组的头部,建立 ,可减少判断的分支。
    这里写图片描述

  • 方法2:二分查找(时间复杂度 O(logN) )。

    这里写图片描述

问:

在二分查找中,我们是取mid等于left和right的中间值,即用等分的方法进行查找。
那为什么一定要等分呐?能不能进行“黄金分割”?也就是mid=left+0.618(right-left),当然mid要取整数。如果这样查找,时间复杂性是多少?也许你还可以编程做个试验,比较一下二分法和“黄金分割”法的执行效率。

答:
二分法每次能有100%的概率能只剩50%的数据,每次剩下的期望为50%,即每次除以2。所以时间复杂度是: log2(N)
而黄金分割的话每次都有 0.618 的概率剩 0.618 0.382 的概率剩0.382,每次剩下的期望为 0.528 ,即 1/0.528=1.894 。所以时间复杂度是: log1.894(N)=1.085log2(N)

3.1.2 树


  1. 定义: nn0 个结点构成的有限集合
    • n=0 时,称为 空树
    • 树中有一个称为“ (Root ) ”的特殊结点, 用 r 表示;
    • 其余结点可分为m(m>0) 个 互不相交的 有限集,其中每个集合本身又是一棵树,称为原来树的“ 子树 (SubTree )”。

2.

  • 子树是 不相交 的 ;
  • 除了根结点外, 每个结点有且仅有一个父结点
  • 一棵N 个结点的树有N-1 条边

3.
树是保证节点联通的最小的一种联通方式!

  1. 结点的度(Degree ): 结点的 子树个数
  2. :树的所有结点中最大的度数;
  3. 叶结点 (Leaf): 度为0 的结点;
  4. 父结点 (Parent ):有子树的结点是其子树的根结点的父结点;
  5. 子结点 (Child ):若A 结点是B 结点的父结点,则称B 结点是A 结点的子结点;子结点也称 孩子结点
  6. 兄弟结点 (Sibling ):具有同一父结点的各结点彼此是兄弟结点;
  7. :从结点 n1 nk 路径 为一个结点序列 n1,n2,,nk,nini+1 的父结点。路径所包含边的个数为 路径的长度 ;
  8. 祖先结点(Ancestor) :沿 树根到某一结点路径 上的所有结点都是这个结点的祖先结点;
  9. 子孙结点(Descendant) :某一结点的 子树中的所有结点 是这个结点的子孙;
  10. (Level):规定 根结点在1 层 ,其它任一结点的层数是其父结点的层数加1;
  11. (Depth) :树中所有结点中的 最大层次 是这棵树的深 度。

4.

儿子-兄弟表示法
所需的总空间为 3N (N为节点数)。
这里写图片描述

问:
树的集合称为森林。是否也可以使用“儿子-兄弟”表示法存储森林?如何实现?
答:
可以,不同树的根节点看成是这棵树的入口,各个树之间可以看做是兄弟节点。


3.2 二叉树及存储结构


3.2.1 二叉树

  1. 定义:一个有穷的结点集合。

    • 这个集合 可以为空
    • 若不为空,则它是由 根结点 和称为其 左子树 TL 右子树 TR 的两个不相交的二叉树组成。
  2. 这里写图片描述

    • 一个二叉树第 i 层的最大结点数为: 2i1 i1
    • k 的二叉树有最大结点总数为: 2k1 k1
    • T ,若 n0 表示叶结点的个数、 n2 是度为 2 的非叶结点个数,那么两者满足关系n0=n2+1

问:
讨论3.3: m 叉树中各类结点数之间的关系:
在二叉树中,我们知道叶结点总数与有两个儿子的结点总数之间的关系是:
那么类似关系是否可以推广到m叉树中?也就是,如果在 m 叉树中,叶结点总数是,有一个儿子的结点总数是,有2个儿子的结点总数是,有3个儿子的结点总数是,…。那么,之间存在什么关系?
答:
这里写图片描述
4.

  • 引用块内容
  • 遍历又分为:

3.2.2 二叉树的存储结构


  1. 顺序存储(主要针对 );

  2. 链式存储


typedef struct PloyNode *Polynomial;//这句有什么用呢?
typedef struct PolyNode{//这两个typedef有什么联系吗?
int coef;
int expon;
Polynomial link;//这里是定义了一个相当于next后继指针吗?
}
如果按这样定义好了之后,如何定义一个这样的链表?

:
1. typedef struct PloyNode *Polynomial;
这句是说:定义了一个结构体变量的指针,以后你可以直接用Polynomial来代表struct PolyNode *这个数据.
2. typedef struct PolyNode{
int coef;
int expon;
Polynomial link; }

这个也是一个定义,定义了一个结构体变量,简化了结构体变量的定义。你可以用PolyNode来代表整个结构体变量。

再来说一下他们之间的联系: 第一个是定义的一个简化的指针变量:struct PolyNode * ; 第二个定义了一个简化的结构体变量PolyNode来代替:typedef struct PolyNode;

综上是否可理解为:指针变量Polynomial具有结构体PolyNode的结构,即L=(Polynomial)malloc(sizeof(PolyNode))这样就生成一个新的PloyNode结点。


3.3 二叉树的遍历


1.运用递归函数进行遍历


  1. 引用块内容


  2. 这里写图片描述


  3. 这里写图片描述

    • 三种遍历算法的遍历路径是一样的,只是访问各节点的时机不同。
      这里写图片描述

2.非递归遍历(使用

这里写图片描述
>

问:
讨论3.4 如何用堆栈实现后序遍历的非递归程序?
我们前面看到,借助堆栈可以实现前序遍历、中序遍历的非递归程序,而且两者的程序结构几乎一样。
答:
不行,不能用前面同样的结构进行对比处理。
可以利用两个堆栈和一个指针进行处理。

3.层序遍历(使用

这里写图片描述

问:
将层序遍历中的队列改为堆栈:
如果将层序遍历中的队列改为堆栈,是否也是一种树的遍历?可以应用这种方法改造出一种前序、中序、后序的非递归遍历吗?
答:

就是把数据 push 到栈A,再把栈A的数据依次 pop push 到栈B。那么先入栈A的数据就会移到栈B的顶部,对栈B进行 pop 时就会先出栈B。恰恰符合了队列的本质——First in First out

把数据都 enqueue 到队列A,如果你想取出最后一个 enqueue 的数据(即Last in),那么就把队列A的数据依次 dequeue enqueue 到队列B,但是不能全部移到队列B,要留下一个。这一个就是你需要的数据把它 dequeue 出来,就会得到 Lastin 的数据。这个过程,相当于对栈进行了依次 pop 操作,符合——Last in First out。需要再次 pop 的时候,按照同样的方法,只需将对A、B的操作交换就可以了。


4.二叉树的应用

例1:输出二叉树中的 叶子结点。
这里写图片描述

例2. 求二叉树的高度
这里写图片描述

例3. 由 任意两种 遍历序列确定二叉树,对么?


3.4 小白专场:树的同构


这里写图片描述

程序:

//author: Paul-Huang
//data: 18/6/2017

#include<stdio.h>
#include <stdlib.h>
#include<process.h>//引入头文件

#define MaxTree 10
#define Null -1 
#define  ElementType char 
#define  Tree int

//建立动态链表
struct TreeNode
{
    ElementType node;
    Tree Left;
    Tree Right;
}Tree1[MaxTree],Tree2[MaxTree];

Tree BuildTree(struct TreeNode T[]);
int Isomprphic(Tree root1, Tree root2);

int main()
{
    Tree R1, R2;
    R1 = BuildTree(Tree1);
    R2 = BuildTree(Tree2);
    if (Isomprphic(R1, R2)){
        printf("Yes\n");
    }
    else{
        printf("No\n");
    }

    system("pause");//暂停往下执行 按下任意键继续
    return 1;
}

Tree BuildTree(struct TreeNode T[])
{
    Tree check[MaxTree], Root = Null; //root = Null 空树则返回Null
    char cl, cr; 
    int i,N;
    scanf("%d\n",&N);
    if (N) {
        for ( i = 0; i < N; i++)
            check[i] = 0;
        //输入数值,并建立链表
        for (i = 0; i < N; i++)
        {
            scanf("%c %c %c\n", &T[i].node, &cl, &cr);
            if (cl != '-'){
                T[i].Left = cl - '0';
                check[T[i].Left] = 1;
            }
            else{
                T[i].Left = Null;
            }
            if (cr != '-'){
                T[i].Right = cr - '0';
                check[T[i].Right] = 1;
            }
            else{
                T[i].Right = Null;
            }
        }
        //查找二叉树的根
        for (i = 0; i < N; i++)
            if (!check[i])
                break;
        Root = i;
    }
    return Root;

}


int Isomprphic(Tree root1, Tree root2)
{
    if ((root1 == Null) && (root2 == Null))/* both empty */
        return 1;
    if (((root1 == Null) && (root2 != Null)) || ((root1 != Null) && (root2 == Null)))
        return 0;/* one of them is empty */

    if (Tree1[root1].node != Tree2[root2].node)
        return 0; /* roots are different */

    if ((Tree1[root1].Left == Null) && (Tree2[root2].Left == Null))
        /* both have no left subtree */
        return Isomprphic(Tree1[root1].Right,Tree2[root2].Right);

    if ((Tree1[root1].Right == Null) && (Tree2[root2].Right == Null))
        /* both have no right subtree */
        return Isomprphic(Tree1[root1].Left, Tree2[root2].Left);

    if ((Tree1[root1].Left != Null) && (Tree2[root2].Left != Null) &&
        ((Tree1[Tree1[root1].Left].node) == (Tree2[Tree2[root2].Left].node)))
        /* no need to swap the left and the right */
        return (Isomprphic(Tree1[root1].Left, Tree2[root2].Left)&&
        Isomprphic(Tree1[root1].Right,Tree2[root2].Right));
    else/* need to swap the left and the right */
        return (Isomprphic(Tree1[root1].Left, Tree2[root2].Right) &&
            Isomprphic(Tree1[root1].Right, Tree2[root2].Left));

}

这里写图片描述

#include<stdio.h>
#include<iostream>

#define MaxTree 10
#define ElementType int
#define Tree int 
#define Null -1

struct TreeNode{
    ElementType Element;
    Tree left;
    Tree right;
}T[MaxTree];

Tree BulidTree(struct TreeNode T[]);
void FindLeave(Tree R);

int main()
{
    Tree R;
    R = BulidTree(T);
    FindLeave(R);
    //system("pause");//暂停往下执行 按下任意键继续
    return 0;
}

Tree BulidTree(struct TreeNode T[])
{
    int i,N;
    char cl,cr;
    Tree check[MaxTree],Root = Null;
    scanf("%d\n",&N);
    for(i=0;i<N;i++)check[i] = 0;
    if(N)
    {
        for(i=0;i<N;i++)
        {
            T[i].Element = i;
            scanf("\n%c %c",&cl,&cr);
            if(cl != '-')
            {
                T[i].left = cl - '0';
                check[T[i].left] = 1;
            }
            else T[i].left = Null;
            if(cr !='-')
            {
                T[i].right = cr - '0';
                check[T[i].right] = 1;
            }
            else T[i].right = Null;
        }
        for(i=0;i<N;i++)
            if(!check[i])break;
        Root = i;
    }
    return Root;
}

void FindLeave(Tree R)
{
    int leaves = 0;
    int queue[MaxTree];
    int front = 0,rear = 0;

    queue[rear++] = R;
    while(rear - front)
    {

        if((T[queue[front]].left != -1)) 
            queue[rear++] = T[queue[front]].left;       //左边如果不为空,加入队列
        if((T[queue[front]].right != -1)) 
            queue[rear++] = T[queue[front]].right;      //右边如果不为空,加入队列

        if((T[queue[front]].left == -1)&&
            (T[queue[front]].right == -1))
        {   
            if(leaves) printf(" ");
            printf("%d",queue[front]);
            ++leaves;
        }                                   //左右两边为空,则是叶子节点,输出
        front++;

    }
}
  • 12
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值