树和二叉树

简介: 树和二叉树

一、树

1.概述

树(Tree)是n(n≥0)个结点的有限集合,当n=0时,为空树;n>0时,为非空树。任意一棵非空树,满足:

1)有且仅有一个称之为根的结点;

2)除根结点以外的其余结点可分为m(m>0)个互不相交的有限集T1, T2, …, Tm, 其中每一个集合本身又是一棵树,并且称为根的子树(SubTree)。

20210425162110917.png

2.树的相关术语


● 节点——节点包含数据元素及若干指向子树的分支信息。

● 节点的度——节点拥有的子树个数。

● 树的度——树中节点的最大度数。

● 终端节点——度为0的节点,又称为叶子

● 分支节点——度大于0的节点。除了叶子都是分支节点。

● 内部节点——除了树根和叶子都是内部节点。

● 节点的层次——从根到该节点的层数(根节点为第1层)。

● 树的深度(或高度)——指所有节点中最大的层数。

● 路径——树中两个节点之间所经过的节点序列。

● 路径长度——两节点之间路径上经过的边数。

● 双亲、孩子——节点的子树的根称为该节点的孩子,反之,该节点为其孩子的双亲。

● 兄弟——双亲相同的节点互称兄弟。

● 堂兄弟——双亲是兄弟的节点互称堂兄弟。

● 祖先——从该节点到树根经过的所有节点称为该节点的祖先。

● 子孙——节点的子树中的所有节点都称为该节点的子孙。

● 森林——由m(m≥0)棵不相交的树组成的集合。


3.树的存储结构

顺序存储

分为双亲表示法、孩子表示法和双亲孩子表示法。


20210425162734978.png

3种表示法的优缺点如下。

双亲表示法只记录了每个节点的双亲,无法直接得到该节点的孩子;孩子表示法可以得到该节点的孩子,但是无法直接得到该节点的双亲,而且由于不知道每个节点到底有多少个孩子,因此只能按照树的度(树中节点的最大度)分配孩子空间,这样做可能会浪费很多空间。双亲孩子表示法是在孩子表示法的基础上,增加了一个双亲域,可以快速得到节点的双亲和孩子,其缺点和孩子表示法一样,可能浪费很多空间。


链式存储

(1)孩子链表表示法

类似于邻接表,表头包含数据元素并指向第一个孩子指针,将所有孩子放入一个单链表中。在表头中,data存储数据元素,first为指向第1个孩子的指针。单链表中的节点记录该节点的下标和下一个节点的地址。仍以图6-10为例,其孩子链表表示法如图6-12所示。


20210425162836561.png


(2)孩子兄弟表示法

节点除了存储数据元素之外,还有两个指针域lchild和rchild,被称为二叉链表。lchild存储第一个孩子地址,rchild存储右兄弟地址。其节点的数据结构如图6-13所示。


2021042516295962.png

20210425163032365.png


4.树、森林与二叉树的转换

(1).树和二叉树的转换

秘诀:长子当作左孩子,兄弟关系向右

2021042516333890.png

还原时逆操作即可.

(2).森林和二叉树的转换

可以把森林中的每棵树的树根看作兄弟关系,因此3棵树的树根B、C和D是兄弟,兄弟关系在右斜线上,其他的转换和树转二叉树一样,长子当作左孩子,兄弟关系向右斜。或者把森林中的每一棵树转换成二叉树,然后把每棵树的根节点连接在右斜线上即可

20210425163453972.png


以上大部分内容来自陈小玉老师的《趣学数据结构》

二、二叉树

1.概述


二叉树(binary tree)是n(n≥0)个节点构成的集合,

它或为空树(n=0),或满足以下两个条件:

1)有且仅有一个称为根的节点;

2)除根节点以外,其余节点分为两个互不相交的子集T1和T2,分别称为T的左子树和右子树,且T1和T2本身都是二叉树。


二叉树是一种特殊的树,它最多有两个子树,分别为左子树和右子树,二者是有序的,不可以互换。也就是说,二叉树中不存在度大于2的节点。


两种特殊的二叉树:

●满二叉树:一棵深度为k且有2k-1个节点的二叉树。满二叉树每一层都“充满”了节点,达到最大节点数,如图6-24所示。

●完全二叉树:除了最后一层外,每一层都是满的(达到最大节点数),最后一层节点是从左向右出现的


2.性质


性质1:在二叉树的第i层上至多有 2(i-1) 个节点。

性质2:深度为k的二叉树至多有 2^k-1个节点。

性质3:对于任何一棵二叉树,若叶子数为n0,度为2的节点数为n2,则n0=n2+1。

性质4:具有n个节点的完全二叉树的深度必为 log2n+1。

性质5:对于完全二叉树,若从上至下、从左至右编号,则编号为i的节点,其左孩子编号必为2i,其右孩子编号必为2i +1,其双亲的编号必为i/2。


3. 二叉树的存储结构

1.顺序存储(可以使用树存储的三种方法,也可使用两个数组,一个记录左孩子,一个记录右孩子,节点和索引相对应)

2. 链式存储:二叉链表(最常用的那个,一个存放数据,还有两个指针),三叉链表

4.二叉树的创建

1.询问法
void createtree(Btree &T){
  char check;
  T = new Bnode;
  cout<<"请输入节点信息:"<<endl;
  cin>>T->data;
  cout<<"是否添加"<<T->data<<"的左孩子? (Y/N)"<<endl;//询问是否创建T的左子树
  cin>>check;
  if(check=='Y'){
    createtree(T->lchild); 
  } else{
    T->lchild = NULL;
  } 
  cout<<"是否添加"<<T->data<<"的右孩子? (Y/N)"<<endl;//询问是否创建T的左子树
  cin>>check;
  if(check=='Y'){
    createtree(T->rchild); 
  } else{
    T->rchild = NULL;
  } 
} 


2.补空法(更常用)

void Createtree(Btree &T) { /*创建二叉树函数*/
  char ch;
  cin>>ch;
  if(ch=='#'){
    T = NULL;
  } else{
    T = new Bnode;
    T->data = ch;
    Createtree(T->lchild);
    Createtree(T->rchild);
  }
}

三、二叉树的遍历

直接上代码

#include<iostream>
#include<queue>
using namespace std;
typedef struct Bnode {
  char data;
  Bnode *lchild,*rchild; 
} Bnode,*Btree;//Btree指代二叉树 
void Createtree(Btree &T) { /*创建二叉树函数*/
  char ch;
  cin>>ch;
  if(ch=='#'){
    T = NULL;
  } else{
    T = new Bnode;
    T->data = ch;
    Createtree(T->lchild);
    Createtree(T->rchild);
  }
}
void preorder(Btree T){//先序遍历 
  if(T){
    cout<<T->data<<" ";
    preorder(T->lchild);
    preorder(T->rchild);
  }
}
void inorder(Btree T){//中序遍历 
  if(T){
    preorder(T->lchild);
    cout<<T->data<<" ";
    preorder(T->rchild);
  }
} 
void posorder(Btree T){//后序遍历 
  if(T){
    posorder(T->lchild);
    preorder(T->rchild);
    cout<<T->data<<" ";
  } 
} 
bool Leveltraverse(Btree T){
  Btree p;
  if(!T){//如果是空树则结束 
    return false;
  }
  queue<Btree> Q;
  Q.push(T);
  while(!Q.empty()){
    p=Q.front();
    Q.pop();
    cout<<p->data<<" ";
    if(p->lchild){
      Q.push(p->lchild);
    }
    if(p->rchild){
      Q.push(p->rchild);
    }
  }
  return true;
}
int main() {
  Btree mytree;
  cout<<"按先序次序输入二叉树中结点的值(孩子为空时输入#),创建一棵二叉树"<<endl;
  Createtree(mytree);//创建二叉树
  cout<<endl;
  cout<<"二叉树的先序遍历结果:"<<endl;
  preorder(mytree);//先序遍历二叉树
  cout<<endl;
  cout<<"二叉树的中序遍历结果:"<<endl;
  inorder(mytree);//中序遍历二叉树
  cout<<endl;
  cout<<"二叉树的后序遍历结果:"<<endl;
  posorder(mytree);//后序遍历二叉树
  cout<<endl;
  cout<<"二叉树的层次遍历结果:"<<endl;
  Leveltraverse(mytree);//层次遍历二叉树
  return 0;
}


运行结果:

202104251737245.png

2021042517374621.png

四、树的应用

1.前序/后序和中序还原建立二叉树.

#include <iostream>
using namespace std;
typedef struct node
{
    char data;
    struct node *lchild,*rchild;
}BiTNode,*BiTree;
BiTree pre_mid_createBiTree(char *pre,char *mid,int len) //前序中序还原建立二叉树
{
    if(len==0)
        return NULL;
    char ch=pre[0];  //找到先序中的第一个结点
    int index=0;
    while(mid[index]!=ch)//在中序中找到的根结点的左边为该结点的左子树,右边为右子树
    {
        index++;
    }
    BiTree T=new BiTNode;//创建根结点
    T->data=ch;
    T->lchild=pre_mid_createBiTree(pre+1,mid,index);//建立左子树
    T->rchild=pre_mid_createBiTree(pre+index+1,mid+index+1,len-index-1);//建立右子树
    return T;
}
BiTree pro_mid_createBiTree(char *last,char *mid,int len)//后序中序还原建立二叉树
{
    if(len==0)
       return NULL;
    char ch=last[len-1]; //取得后序遍历顺序中最后一个结点
    int index=0;//在中序序列中找根结点,并用index记录长度
    while(mid[index]!=ch)//在中序中找到根结点,左边为该结点的左子树,右边为右子树
       index++;
    BiTree T=new BiTNode;//创建根结点
    T->data=ch;
    T->lchild=pro_mid_createBiTree(last,mid,index);//建立左子树
    T->rchild=pro_mid_createBiTree(last+index,mid+index+1,len-index-1);//建立右子树
    return T;
}
void pre_order(BiTree T)//前序递归遍历二叉树
{
    if(T)
    {
        cout<<T->data;
        pre_order(T->lchild);
        pre_order(T->rchild);
    }
}
void pro_order(BiTree T)//后序递归遍历二叉树
{
    if(T)
    {
        pro_order(T->lchild);
        pro_order(T->rchild);
        cout<<T->data;
    }
}
int main()
{
    BiTree T;
    int n;
    char pre[100],mid[100],last[100];
    cout<<"1. 前序中序还原二叉树\n";
  cout<<"2. 后序中序还原二叉树\n";
  cout<<"0. 退出\n";
  int choose=-1;
  while(choose!=0)
  {
      cout<<"请选择:";
    cin>>choose;
    switch (choose)
    {
        case 1://前序中序还原二叉树
            cout<<"请输入结点的个数:"<<endl;
            cin>>n;
            cout<<"请输入前序序列:"<<endl;
            for(int i=0;i<n;i++)
                    cin>>pre[i];
                cout<<"请输入中序序列:"<<endl;
                for(int i=0;i<n;i++)
                    cin>>mid[i];
                T=pre_mid_createBiTree(pre,mid,n);
                cout<<endl;
                cout<<"二叉树还原成功,输出其后序序列:"<<endl;
                pro_order(T);
                cout<<endl<<endl;
                break;
            case 2://后序中序还原二叉树
                cout<<"请输入结点的个数:"<<endl;
            cin>>n;
                cout<<"请输入后序序列:"<<endl;
                for(int i=0 ;i<n;i++)
                    cin>>last[i];
                cout<<"请输入中序序列:"<<endl;
                for(int i=0 ;i<n;i++)
                    cin>>mid[i];
                T=pro_mid_createBiTree(last,mid,n);
                cout<<endl;
                cout<<"二叉树还原成功,输出其先序序列:"<<endl;
                pre_order(T);
                cout<<endl<<endl;
                break;
    }
  }
    return 0;
}


运行结果

20210425182113413.png

20210425182427709.png

2.求叶子数和节点数

#include <iostream>
using namespace std;
typedef struct Bnode  /*定义二叉树存储结构*/
{ char data;
  struct Bnode *lchild,*rchild;
}Bnode,*Btree;
void Createtree(Btree &T) /*创建二叉树函数*/
{
    //按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T
  char ch;
  cin >> ch;
  if(ch=='#')
        T=NULL;     //递归结束,建空树
  else{
    T=new Bnode;
    T->data=ch;         //生成根结点
    Createtree(T->lchild);  //递归创建左子树
    Createtree(T->rchild);  //递归创建右子树
  }
}
int LeafCount(Btree T)//求二叉树的叶子数
{
    if(T==NULL)//如果为空树,深度为0
        return 0;
    else
        if(T->lchild==NULL&&T->rchild==NULL)//左右子树均为空,则叶子数为1
           return 1;
        else
           return LeafCount(T->lchild)+LeafCount(T->rchild);//递归计算左子树和右子树的叶子数之和
}
int NodeCount(Btree T)//求二叉树的结点数
{
    if(T==NULL)//如果为空树,深度为0
        return 0;
    else
        return NodeCount(T->lchild)+NodeCount(T->rchild)+1;//递归计算左子树和右子树的结点数之和加1
}
int main()
{
    Btree mytree;
    cout<<"按先序次序输入二叉树中结点的值(孩子为空时输入#),创建一棵二叉树"<<endl;
    //ABD##E##CF#G###
    Createtree(mytree);//创建二叉树
    cout<<endl;
    cout<<"二叉树的结点数为:"<<NodeCount(mytree)<<endl;
    cout<<"二叉树的叶子数为:"<<LeafCount(mytree)<<endl;
    return 0;
}

3.求二叉树的深度

#include <iostream>
using namespace std;
typedef struct Bnode  /*定义二叉树存储结构*/
{ char data;
  struct Bnode *lchild,*rchild;
}Bnode,*Btree;
void Createtree(Btree &T) /*创建二叉树函数*/
{
    //按先序次序输入二叉树中结点的值(一个字符),创建二叉链表表示的二叉树T
  char ch;
  cin >> ch;
  if(ch=='#')
        T=NULL;     //递归结束,建空树
  else{
    T=new Bnode;
    T->data=ch;         //生成根结点
    Createtree(T->lchild);  //递归创建左子树
    Createtree(T->rchild);  //递归创建右子树
  }
}
int Depth(Btree T)//求二叉树的深度
{
    int m,n;
    if(T==NULL)//如果为空树,深度为0
        return 0;
    else
    {
       m=Depth(T->lchild);//递归计算左子树深度
       n=Depth(T->rchild);//递归计算左子树深度
       if(m>n)
          return m+1;//返回左右子树最大值加1
       else
          return n+1;
    }
}
int main()
{
    Btree mytree;
    cout<<"按先序次序输入二叉树中结点的值(孩子为空时输入#),创建一棵二叉树"<<endl;
    //ABD##E##CF#G###
    Createtree(mytree);//创建二叉树
    cout<<endl;
    cout<<"二叉树的深度为:"<<Depth(mytree)<<endl;
    return 0;
}
相关文章
树和二叉树(三)
树和二叉树(三)
|
7月前
|
存储
树与二叉树
树与二叉树
|
存储 算法 数据库管理
树和二叉树(二)
树和二叉树(二)
|
存储
树和二叉树
树和二叉树
69 0
|
7月前
|
机器学习/深度学习 存储 算法
树【二叉树,红黑树,B树,B+树】
树【二叉树,红黑树,B树,B+树】
68 0
|
存储 人工智能 算法
树结构的讲解与二叉树的基本运用
树结构的讲解与二叉树的基本运用
|
存储 人工智能 BI
树和二叉树(一)
树和二叉树(一)
|
存储 机器学习/深度学习 算法
九、树和二叉树
九、树和二叉树
九、树和二叉树
|
存储 算法
九、树和二叉树2
九、树和二叉树
九、树和二叉树2