430 likes | 538 Views
6.3 遍历二叉树和线索二叉树 6.3.1 遍历二叉树 6.3.2 建立(生成)二叉树 6.3.3 线索二叉树 6.3.4 表达式二叉树 表达式二叉树 T = ( 第一操作数) (运算符) (第二操作数) 其中:第一操作数、第二操作数也是表达式二叉树, 分别为表达式二叉树 T 的左子树和右子树. 运算符. 第一操作数 左二叉树. 第二操作数 右二叉树. 表达式二叉树. 例 表达式: A + B * C - D /(E - F). -. +. /. A. *. D. -. B. C. E. F. 表达式二叉树.
E N D
6.3 遍历二叉树和线索二叉树 • 6.3.1 遍历二叉树 • 6.3.2 建立(生成)二叉树 • 6.3.3 线索二叉树 • 6.3.4 表达式二叉树 • 表达式二叉树T = (第一操作数) (运算符) (第二操作数) • 其中:第一操作数、第二操作数也是表达式二叉树, • 分别为表达式二叉树T的左子树和右子树 运算符 第一操作数 左二叉树 第二操作数 右二叉树 表达式二叉树
例 表达式: A + B * C - D /(E - F) - + / A * D - B C E F 表达式二叉树 ●前序遍历:- + A * B C / D - E F 前缀表示,前缀表达式,波兰式 ●中序遍历: A + B * C - D /( E - F ) 中缀表示,中缀表达式 ●后序遍历: A B C * + D E F - / - 后缀表示,后缀表达式,逆波兰式
6.3.5 中序遍历序列和前(或后)序序列确定唯一棵二叉树 由前序序列:A D E B C 和(或)后序序列:E D C B A 不能确定唯一棵二叉树(如图T1,T2) A A D B D B E C E C T1 T2 例1. 给定二叉树T的 中序序列:B A C D E G F 前序序列:E A B C DF G 如何求二叉树T? E A F E B C G B,A,C,D G,F D
6.3.5 中序遍历序列和前(或后)序序列确定唯一棵二叉树 例2. 给定二叉树T的 后序序列:B G H E C AJ I F D 中序序列:B A C G E H D I J F 如何求二叉树T?(根结点,左右子树结点) D B,A,C G,E,H I,J,F
6.3.5 中序遍历序列和前(或后)序序列确定唯一棵二叉树 例2. 给定二叉树T的 后序序列:B G H E C AJ I F D 中序序列:B A C G E H D I J F 如何求二叉树T? D A F D B C I B,A,C G,E,H I,J,F E J G H 二叉树T
递归算法 • 1.前序遍历以root为根指针的二叉树 • preorder(struct nodeb *root) • { if (root) //为非空二叉树 • { printf("%c",root->data); //访问根结点 • preorder(root->lchild); //递归遍历左子树 • preorder(root->rchild); //递归遍历右子树 • } • } 6.3.6 遍历二叉树算法 root A B C D E F 二叉树 二叉链表
执行preorder(struct nodeb *t); 地址栈 根指针栈 t t1 1.输出A;L1,t进栈 t2 2.t指向B,记作t1;输出B; L1,t1进栈 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 3.t指向NULL,记作t2;退栈,返回到L1,恢复t1;L2,t1进栈
执行preorder(struct nodeb *t); t t1 t2 t3 4.t指向D,记作t3;输出D; L1、t3进栈 t4 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 5.t指向E,记作t4; 输出E;L1,t4进栈
执行preorder(struct nodeb *t); t t1 6.t指向NULL,记作t5; 退栈,返回到L1,恢复t4; L2、t4进栈 t3 t4 t5 t6 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 7.t指向NULL,记作t6; 退栈,返回到L2,恢复t4; 退栈,返回到L1,恢复t3; L2、t3进栈
执行preorder(struct nodeb *t); t t1 t3 8.t指向F,记作t7; 输出F;L1,t7进栈 t7 t8 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 9.t指向NULL,记作t8; 退栈,返回到L1,恢复t7; L2,t7进栈
执行preorder(struct nodeb *t); t t1 10.t指向NULL,记作t9; 退栈,返回到L2,恢复t7; 退栈,返回到L2,恢复t3; t3 t7 t9 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 退栈,返回到L2,恢复t1; 退栈,返回到L1,恢复t; L2、t进栈
执行preorder(struct nodeb *t); t t10 t11 11.t指向C,记作t10; 输出C;L1,t10进栈 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 12.t指向NULL,记作t11; 退栈,返回到L1,恢复t10; L2、t10进栈
执行preorder(struct nodeb *t); t t10 t12 13.t指向NUII,记作t12; 退栈,返回到L2,恢复t10 preorder(struct nodeb *t) { if (t) { printf("%c",t->data); preorder(t->lchild); L1: preorder(t->rchild); L2:} } 退栈,返回到L2,恢复t; preorder(struct nodeb *t) 执行结束。
2.中序遍历以root为根指针的二叉树 t A B C D E F 二叉树 二叉链表 preorder(struct nodeb *root) //递归算法 { if (root) //为非空二叉树 { preorder(root->lchild); //递归遍历左子树 printf("%c",root->data); //访问根结点,字符型 preorder(root->rchild); //递归遍历右子树 } }
3.后序遍历以root为根指针的二叉树 t A B C D E F 二叉树 二叉链表 preorder(struct nodeb *root) //递归算法 { if (root) //为非空二叉树 { preorder(root->lchild); //递归遍历左子树 preorder(root->rchild); //递归遍历右子树 printf("%c",root->data); //访问根结点 } }
4.中序遍历非递归算法 • preorder(struct nodeb *t) //t为根指针 • { struct bnode *st[maxleng+1];//定义地址栈st[0..maxleng] • int top=0; //置空栈 • do • { while(t) //为非空二叉树 • { if (top==maxleng) exit("overflow");//栈已满,退出 • st[++top]=t; //根地址进栈 • t=t->lchild; //t移向左子树 • } • if (top) //为非空栈 • { t=st[top--]; //弹出根地址 • printf("%c",t->data); //访问根结点 • t=t->rchild; //遍历右子树 • } • } • while(top||t); • } //父结点未访问,或右子树未遍历
6.3.7 遍历二叉树的应用 • 例. 求二叉树中结点的个数 • creat_tree(struct bnode **root) //生成二叉树 • { ........... • } • preorder(struct nodeb *root,int *n)//求二叉树中结点的个数 • { if (root) • { (*n)++; //计数 • preorder(root->lchild,n); //递归遍历左子树 • preorder(root->rchild,n); //递归遍历右子树 • } • } • main() • { int n=0; //n为计数器,假定二叉树已生成 • preorder(root,&n); //root为指针,执行preorder(root,&n) • printf("%d",n); //输出n • }
6.4 树和森林 • 6.4.1 树的存储结构 • 1.双亲表示法/数组表示法/顺序表示法 data parent A 1 2 3 4 5 6 7 B C D E F G 树 t[1..7] struct snode { char data; int parent; }t[maxleng+1};
2.孩子表示法/链接表表示法 • (1)固定大小的结点格式,(设树T的度为k,结点数为n) data child1 childk root A B C D E F G 树T T的链接表
2.孩子表示法/链接表表示法 2.孩子表示法/链接表表示法 (1)固定大小的结点格式,(设树T的度为k,结点数为n) 优点:各结点结构相同。 缺点:空域多达 n(k-1)+1 个。 练习:如T是一棵有 n 个结点的、度为 k 的树,每个 结点有k个指针域,则在nk个指针域中有n (k-1)+1 个指针为空域。 证:由于每个非空指针域指向一个结点,而且除根结点外的每一个结点都有一个指针指向,故在一棵n个结点的树中非空指针的个数恰好是n-1。 又因T中每一结点有k个指针域,故T中共有nk个域,因此,空域的个数为 nk-(n-1)=n(k-1)+1 证毕
2.孩子表示法/链接表表示法 • (2)非固定大小的结点格式 data degree child1 childn root A B C D E F G 树 链接表
3.孩子兄弟表示法/二叉树表示法/二叉链表 firstchild data nextbrother 右兄弟 左孩子 root A B C G D E F H I J K 树 二叉链表
3.孩子兄弟表示法/二叉树表示法/二叉链表 root firstchild data nextbrother 左孩子 右兄弟 A B C G D E F H I J K 树 二叉链表
4.孩子链表表示法/单链表表示法 data first child next 第一个孩子 下一个孩子 表头结点 孩子结点/表结点 1 2 3 4 5 6 7 8 9 10 11 A B C G D E F H I J K 树 表头结点数组
5.带双亲的孩子链表表示法 data parent first child next 第一个孩子 下一个孩子 表头结点 孩子结点/表结点 1 2 3 4 5 6 7 8 9 10 11 A B C G D E F H I J K 树 表头结点数组
6.4.2 树与二叉树的转换 • 1.树 二叉树 A A B C B C D E F G H E D F G H I J K L M J K L M I 树 1.在兄弟之间加连线 A B C D E F G H I J K L M 2.保留根与最左孩之间的连线 删除与其它孩子之间的连线
1.树 二叉树 A B A D C B C I E F D E F G H J K G I J K L M H 2.保留根与最左孩之间的连线 删除与其它孩子之间的连线 L M 二叉树 3.以根为轴心顺时针 方向旋转45度
2.森林 二叉树 G J A F G J A F H I B C D H I K K B C D L M E L M E T2 T3 T1 T4 加线拆线之后 A F G J A F G J B H K B H K C I L C I L E D M E D M B2 B3 B2 B3 B1 BT4 B1 BT4 旋转后变为多棵二叉树 连成一棵二叉树
2.森林 二叉树 A A F G J B F C G B C D H I K E D H J E L M T2 T3 T1 T4 I K 森林F={T1,T2,T3,T4} L M 旋转后,变为一棵二叉树
3. 二叉树 树 A 例1 A B B C D C D E F G E F G H I J H I J 二叉树B A A B B D G C D C F I J E F G E H H I J 树T
3. 二叉树 森林 例2 A A B H B H C D I C D I E F G J K E F G J K 二叉树B A H I K A B H J B D G C D I C F E F G J K E T1 T2 T3 T4
6.4.3 树和森林的遍历 • 1.树的遍历 A 前根遍历:A B G H I C D E J K F 后根遍历:H I G B D J K E F C A B C G D E F H I J K 2.森林的遍历 前序遍历:A B C D E F G H I J K L 中序遍历:B D C A F E H J K I L G G A E B C F H I L D J K
6.6 哈夫曼(Huffman)树及其应用 • 1.路径长度----路径上分枝的数目(连线的数目) • 2.树T的路径长度----从树T的根到其余每个结点的路径长度之和 • 记作PL(T) PL(T1)=1+1+2+2+2=8 PL(T2)=1+2+3+4+5=15 PL(T3)=1+1+2+3+3=10 ● T1,T3各有3个叶子,但其PL值互不相同??? T1 T2 T3 ●当n个结点的二叉树为完全二叉树时,PL(T)具有最小值: ∵ 结点i的层 = log2i + 1 树T的根到结点i的路径长度 = 结点i的层-1 = log2i n ∴ PL(T)= log2i+log2i+...+log2i = ∑log2i i=1
●当n个结点的二叉树为单枝树时,PL(T)具有最大值:●当n个结点的二叉树为单枝树时,PL(T)具有最大值: • PL(T)=0+1+2+...+(n-1) = n(n-1)/2 • 3. 树T的带权路径长度----每个叶子的权与根到该叶子的路径 • 长度的乘积之和,记作WPL(T) • n • WPL(T) = ∑ wklk • i=1 • 其中: n ---树T的叶子数目 wk--- 叶子i的权 • lk--- 树T的根到叶子i的路径长度 7 WPL(T)=6*1+3*2+4*3+9*3=51 5 6 3 8 4 9 T1
4.哈夫曼树/最佳树/最优树---- • 在具有n个相同叶子的各二叉树中,WPL(T)最小的二叉树。 28 28 28 11 17 14 14 10 18 5 6 7 10 11 3 4 10 6 12 3 4 5 6 5 7 T1 T3 T2 4 3 ● PL(T1)=1+1+2+2+2+2+3+3=16 ● WPL(T1)=5*3+6*3+3*2+4*2+10*2=67 ● PL(T2)=1+1+2+2+3+3+4+4=20 ● WPL(T2)=10*1+6*2+5*3+4*4+3*4=65 ● PL(T3)=1+1+2+2++2+2+3+3=16 ● WPL(T3)=5*3+6*3+3*2+4*2+10*2=63
5.哈夫曼算法 • 例 给定权集合{4,5,3,6,10},构造哈夫曼树 • 1.按权值大小排序: 3,4,5,6,10 • 2.生成森林: 3 4 5 6 10 T1 T2 T3 T4 T5 3.合并两棵权最小的二叉树,并排序,直到为一棵二叉树: 5 6 7 10 7 10 11 3 4 3 4 5 6 28 11 17 11 17 5 6 7 10 5 6 7 10 3 4 3 4
6.最小冗余码/哈夫曼码 • ● ASCII码/定长码 • ab12: 01100001 01100010 00110001 00110010 • 97 98 49 50 • ●哈夫曼码/不定长码 • 能按字符的使用频度,使文本代码的总长度具有最小值。 • 例. 给定有18个字符组成的文本: • A A D A T A R A E F R T A A F T E R • 求各字符的哈夫曼码。 • (1) 统计: (2) 构造Huffman树: 1 2 2 3 3 7
(2) 构造Huffman树: 3 2 3 3 3 7 1 2 2 3 3 7 1 2 合并1和2 排序 5 3 3 5 7 2 3 3 3 7 2 3 1 2 1 2 排序 合并2和3
(2) 构造Huffman树: 6 5 6 7 3 3 5 7 3 3 2 3 2 3 1 2 排序 1 2 合并3和3 7 11 11 5 6 5 6 7 3 3 3 3 2 3 2 3 1 2 1 2 合并5和6 排序
(2) 构造Huffman树: (3) 在左分枝标0,右分枝标1: 18 18 0 1 7 11 7 11 1 0 A 5 6 5 6 0 1 0 1 3 3 3 3 2 3 2 3 0 1 F T R 1 2 1 2 D E 合并7和11 Huffman树 (4) 确定Huffman编码: 特点:任一编码不是其它编码的前缀
如何译码? 18 0 1 7 11 1 0 A 5 6 0 1 0 1 3 3 2 3 0 1 F T R 1 2 D E Huffman树 例. 给定代码序列: 0 0 1 0 0 0 1 1 1 0 1 0 1 0 1 0 1 1 1 10 文本为:A A F A R A D E T
一、填空 • 1.假设含有20个元素的线性表顺序存储, 欲在它的第11和 • 第12个元素之间插入一个新元素, 共需移动 ______元素。 • 2.设数组A[1..6, 1..8 ]的基地址为1000,每个元素占2 • 个存储单元,若以行序为主序顺序存储, 则元素A[4,5]的存储 • 地址为 _________ 。 • 3. 一棵深度为5的二叉树最多有 ___________ 个结点。 • 4.具有n个结点的二叉树的二叉链表共有 ________ 个空 • 链域和______个非空链域 。 • 5.具有n个结点的满二叉树共有_____________个叶结 • 点和___________个非叶结点。 • 6.若一棵二叉树中有10个度为2的结点,则它有 _______ • 个叶子。 • 二、回答问题:二叉树有哪几种基本形态? 画图说明之。
三、给定21个字符组成的文本(电文): • A A A B B B A A A A B B B C C A C C D D E, • 试为字符 A、B、C、D、E 设计哈夫曼(Huffman)编码。 • 1.画出相应的哈夫曼树; • 2.分别列出 A、B、C、D、E、F 的哈夫曼码 四、画图 1.画出数组 A[2][3] 的以行序 为主序的顺序存储结构。 2.依次将元素D,C,A,B,E插入一 个初始状态为空的链式栈中,试画出 插入完成之后的链式栈。 3.画出右面二叉树的先序线索二 叉树、中序线索二叉树、后序线索二 叉树。 A B H C D I F J K G E 二叉树