数据结构课件 第6章
- 格式:pdf
- 大小:1.43 MB
- 文档页数:72
数 据 结 构 第6章 树和二叉树第6章树和二叉树•树是一类重要的非线性数据结构,是以分支关系定义的层次结构6.1 树的定义和基本术语•树(tree)是n (n >0)个结点的有限集T ,在任一个非空的树中–有且仅有一个特定的结点,称为树的根(root)–当n >1时,其余结点可分为m (m >0)个互不相交的有限集 T 1,T 2,…,T m ,其中每一个集合本身又是一棵树,称为根的子树(subtree)AC D E FGH IJKLMB6.1 树的定义和基本术语•基本术语–结点(node)—表示树中的元素,包括数据项及若干指向其子树的分支–结点的度(degree)—结点拥有的子树数–叶子(leaf)—度为0的结点–孩子(child)—结点子树的根称为该结点的孩子–双亲(parents)—孩子结点的上层结点叫该结点的~–兄弟(sibling)—同一双亲的孩子–树的度—一棵树中最大的结点度数–结点的层次(level)—从根结点算起,根为第一层,它的孩子为第二层……–深度(depth)—树中结点的最大层次数–森林(forest)—m(m ≥ 0)棵互不相交的树的集合线性结构和树型结构的比较线性结构树型结构第一个数据元素(无前驱)根结点(无前驱)最后一个数据元素(无后继)多个叶子结点(无后继)其它数据元素(一个前驱、一个后继)其它数据元素(一个前驱、多个后继)6.2 二叉树•二叉树定义–二叉树是n(n ≥ 0)个结点的有限集,它或为空树,或是由一个根结点加上两棵分别称为左子树和右子树的、互不交的二叉树组成。
•二叉树特点–每个结点至多有二棵子树(即不存在度大于2的结点)–二叉树的子树有左、右之分,且其次序不能任意颠倒6.2 二叉树•二叉树的基本形态AA B ABAB CΦ空二叉树只有根结点的二叉树右子树为空左子树为空左右子树均非空6.2 二叉树•二叉树性质1–在二叉树的第i 层上至多有2i-1个结点。
(i≥1)•证明:用归纳法证明之1.i=1时,只有一个根结点,2i-1 = 20 = 1;是对的2.假设对所有j(1 ≤j<i)命题成立,即第j层上至多有2j-1个结点,那么,第i-1层至多有2i-2个结点又二叉树每个结点的度至多为2,第i层上最大结点数是第i-1层的2倍,即2i-2 * 2 = 2i-1 故命题得证6.2 二叉树•二叉树性质2–深度为k的二叉树上至多含 2k-1 个结点(k≥1)。
–证明基于上一条性质,深度为k的二叉树上的结点数至多为20+21+ …+2k-1 = 2k-1 。
6.2 二叉树•二叉树性质3–对任何一棵二叉树,若它含有n 0 个叶子结点、n 2 个度为 2 的结点,则必存在关系式:n 0 = n 2+1。
–证明:设n 1 为二叉树中度为1的结点数,二叉树上结点总数 n = n 0 + n 1 + n 2 ,又二叉树上分支总数 b = n 1 +2 n 2 ,而 b = n -1 = n 0 + n 1 + n 2 - 1,由此, n 0 = n 2 + 1 。
6.2 二叉树•满二叉树–指的是深度为k 且含有2k -1个结点的二叉树。
–特点•每一层上的结点数都是最大结点数1231145891213671014156.2 二叉树•完全二叉树–深度为k ,有n 个结点的二叉树当且仅当其每一个结点都与深度为k 的满二叉树中编号从1至n 的结点一一对应。
–特点•叶子结点只可能在层次最大的两层上出现•对任一结点,若其右分支下子孙的最大层次为l ,则其左 分支下子孙的最大层次必为l 或l+11231145891267106.2 二叉树•二叉树性质4–具有 n 个结点的完全二叉树的深度为 +1– 证明设完全二叉树的深度为 k则根据第二条性质得 2k-1≤ n < 2k 即 k-1 ≤ log 2 n < k因为 k 只能是整数,因此, k =� + 1 。
2log n ⎢⎥⎣⎦2log n ⎢⎥⎣⎦6.2 二叉树•二叉树性质5–若对含 n个结点的完全二叉树从上到下且从左至右进行 1 至n 的编号,则对完全二叉树中任意一个编号为 i的结点:1.若i=1,则该结点是二叉树的根,无双亲,否则,编号为⎣i/2⎦的结点为其双亲结点;2.若2i>n,则该结点无左孩子,否则,编号为2i的结点为其左孩子结点;3.若 2i+1>n,则该结点无右孩子结点,否则,编号为2i+1 的结点为其右孩子结点。
6.3 二叉树的存储结构•顺序存储结构–特点•结点间关系蕴含在其存储位置中•浪费空间,适于存满二叉树和完全二叉树#define MAX_TREE_SIZE 100 // 二叉树的最大结点数typedef TElemType SqBiTree[MAX_TREE_SIZE]; // 0号单元存储根结点SqBiTree bt;#define MAX_TREE_SIZE 100 // 二叉树的最大结点数typedef TElemType SqBiTree[MAX_TREE_SIZE]; // 0号单元存储根结点SqBiTree bt;–实现:按满二叉树的结点层次编号,依次存放二叉树中的数据元素二叉树的存储结构(顺序存储)12345678910a b c d e 0f gab cgde f6.3 二叉树的存储结构•二叉树的链式存储结构–– 二叉链表typedef struct _BiTNode { // 结点结构 TElemType data;struct _BiTNode *lchild, *rchild; // 左右孩子指针} BiTNode, *BiTree;typedef struct _BiTNode { // 结点结构 TElemType data; struct _BiTNode *lchild, *rchild; // 左右孩子指针} BiTNode, *BiTree;lchild data rchild二叉链表例A B CDE FG A BCD E FG^^^^^^^^在n个结点的二叉链表中,有n+1个空指针域Root6.3 二叉树的存储结构•二叉链表的性质–在n 个结点的二叉链表中,有n +1个空指针域–证明:设共有t 个空指针域n 个结点共有2n 个指针域所以,2n=t+n 1+2n 2而n=n 0+n 1+n 2t =2n 0+n 1=n 0+n 1+n 0=n 0+n 1+n 2+1=n +15.3 二叉树的存储结构•二叉树的链式存储结构—三叉链表typedef struct _TriTNode { // 结点结构 TElemType data;struct _TriTNode *lchild, *rchild;// 左右孩子指针struct _TriTNode *parent; //双亲指针} TriTNode, *TriTree;typedef struct _TriTNode { // 结点结构 TElemType data; struct _TriTNode *lchild, *rchild;// 左右孩子指针 struct _TriTNode *parent; //双亲指针 } TriTNode, *TriTree;lchild data rchildparent三叉链表例rootA B CDE FGAB C D EF G ^^^^^^^^^6.3 遍历二叉树和线索二叉树•问题的提出–顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。
•“访问”的含义可以很广,如:输出结点的信息等。
•“遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。
而二叉树是非线性结构,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径遍历的问题。
6.3 遍历二叉树和线索二叉树•对“二叉树”而言,可以有三条搜索路径:–先上后下的按层次遍历;–先左(子树)后右(子树)的遍历;–先右(子树)后左(子树)的遍历。
6.3 遍历二叉树和线索二叉树•先(根)序的遍历算法–若二叉树为空树,则空操作;否则,•访问根结点•先序遍历左子树•先序遍历右子树。
// 采用递归算法void PreOrderTraverse(BiTree T, void (*visit)(TElemType &e)){if (T) {visit(T->data); // 访问结点 PreOrderTraverse(T->lchild, visit); // 遍历左子树 PreorderTraverse(T->rchild, visit);// 遍历右子树}}// 采用递归算法void PreOrderTraverse(BiTree T, void (*visit)(TElemType &e)){ if (T) { visit(T->data); // 访问结点 PreOrderTraverse(T->lchild, visit); // 遍历左子树 PreorderTraverse(T->rchild, visit);// 遍历右子树 }}6.3 遍历二叉树和线索二叉树•中(根)序的遍历算法–若二叉树为空树,则空操作;否则,•中序遍历左子树•访问根结点•中序遍历右子树。
// 采用递归算法void InOrderTraverse(BiTree T, void (*visit)(TElemType &e)){if (T) {InOrderTraverse(T->lchild, visit); // 遍历左子树 visit(T->data); // 访问结点 InOrderTraverse(T->rchild, visit);// 遍历右子树}}// 采用递归算法void InOrderTraverse(BiTree T, void (*visit)(TElemType &e)){ if (T) { InOrderTraverse(T->lchild, visit); // 遍历左子树 visit(T->data); // 访问结点 InOrderTraverse(T->rchild, visit);// 遍历右子树 }}6.3 遍历二叉树和线索二叉树•后(根)序的遍历算法–若二叉树为空树,则空操作;否则,•后序遍历左子树•后序遍历右子树。
•访问根结点// 采用递归算法void PostOrderTraverse(BiTree T, void (*visit)(TElemType &e)){if (T) {PostOrderTraverse(T->lchild, visit); // 遍历左子树 PostOrderTraverse(T->rchild, visit); // 遍历右子树 visit(T->data); // 访问结点}}// 采用递归算法void PostOrderTraverse(BiTree T, void (*visit)(TElemType &e)){ if (T) { PostOrderTraverse(T->lchild, visit); // 遍历左子树 PostOrderTraverse(T->rchild, visit); // 遍历右子树 visit(T->data); // 访问结点 }}例:利用先缀表达式建立表达式树•前缀表达式- + a * b – c d / e f-+/a*e fb-c d例 :利用前缀表达式建立表达式树Status CreateExpTree(BiTree &T){scanf(&ch);T = (BiTNode*) malloc(sizeof(BiTNode)) if (!T)exit(OVERFLOW); T->data = ch;if (ch >= ‘a’ && ch <= ‘z’) // 为操作数,建立为叶子结点 T->lchild = T->rchild = NULL; else { // 为运算符 CreateExpTree(T->lchild); // 构造左子树 CreateExpTree(T->rchild); // 构造右子树 }}Status CreateExpTree(BiTree &T){ scanf(&ch); T = (BiTNode*) malloc(sizeof(BiTNode)) if (!T) exit(OVERFLOW); T->data = ch; if (ch >= ‘a’ && ch <= ‘z’) // 为操作数,建立为叶子结点 T->lchild = T->rchild = NULL; else { // 为运算符 CreateExpTree(T->lchild); // 构造左子树 CreateExpTree(T->rchild); // 构造右子树 }}6.3 遍历二叉树和线索二叉树•遍历二叉树的结果是,求得结点的一个线性序列。