二叉树

  二叉树的定义:是n个结点的有限集合;(n>=0)特别之处在于度不能超过2;

特殊二叉树

  满二叉树:在一棵二叉树中,所有分支结点都存在左子树和右子树,并且所有的叶子结点都在同一层上

  满二叉树特点:

  • 叶子只出现在最后一层
  • 非叶子结点的度一定是2
  • 在同样深度的二叉树满,满二叉树的结点个数一定最多,叶子结点也是最多的

  完全二叉树:对一颗具有n个结点的二叉树按层序编号,如果编号为 i(1<=i<=n) 的结点与同样深度的满二叉树中编号为 i 的结点位置完全相同,则称为完美二叉树(其实就是满二叉树在最后一层叶子结点上从右到左去掉若干个结点

  完全二叉树特点:

  • 叶子结点只能出现在最下两层
  • 最下层的叶子结点一定在左部连续位置;倒数第二层,若有叶子结点,一定在右部连续位置
  • 如果结点度为1,则该结点只有左孩子
  • 同样结点的二叉树,完全二叉树的深度最小

二叉树的性质

  • 第 i 层上最多有 2^(i-1) 个结点
  • 深度为 k 的二叉树至多有 2^k-1 个结点( 2^0+2^1+...+2^(k-1)= 2^k-1 ——等比公式)
  • 叶子节点数为n0,度为2的结点数为n2,则 n0=n2+1;(叶子结点数=度为2的结点数+1)
  1. 证明:结点总数为 n ,度为 0 的叶子结点数为 n0,度为 1 的结点数为 n1,度为 2 的结点数为 n2,树中连接线为 n-1;
  2. n-1=n1+2*n2  =>  n-1=n0+n1+n2-1=n1+n2+n2  =>  n0=n2+1
  • 具有n个结点的完全二叉树的深度为 【└ Log┘+1:Log₂n 取下限加1(取下限就是去除小数部分)】相应由性质2可推导出满二叉树的深度为 Log(n+1)
  1. 证明:完全二叉树的结点数的范围在比它少一层的满二叉树和跟它同一层的满二叉树之间,所以Log₂n 取下限加1
  • 完全二叉树,给任意一结点 i 有:
  1. 如果 i=1,为根节点;如果 i>1,则其双亲序号是结点└ i/2
  2. 如果2*i>n,则结点 i 无左子结点,(连左子结点都没有就说明是叶子结点);如果2*i<=n,则结点 i 的左子结点是 2i
  3. 如果2*i+1>n,则结点 i 无右子结点,(没有左子结点不能说明是叶子结点,应为可能有左子结点);如果2*i+1<=n,则结点 i 的右子结点为 2i+1
  4. 左子树序数都为偶数,右子树序数都为奇数

二叉树的存储结构

  顺序存储结构:在数组中按结点编号把元素存储到相应下标中,若某下标无对应的结点序数,用空表示(遇到斜二叉树时比较消耗空间)

  二叉链表:包括一个数据域和两个指针域

1 typedef int ElemType;
2 
3 typedef struct BirTNode{
4     ElemType data;
5     struct BiTNode *lchild;//左孩子
6     struct BiTNode *rchild;//右孩子
7 }BirTNode,* BirTree;

二叉树的遍历

  前序:若二叉树为空,则操作返回,否则先访问根节点,再前序左子树,然后前序右子树;

  中序:先中序左子树,再访问根节点,然后访问右子树;

  后序:先后序左子树,再后序右子树,然后访问根节点;

  层序:从树的第一层,从上到下逐层遍历,同层从左到右逐个访问(适用于顺序存储的访问)

 1 #include <stdio.h>
 2 #include <malloc.h>
 3 typedef char ElemType;
 4 
 5 typedef struct BirTNode{
 6     ElemType data;
 7     struct BirTNode *lchild;//左孩子
 8     struct BirTNode *rchild;//右孩子
 9 }BirTNode,* BirTree;
10 
11 void proprintf(BirTree tree){
12     if(tree){
13         printf("%c  ",tree->data);
14         proprintf(tree->lchild);
15         proprintf(tree->rchild);
16     }
17 }
18 
19 void cenprintf(BirTree tree){
20     if(tree){
21         cenprintf(tree->lchild);
22         printf("%c  ",tree->data);
23         cenprintf(tree->rchild);
24     }
25 }
26 
27 void afterprintf(BirTree tree){
28     if(tree){
29         afterprintf(tree->lchild);
30         afterprintf(tree->rchild);
31         printf("%c  ",tree->data);
32     }
33 }
34 
35 void creat(BirTree tree,ElemType val){
36     ElemType v;
37     BirTree l,r;
38     tree->data=val;
39     printf("输入%c左子树的根值(以空格结束):\n",tree->data);
40     v=getch();
41     if(' '!=v){
42         l=(BirTree)malloc(sizeof(BirTNode));
43         tree->lchild=l;
44         creat(l,v);
45     }else{
46         tree->lchild=NULL;
47     }
48     printf("输入%c右子树的根值(以空格结束):\n",tree->data);
49     v=getch();
50     if(' '!=v){
51         r=(BirTree)malloc(sizeof(BirTNode));
52         tree->rchild=r;
53         creat(r,v);
54     }else{
55         tree->rchild=NULL;
56     }
57 }
58 
59 void main(){
60     ElemType val;
61     BirTree tree=(BirTree)malloc(sizeof(BirTNode));
62     printf("请输入根节点的值(空格结束):\n");
63     val=getch();
64     creat(tree,val);
65 
66     printf("前序遍历树:");
67     proprintf(tree);
68     printf("\n");
69 
70     printf("中序遍历树:");
71     cenprintf(tree);
72     printf("\n");
73 
74     printf("后序遍历树:");
75     afterprintf(tree);
76     printf("\n");
77 }

解树的两种情况
  已知前序和中序

  • 解树依据:前序先出现的为根节点;

  过程:1、先确定根节点;
    2、在中序中找到这结点,左边与已确定结点之间的结点为左子树;右边与已确定结点之间的结点为右子树;
    2、重复以上两步,不断确定跟结点,左右树,直到解出整棵树;
  已知中序和后序

  • 解树依据:后序后出现的为根节点;

 

  过程:1、先确定根节点;
    2、在中序中找到这结点,左边与已确定结点之间的结点为左子树;右边与已确定结点之间的结点为右子树;
    3、重复以上两步,不断确定跟结点,左右树,直到解出整棵树;