来源:互联网 发布: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 ;


原创粉丝点击