ll_abc

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第五章的内容是树以及二叉树。

1、树的定义:1)有且只有一个根结点。 2)除根结点以外的结点互不相交。

树的结构定义是递归形式,于是在创建遍历等多个基本操作都会用到递归。

 

2、树的表示方法:嵌套集合、线性表、凹入表示

 

3、树的基本术语:

1)结点

2)结点的度:结点拥有的子树

3)树的度:各结点度的最大值

4)叶子

5)树的深度:树中结点的最大层次

6)完全二叉树:在存储时可通过对编号的计算找到父、子结点的对应编号

7)满二叉树(深度为K,且含有2的k次方-1个结点)

 

4、二叉树(结构最简单、规律性强,所有树都可以化成唯一的二叉树)

1)二叉树每个结点至多有两颗子树

2)性质:第i层至多有2的i-1次方个结点;深度为k的二叉树至多有2的k-1次方个结点;对任意一颗二叉树其终端结点为n0,度为2的结点树为n2,则n0=n2+1;

 

5、二叉树的存储结构(结点间的关系蕴含在存储位置 不容易实现增加删除)

1)顺序存储结构(线性,某个结点的父/子结点,按照完全二叉树down right 的编号利用下标计算)

但是遇到稀疏的树,会造成空间的极大浪费

2)链式存储结构(在含有n个结点的二叉链表中必有2n个链域,有n-1个非空,则有2n-(n-1)个空链域)

//结点的定义
typedef struct bitnode{
    telemtype data;
    struct bitnode *lchild,*rchild;  //递归
}bitnode, *bitree;

6、遍历

1)先序遍历 (根 左 右)

void preorder( bitree t ){
    if ( t == null ) return;
    cout<< t->data <<" ";
    preorder ( t->lchild );
    preorder ( t->rchild );
}

2)中序遍历 (左 根 右)

3)后序遍历 (右 左 根)

4)层次遍历

void level (bitree t){
    queue q;
    bitree p;

    while(!q.empty()){
        p=q.pop();
        if(p!=null){
            cout<<p->data<<" ";
            q.push(p->lchild);
            q.push(p->rchild);
        }
    }
}
//采用队列来遍历    

7、二叉树的建立

void creatbitree(bitree &t){
    cin>>ch;
    if (ch=='#') 
       { t=null;  retuen;//强行终止}
    else 
    {
        t=new bitnode;
        t->data=ch;
        createbitree (t->lchild);
        createbitree (t->rchild);
    }
}

 

8、深入虎穴的题目

著名的王牌间谍 007 需要执行一次任务,获取敌方的机密情报。已知情报藏在一个地下迷宫里,迷宫只有一个入口,里面有很多条通路,每条路通向一扇门。每一扇门背后或者是一个房间,或者又有很多条路,同样是每条路通向一扇门…… 他的手里有一张表格,是其他间谍帮他收集到的情报,他们记下了每扇门的编号,以及这扇门背后的每一条通路所到达的门的编号。007 发现不存在两条路通向同一扇门。

内线告诉他,情报就藏在迷宫的最深处。但是这个迷宫太大了,他需要你的帮助 —— 请编程帮他找出距离入口最远的那扇门。

深入虎穴
 1 #include <iostream>
 2 #include <queue>
 3 using namespace std;
 4 
 5 typedef struct {
 6     int doors;  //门的数量
 7     int *p;  //指向后面的门的编号序列
 8 }node;
 9 
10 int input(node *a, int n);
11 int level(node *a, int r);
12 
13 int main()
14 {
15     node *a;  //a用于存储整棵树    为啥是*a??
16     int n; //门的数量
17     int root;
18     int t;
19     
20     cin >> n;
21     a = new node[n + 1];   //为a开辟一个新的空间 大小为n(门 的数量)  +1 1是啥?? 因为门牌号从1开始
22 
23     root = input(a, n);
24     t = level(a, root);
25     cout << t << endl;
26 
27     return 0;
28 }
29 
30 int input(node *a, int n) {  //读入n扇门的信息给a数组,返回根所在的门牌号
31 
32     int i, j;
33     bool *vi;  //为了找根
34     vi = new bool[n + 1];
35     for (i = 1; i <= n; ++i) {  //初始化vi 数组的全部元素为false
36         vi[i] = false;
37     }
38 
39     for (i = 1; i <= n; ++i) {  //读入n扇门的信息
40         cin >> a[i].doors;  //直接读入到node里面的结构体的door的数量
41 
42         if (a[i].doors != 0) {
43             a[i].p = new int[a[i].doors];  //当a[i].doors 的数量不为0时 开辟一个大小为门的数量的空间
44 
45             for (j = 1; j <= a[i].doors; ++j) {
46                 cin >> a[i].p[j-1];  //这是啥?? 因为假如门的数量为4,则开辟4个空间,那么如果j 从1 开始就不够空间,所以j 从0 开始   读入会被通向的门
47                 vi[a[i].p[j-1]] = true;  //令被通向的门变为1,则可以找到根
48             }
49 
50         }
51         else //door 为空
52             a[i].p = NULL;
53     }
54 
55     for (i = 1; i < n; ++i) {  //找到根节点
56         if (!vi[i]) return i;
57     }
58 }
59 
60 int level(node *a, int r)
61 {//从a [r] 开始对a进行层次遍历,并返回遍的最后一个节点的编号
62     queue<int> q;
63     
64     int t;
65     q.push(r);
66      
67     while (!q.empty()) {  //非空
68         t = q.front(); //那队列的队头元素
69         q.pop();  //chu队
70 
71         if (a[t].doors!=0) {  //说明t号门后面还有门 则入队
72             for (int i = 0; i < a[t].doors; ++i) {
73                 q.push(a[t].p[i]);
74                 
75             }
76         }
77     }
78     return t; //因为t 不断被赋值,最后访问的还是t
79 }

首先建立一颗树,其本质是建立一个一维数组;

一开始的第一种方法是建立一维数组,每一个下标存储了每一个结点的数据域以及孩子域,但是因为这道题目不是二叉树,开拓的孩子空间不一样的话会造成浪费;

然后就采用有一个首先知道有多少个孩子,然后通过这个数据来new 一个这么大的空间。

然后就是递归构造、遍历。

 

9、树的存储结构

1)双亲表示法:需要找父节点的

2)孩子表示法

3)孩子兄弟表示法(左孩子右兄弟)

 

10、哈夫曼树(最优树  带权路径长度最短的树)

构造:1)根据n个权值构造n棵二叉树

2)选取最小的权值作为左右子树构成一颗新的二叉树,且置新的二叉树的根节点的权值为左右子树的权值之和

3)删除原来的两棵树,并将新的二叉树加入

4)重复操作直到最后只剩一棵树

 

本章最大收获就是基本懂得如何构造以及遍历一棵树。

posted on 2019-05-04 22:51  ll_abc  阅读(112)  评论(2编辑  收藏  举报