树
来源:互联网 发布:excuse me网络用语简写 编辑:程序博客网 时间:2024/04/28 21:23
理论基础
递归不要太想细节, 想大方向比较好 .( 树这多想递归 )
树 一般是有向 ↓ ( 从根到叶子 ) 无序 ←→
树是从家族的族谱来的 , ( 双亲 , 儿子 )
定义 : 根 和 子树 , 子树之间互不相交 . ( 递归定义 ) 树 = ( 根 , 子树 )
基本概念 :
树的结点 : 包含一个数据元素及若干指向其子树的分支。
结点的度 : 结点拥有的子树( 直接 ) 数
叶子 : 度为 0 ( 也叫终端结点 )
分支结点 : 度不为 0 ( 也叫非终端结点 ) ( 根除外 )
树的度 : 树内各个结点的度的最大值
结点的子树的根称为该结点的 孩子 , 该结点成为孩子的 双亲 ,同一个双亲的孩子之间互称 兄弟
结点的祖先 是从根到该结点所经分支上的所有结点 , 以某结点为根的子树中的任一结点都称为该结点的 子孙
结点的层次 : 从根开始定义起 , 根为第一层 , 根的孩子为第二层 , 依此类推
双亲在同一层的结点互为 堂兄弟
树中结点的最大层次称为 树的深度
树中结点的各个子树看成是从左到右有次序的 , 成为有序树 , 否则为无序树 ( 一般都是无序树 )
森林 是 m( m>=0 ) 棵互不相交的书的集合
树的存储方式 :
1. 双亲表示
typedef struct PTNode{
ElemTYpe data ; /* 存储数据 */
int parent ; /* 在数组中的位序*/
}PT ;
整个树的结构是以上结点的集合 :
typedef struct {
PTNode nodes[ MAX_TREE_SIZE ];
int r, n ; /* 根结点所在位置, 结点个数*/
}PTree ; /* 双亲表示法 */
( n 实际为 7 , 图上一点点错误 )
2. 孩子链表
孩子结点结构
typedef struct CTNode {
int child ;
struct CTNode *next ;
} * ChildPtr ;
双亲节点结构
typedef struct CTBox {
ElemType data ;
ChildPtr firstChild ; /* 第一个孩子的指针, 之后顺序向后找到所有的孩子*/
};
整个树的结构是双亲节点的集合
typedef struct {
CTBox nodes[ Max_node_size] ;
int n , r ; /* n结点数和根的位置*/
}CTree ;
( n =7 )
3. 孩子兄弟链表 ( 可以实现树的操作转换为二叉树 )
typedef struct CSNode {
ElemType data ;
struct CSNode *filrstchild, *nextsibling ; /*左孩子, 右兄弟*/
} CSNode, *CSTree ;
森林和二叉树的对应转换关系 : ( 递归 )
设森林 F = ( T1, T2, T...... ) ( 除 T1外, 其余的为最后二叉树的右子树 )
T1 = ( root , t11, t12, ... )其中 root 为 T1 这棵子树的根 , 其余的为子树森林 ( root 为最后二叉树的根, 子树为最后二叉树的左子树 )
二叉树 B = ( LBT, Node(root) , RBT ) ;
二叉树转变为森林 : ( 递归, 上边倒过来了 )
由 Node(root) 对应得到 ROOT( T1) 二叉树的根变成森林第一棵树的根
由 LBT 得到 ( t1, t2, t... ) 二叉树的左子树变成第一棵树的子树森林
由 RBT 得到 ( T2, T3, T...) 二叉树的右子树得到其他子树
树和森林的遍历 :
树的遍历 :
1. 先序遍历 ( 根 , 第一子树 , 其他子树 )
2. 后序遍历 ( 左孩子-》第一棵子树,其他子树, 根 )
3. 按照层次
树的先根遍历对应森林的先序遍历对应二叉树的先序遍历
树的后根遍历对应森林的中序遍历对应二叉树的中序遍历
森林遍历 :
1. 先序遍历 ( 根 , 第一子树森林 , 其他子树 )
2. 中序遍历 ( 左孩子-》第一棵子树森林, 根, 其他子树 )
3. 按照层次
二叉树 : 每个结点至多只有两棵子树 ( 二叉树中不存在度大于2的结点 ) ,二叉树的子树有左右之分 , 其次序不能任意颠倒 . ( 有序树 )
二叉树的 5 种状态 : 空二叉书 , 仅有根结点的二叉树 , 右子树为空的二叉树 , 左,右子树非空的二叉树 , 左子树为空的二叉树
二叉树的性质 :
a. 在二叉树的第i 层上至多有 2 i – 1 个结点 ( i >=1 )
b. 深度为 k 的二叉树至多有 2k - 1 个结点 ( k >= 1)
c. 对任何一棵二叉树 T , 如果其终端结点数为n0 , 度为 2 的结点数为 n2 则 n0 = n2 + 1
满二叉树 : 深度为 k 且有 2k– 1 个结点 ( 除叶子结点 , 全都是度为 2 的结点 ) , 编号的 满二叉树 为完全二叉书 ( 编号顺序 ↓ → )
完全二叉树的特性 :
a. 具有 n 个结点的完全二叉树的深度为 log2n + 1 ( log2n 向下取整 ).
b. 完全二叉树按层次编号 i 层
如果 i =1 则为二叉树的根 , 无双亲 , 否则双亲为 i/2 .
如果 2i > n ( 结点总数 ), 则 结点 i 无左孩子( 结点 i 为叶子结点 ) 否则 左孩子为 2i
如果 2i + 1 > n , 则结点 i 无右孩子 , 否则右孩子为 2i + 1
二叉树的存储结构
1. 顺序存储结构 ( 参照完全二叉树存储 , 参照完全二叉树的编号 )
#define MAX_TREE_SIZE 100
typedef TElemType SqBiTree[ MAX_TREE_SIZE ] ;
SqBiTree bt ;
例如 :
abcde0000fg
如上 : 顺序存储会浪费很多空间 ( 除非是完全二叉树 ) , 所以不推荐使用
2. 链式存储结构 ( 二叉链表 , 三叉链表 )
二叉链表 : lchild - data - rchild ( 结点包含三个域 )
三叉链表 : lchild - data - parent - rchild ( 结点包含四个域 )
链表的头指针指向二叉树的 根 结点
定义 :
typedef struct BiTNode {
TElemType data ; // 例如 char型
struct BiTNode *lchild , *rchild ; //左右孩子指针
} BiTNode , *BiTree ; // 二叉链表
遍历二叉树 : 需要找到一种规律 , 使二叉树上的结点排列在一个线性队列上 , 从而便于遍历
先序遍历 : 根 左 右
中序遍历 : 左 根 右
后序遍历 : 左 右 根
表达式求值中的 : 逆波兰式 ( 先序 , 中序 ,后序 ) 正好对应 .
二叉链表基本操作 :
/* The Basic operate for 二叉树,采用二叉链表存储方式 * Author : Kevin * Date : 2012.03.06 * 树这会用到很多递归 * */#include"stdio.h"#include"stdlib.h"#include"alloc.h"/*树中结点存储结构*/typedef struct BiTNode{chardata;/*存储数据*/struct BiTNode *lchild , *rchild;/*左右孩子*/}BiTNode,*BiTree;/*销毁二叉树*/void DestoryBiTree(BiTree t){if(!t->lchild && !t->rchild){free(t);}else{while(t->lchild){DestoryBiTree(t->lchild);}while(t->rchild){DestoryBiTree(t->lchild);}}}/*清空二叉树*/void ClearBiTree(BiTree t){t->data = '';if(t->lchild){ClearBiTree(t->lchild);}if(t->rchild){ClearBiTree(t->rchild);}}/*判空二叉树*/int BiTreeEmpty(BiTree t){if(t == NULL){return 1;}elsereturn 0;}/*求二叉树深度*/int BiTreeDepth(BiTree t){int suml = 0;/*左子树深度*/ int sumr = 0;/*右子树深度*/if(suml >= sumr){return suml + 1;}elsereturn sumr + 1;}/*返回二叉树的根*/char Root(BiTree t){return t->data ;}/*插入结点,p为双亲结点指针,lr为左右孩子标记,c为值*/void InsertChild(BiTree t, BiTree p, int lr, char c){if(t == p){if(lr ==0){p->lchild = (BiTree)malloc(sizeof(BiTNode));p->lchild->lchild = NULL;p->lchild->rchild = NULL;p->lchild->data = c;}else{p->rchild = (BiTree)malloc(sizeof(BiTNode));p->rchild->rchild = NULL;p->rchild->rchild = NULL;p->rchild->data = c;}}else{if(t->lchild)InsertChild(t->lchild, p, lr, c);if(t->rchild)InsertChild(t->rchild, p, lr, c);}}/*删除结点,p为双亲结点,lr为左右孩子标记*/void DeleteChild(BiTree t, BiTree p, int lr){if(t == p){if(lr ==0){free(p->lchild);}else{free(p->rchild);}}else{if(t->lchild)DeleteChild(t->lchild, p, lr);if(t->rchild)DeleteChild(t->rchild, p, lr); }}
遍历操作 :
/* 6.1 遍历二叉树 , 先序 , 中序 , 后序 ( 递归实现 )** Author : Kevin** Date : 2011/11/30 */#include"stdio.h"#include"alloc.h"#include"stdlib.h"#define ELEM int#define Status inttypedef struct MyTree{ ELEM myNode ; /*节点定义*/ struct myTree *leftChild ; /*左子树*/ struct myTree *rightChild ; /*右子树*/}MyTree , *MyLTree ;/*按照定义递归构造二叉数*/void CreateTree( MyLTree mt , int level ){ int du ; int i ; int j ; int leftOrRight ; MyLTree p = NULL ; MyLTree q = NULL ; q = mt ; if( !mt->leftChild || !mt->rightChild ){ mt->leftChild = NULL ; mt->rightChild = NULL ; } printf("&& Please input this %d nodes du ;\n", level); scanf("%d",&du) ; if(du > 2 || du < 0){ printf("Error occur , du is error !\n"); return 0 ; } else if( du == 1){ printf("Please goon input the left or right , 1 left , 2 right !\n"); scanf("%d",&leftOrRight); if(leftOrRight == 1){ for(j=0; j<du; j++){ p = (MyLTree)malloc(sizeof(MyTree)); p->leftChild = NULL ; p->rightChild = NULL ; printf("please input %d left child value \n",level ); scanf("%d",&p->myNode); q->leftChild = p ; CreateTree(p,p->myNode); } } else{ for(j=0; j<du; j++){ p = (MyLTree)malloc(sizeof(MyTree)); p->leftChild = NULL ; p->rightChild = NULL ; printf("please input %d right child value \n",level); scanf("%d",&p->myNode); q->rightChild = p ; CreateTree(p,p->myNode); } } } else if ( du == 2){ for(j=0; j<du; j++){ if( j==0 ){ p = (MyLTree)malloc(sizeof(MyTree)); p->leftChild = NULL ; p->rightChild = NULL ; printf("please input the %d left child value \n",level); scanf("%d",&p->myNode); q->leftChild = p ; CreateTree(p,p->myNode); }else{ p = (MyLTree)malloc(sizeof(MyTree)); p->leftChild = NULL ; p->rightChild = NULL ; printf("please input the %d right child value \n",level); scanf("%d",&p->myNode); q->rightChild = p ; CreateTree(p,p->myNode); } } } else{ printf("this is the leaf node\n "); } }/*先序*/void preOrderTree(MyLTree mt){ printf(" %d\n ",mt->myNode); if(mt->leftChild){ preOrderTree(mt->leftChild) ; } if(mt->rightChild){ preOrderTree(mt->rightChild); }}/*中序*/void inOrderTree(MyLTree mt){ if(mt->leftChild){ inOrderTree(mt->leftChild); } printf(" %d\n ",mt->myNode); if(mt->rightChild){ inOrderTree(mt->rightChild); }}/*后序*/void posOrderTree(MyLTree mt){ if(mt->leftChild){ posOrderTree(mt->leftChild); } if(mt->rightChild){ posOrderTree(mt->rightChild); } printf(" %d\n ",mt->myNode); }int main(){ MyLTree pm ; int t = 1 ; pm = (MyLTree)malloc(sizeof(MyTree)); pm->leftChild = NULL ; pm->rightChild = NULL ; printf("*************Please input the root node value : ****************\n"); scanf("%d",&pm->myNode); CreateTree( pm , t ) ; printf("\n"); /*先序遍历*/ printf("The pre as below :\n"); preOrderTree( pm ); printf("\n"); printf("The in as below :\n"); /*中顺遍历*/ inOrderTree( pm ); printf("\n"); printf("The pos as below :\n"); /*后顺遍历*/ posOrderTree( pm ); printf("\n"); printf("Please input any key to continue (^_^)\n "); getchar() ; getchar() ; }
按照定义创建二叉树
Status CreateBiTree(BiTree &T){scanf(&ch);if(ch == '') T = NULL;else{if(!(T=(BiTNode *)malloc(sizeof(BiTNode))))exit(OVERFLOW);T->data = ch;CreateBiTree(T->lchild);CreateBiTree(T->rchild);}}
输入 ABC___D_E__ ( 下划线代表空格 )
利用二叉树存储表达式 ( 表达市求值 )
一般情况 : 左子树( 存储第一操作数 ) , 右子树 ( 存储第二操作数 ) , 根 ( 存储运算符 ) . 如果第一操作数或者第二操作数还是个表达式 , 则递归之 .
例如 : ( a+ b ) *c - d / e , 按照先缀表示法为 : - * + a b c / d e , 那么如果根据先缀生成一棵二叉树 ( 按照先序遍历 ) , 由于此操作树比为满二叉树, 另外形式为 根是运算符,两个儿子分别是第一操作数和第二操作树, 同样后序也可以生成一个二叉树, 只不过需要后序生成 .操作数必为一个叶子结点 . 运算符的左右子树都不空,
前缀和后缀表达式, 都可以建树 .
原表达式建树 : 原表达式 -> 后缀 -> 树 ( 一起形成 )
建立两个栈(分别存运算符和操作数), 当形成一个小的子树时, 保存子树的根指针,依此类推.
线索二叉树
遍历二叉树的结果是求得结点的一个线性序列, 指向该线性序列的"前驱"和"后继"的指针, 称为"线索".
作用 : 当以二叉树作为存储结构时, 只能找到结点的左右孩子, 不能找到它在线性序列中的前驱和后继, 所以就形成了线索二叉树.
线索二叉树存储结构 :
左子树为空, 加前驱. 右子树为空, 加后继.( 这里要注意, 线索二叉树的前驱与后继是要根据遍历的顺序决定的 )
typedef enum PointerTag{Link, Thread } PointerTag ; // link ==0 指针 , Trhread == 1 线索
typedef struct BiThrNode{
ElemType data ;
struct BiThrNode *lchild , *rcihild ; // 左右孩子
PointerTag LTag, RTag ; //左右孩子标记, 指针还是线索
} BiThrNode, *BiThrTree ;
赫夫曼树 ( 最优二叉树 )
构造赫夫曼树 : 每次拿最小的两个权值的节点, 组成一棵子树 ( 子树权值等于这两棵子树的和 ) , 删除集合中的这两棵子树, 并同时把这个树加入到集合中, 重复以上的步骤。
赫夫曼编码 : 将以下编码的字符存储在二叉树的叶子结点上, 并且权值为编码字符出现的次数.
前缀编码 : 任一字符的编码都不是另一个字符的编码的前缀.
存储结构 :
typedef strcut{
unsigned int weight ; //权值
unsigned int parment , lchild , rchild ;
} HTNode , * HuffmanCode ;