理论基础

  递归不要太想细节, 想大方向比较好 .( 树这多想递归 )

  树 一般是有向 ↓ ( 从根到叶子 ) 无序 ←→

 

线形结构树形结构
第一个元素无前驱根无前驱
最后一个元素无后继多个叶子结点无后继
其他元素一个前驱一个后继其他结点一个前驱多个后继

  树是从家族的族谱来的 , ( 双亲 , 儿子 )

  定义 :  根 和 子树 , 子树之间互不相交 . ( 递归定义 ) 树 = ( 根 , 子树 )

  基本概念 :

  树的结点 : 包含一个数据元素及若干指向其子树的分支。

  结点的度 : 结点拥有的子树( 直接 ) 数

  叶子 : 度为 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{
	char	data;				/*存储数据*/
	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;
	}	
	else
		return 0;
}


/*求二叉树深度*/
int BiTreeDepth(BiTree t){

	int suml = 0;	/*左子树深度*/
        int sumr = 0;	/*右子树深度*/
	
	if(suml >= sumr){
		return suml + 1;
	}else
		return 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 int


typedef 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 ;


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值