大话数据结构读书笔记 三 树

六、树:

1.树:是n个结点的有限集;

有且仅有一个根结点;其余结点可分为m个互不相交的有限集,这些 有限集是子树;

2.度degree:结点拥有子树数;

度为0:叶结点leaf/终端结点;

分支结点:内部结点;

树的度:树内各节点度的最大值;

3.结点之间关系:

(1)孩子child:结点(该结点为双亲parent)的子树的根;

(2)兄弟sibling:同一个双亲的孩子之间;

(3)祖先:从根到该结点所经分支上所有结点;

(4)子孙:以某结点为根的子树中任一结点;

4.(1)节点的层次:根为第一层,根的孩子在第二层以此类推;

(2)深度(高度):树中结点的最大层次;

(3)各子树从左至右有序则为有序树,反之无序;

(4)森林forest:m棵互不相交的树的集合;(子树之间即为森林)

5.ADT:

6.树的存储结构:

(1)双亲表示法:每个结点设指示器指示双亲结点的位置;数据域data,指针域parent;

根节点双亲为-1;

a.结点为两个整型,一个数据,一个标记双亲在数组中位置;整个树为结点数组;

b.上面的方法得到祖先很容易,但要得到孩子需要遍历整个结构;

故引入长子域(结点最左边的孩子);没有孩子的结点长子域-1;

c.要研究兄弟之间关系 ——引入右兄弟域;

(2)孩子表示法:多重链表;结点有多个指针域,指向子树的根;

但每棵树的度不同,有两种方案:

a.各结点度相差不大时,用树的度即可;

b.建立度域;

c.孩子表示法:每个结点的孩子排列,以单链表存储;n个头指针组成线性表,采用顺序存储结构放入一维数组(所有结点存入数组中,用单链表表示它的孩子);即设置两种结点:孩子链表结点、表头数组结点;

(孩子节点(x为其双亲):数据域存储此孩子在表头的下标;指针域指向x的其他孩子)

(表头数组:将所有结点按顺序排列进来;数据域为结点数据信息,指针域指向第一个孩子,即存储了孩子链表的头指针)

这样方便研究孩子和兄弟关系,但不方便研究双亲;双亲孩子表示法:

(3)孩子兄弟表示法:树结点的第一个孩子和右兄弟唯一;故设置两指针表示;

(将复杂树变为二叉树)

7.二叉树(binary tree):

(1)二叉树:n个结点有限集,该集合的元素为空集和由一个根节点和两棵互不相交的左子树、右子树组成的二叉树;

(2)二叉树特点:最多两子树;左右有序(即使只有一个子树也需要区分);

(3)特殊二叉树:

a.斜树:所有结点都只有右树或左树;即线性表,结点数等于深度;

b.满二叉树:所有分支结点都有左右子树;叶子都在同层;(同深度下结点、叶子最多);

c.完全二叉树:n结点二叉树按层编号;i结点与同深度的满二叉树编号i的结点在二叉树中位置相同;(不满,但编号连续);

特点:叶子结点在最下两层,最下层叶子在左边连续,倒二层叶子在右边连续,结点度为1的话只有左结点;

同样结点数,完全二叉树深度最小;

(4)二叉树性质:

a. 第i 层有2^(i-1)个结点;

b. 深度为k的二叉树至多有2^k-1个结点;

c. 任何一棵二叉树T,叶子结点数n0,则度为2的结点数n0-1;(用数分枝线的方法);

d.具有n个结点的完全二叉树深度为(log2n)+1;

e.n结点完全二叉树,按层编号;

若i=1,无双亲;i>1,双亲是结点(i/2);

若2i>n,i无左孩子(叶子结点);否则左孩子是2i;

若2i+1>n,i无右孩子;否则右孩子是2i+1;

(5)二叉树存储结构:

a.二叉树顺序存储:用一维数组存储,下标即为编号,不存在的用null;一般用于完全二叉树,否则有大量空间浪费;

b.二叉链表:一个数据域两个指针域;

(6)二叉树遍历:从根结点出发,按某次序依次访问二叉树所有结点;每个结点有且仅访问一次;(每个都要有操作:树为空则空操作返回)

a. 前序遍历:先访问根,前序遍历左子树再前序遍历右子树;(从根开始蛇形遍历)

运用递归,先遍历访问、打印左树,直到没有左树或无孩子,执行打印右树指令,返回上一级执行打印右树;

b. 中序遍历:

从最左端元素开始蛇形遍历;

运用递归,先访问左树,直到某元素g无左树或者无孩子,打印这个元素,执行访问右树;如果无右树则返回打印上一级、访问右树(也按这个顺序),访问完右树跳回上上一级……;

(对比前序,实际上从先打印再访问变成了先访问再打印)

无左子树为止的元素打印,返回上一级(上一级已经访问过左子树了,直接打印它再访问右子树);返回上上级……

c.后序遍历:

从左到右先叶子后结点访问左右树,最后访问根结点;(从最左开始,有兄弟先打印兄弟(从兄弟的叶子开始),无兄弟时正常蛇形)

即先访问左树直到无左子树,访问这个元素右子树,打印;返回上一级(上一级是已经执行过左右子树访问的,直接打印);再返回上上级(上上级已经执行过访问左子树,则访问右子树,若有右子树则打印右子树再打印它自己,若无右子树则直接打印自己);

(前中后实际就是输出在三个语句中位置;中左右、左中右、左右中);

d.层序遍历:

从第一层开始从左到右遍历;

(7)二叉树的建立:

a.给每个结点的空指针引出虚结点,值为特定值如“#“,称为扩展二叉树;

b.如果为#则是空结点,如果不是#就生成新结点,递推构造左右子树;

若前序遍历AB#D##C##输入数据;

此程序为前序法;实际上就是把遍历中的打印操作改成了生成结点;

(8)线索二叉树:

a. n个结点的二叉链表,一共有2n个指针域;有n-1条分支,即有n+1个空指针,需要提高空间利用率;

b.指向前驱后继的指针称为线索;加上线索后叫线索链表(线索二叉树);

c. 按某种遍历规则,将空的右指针改为指向后继,空的左指针改为指向前驱;此时变为双向链表,称为线索化;

d.区分是否指向前驱后继,引入标志域ltag和rtag,存放0和1;线索是1;

e.线索二叉树结构:

线索化是在遍历的过程中修改空指针;

若为中序遍历:将打印的步骤变成线索化过程;

前驱:如果结点p左指针域为空,前驱结点刚刚访问过且赋值给了pre;故将pre赋值给p->lchild,修改标志域为1;

后继:p的后继还未访问到,则对前驱pre的右指针判断改变,指向p;

完成后需要后移,将p赋值给pre;

f. 线索二叉树的操作同双向链表:添加头结点,lc指向根,rc指向中序遍历时访问的最后一个结点,此时可以任意顺序遍历了;

即先循环到第一个位置打印,此时让它通过修改过的指针向它的后继访问;

8.树、森林、二叉树转换:

(1)树的孩子兄弟法可以将树用二叉链表存储——树转化为二叉树:

a.加线:所有兄弟结点之间加线;

b.去线:每个结点只保留与第一个孩子结点的连线,删除与其他孩子之间的;

c.层次调整:旋转角度;第一个孩子是左孩子,兄弟转化的是右孩子;

(2)森林转化二叉树:森林是若干棵树,每棵树都可以按照兄弟处理;

a.树转化二叉;

b.第一棵二叉树不动,从第二棵树开始,依次把后一棵二叉树根结点作为前一棵二叉树

根节点的右孩子;

(3)二叉树转化为树:

a.加线:某孩子的左孩子存在,将这个左孩子的n个右孩子都作为此结点的孩子;

b.去线:删除原二叉树中所有结点与右孩子的连线;

c.层次调整:使结构分明;

(4)二叉树转化森林:如果二叉的根结点有右孩子就是森林,无就是树;

a.从根开始,右孩子存在就把与右孩子的连线删除,分离的树右孩子存在也连线删除…直到所有右孩子连线删除,得到分离的二叉树,

b.二叉树转化成树;

(5)树与森林遍历:

a.树的遍历:先根遍历——先访问根节点,再从左到右每棵子树;

后根遍历——依次遍历每棵子树,最后根节点;

b.森林的遍历:前序遍历——先访问第一棵树根节点,先根遍历子树;

         后序遍历——先访问第一棵树,后根遍历每棵子树,最后根节点;

c.森林前序遍历与二叉树前序一样,后续与二叉中序一样;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值