数据结构之树(二叉树)的探索

Posted 码农迪迪

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之树(二叉树)的探索相关的知识,希望对你有一定的参考价值。


点击蓝字

默默关注


树的定义:

树(Tree)是n(n≥0)个结点的有限集,它或为空树(n = 0);或为非空树,对于非空树T:

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

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

图示:

数据结构之树(二叉树)的探索


基本术语表


即根结点(没有前驱)

叶子

即终端结点(没有后继)

森林

m颗不相交的树集合

有序树

结点各子树从左至右有序,不能互换

无序树

结点各子树可互换位置


双亲

即上层的那个结点(直接前驱)

孩子

即下层结点的子树的根(直接后继)

兄弟

同一双亲下的同层结点(孩子之间互称兄弟)

堂兄弟

即双亲位于同一层的结点(但并非同一双亲)

祖先

即从根到该结点所经分支的所有结点

子孙

即该结点下层子树中的任一结点


结点

即树的数据元素

结点的度

结点挂接的子树数

结点的层次

从根到该结点的层数(根结点算第一层)

终端结点

即度为0的结点,即叶子

分支节点

即度不为0的结点(也称为内部结点)

树的度

所有结点度中的最大值

树的深度(高度)

指所有结点中最大的层数


1
二叉树的定义

二叉树(Binary Tree)是n(n≥0)个结点所构成的集合,它或为空树(n = 0);或为非空树,对于非空树T:

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

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


二叉树基本特点

•结点的度小于等于2

•有序树(子树有序,不能颠倒)


二叉树的性质

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

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

性质3: 对于任何一棵二叉树,若2度的结点数有n2个,则叶子数n0必定为n2+1(即n0=n2+1)

证:边的个数B等于结点数n-1(树的性质),也会等于度为2的点n2乘以2加上度为一的点n1乘以1,即B=n-1且 B=n2*2+n1*1 且 n=n2+n1+n0即可证。


满二叉树:

一棵深度为k 且有2k -1个结点的二叉树。(特点每层都“充满”了结点)

数据结构之树(二叉树)的探索

完全二叉树:

数据结构之树(二叉树)的探索


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

证:2^(k−1)−1<n≤2^k−1或2^(k−1)≤n<2^k,推出k−1≤log2 n<k,因为k是整数,所以k=[log2 n]+1


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


例题:

设一棵完全二叉树具有1000个结点,则它有多少个叶子结点

法1:先求全部叶子数。n0=489(末层)+11(k-1层)=500个;

法2:先求2度结点数。n2=255(k-2层)+244(k-1层)=499个;

这两种方法的缺点:都要先计算树的深度 k=[log2 n]+1 =10;

法3:无需求树深k,便可快捷求出完全二叉树的叶子数:n0= n/2 // 取大于n/2的最小整数值 可由二叉树性质5轻松证明!(编号为i的结点,其孩子编号必为2i和2i+1)证:已知最后一个结点编号为n,则其双亲(n/2或(n-1)/2)肯定是最后一个非叶子结点。其编号之后的全部结点都是叶子了!故,n0=n-n/2或n-(n-1)/2=n/2


二叉树的链表储存表示

typedef struct BiTNode{int data;//左右子树struct BiTNode *lchild,*rchild;}BiTNode,*BiTree;



2
树的遍历:

树的遍历分为三种:前序遍历,中序遍历,后序遍历。


以前序遍历为例

(1)先遍历树根

(2)然后前序遍历左子树

(3)最后前序遍历右子树


数据结构之树(二叉树)的探索


遍历流程:

【1】首先遍历树根,输出A

【2】对A的左子树进行前序遍历,怎么前序遍历?对于B这个左子树而言,首先遍历根节点,输出B

【3】然后遍历子树B的左子树,得到D这个子树,对D进行前序遍历,首先遍历树根节点,输出D

【4】然后遍历D的左子树,不存在。那就遍历D的右子树,不存在。此时B的左子树D遍历完成

【5】遍历B的右子树E,则前序遍历E,首先遍历树根结点,输出E

【6】遍历E的左子树,得到子树G,对于子树G,前序遍历G,得到树根节点G,输出G,此时G遍历完成

【7】此时A的左子树遍历完成,现在开始遍历A的右子树C,前序遍历C,得到树根结点,输出C

【8】遍历C的左子树,不存在,则遍历其右子树,得到子树F,前序遍历F,得到树根结点F,输出F

于是结果为 :ABDEGCF

前序遍历

int PreOrderTraverse(BiTree T){  if(T==NULL) return OK; //空二叉树  else{         cout<<T->data; //访问根结点     PreOrderTraverse(T->lchild); //递归遍历左子树     PreOrderTraverse(T->rchild); //递归遍历右子树    }}

同理可得中序遍历

int InOrderTraverse(BiTree T){  if(T==NULL) return OK; //空二叉树  else{         InOrderTraverse(T->lchild); //递归遍历左子树  cout<<T->data; //访问根结点     InOrderTraverse(T->rchild); //递归遍历右子树    }}

后序遍历

int PostOrderTraverse(BiTree T){  if(T==NULL) return OK; //空二叉树  else{         PostOrderTraverse(T->lchild); //递归遍历左子树     PostOrderTraverse(T->rchild); //递归遍历右子树     cout<<T->data; //访问根结点    }}


二叉树的建立(由先序遍历结果建立)

A B C ##D E#G##F###

void CreateBiTree(BiTree &T){cin>>ch;//递归结束,建空树if (ch=='#')   T=NULL;    else{     //生成根结点    T=new BiTNode;    T->data=ch;           //递归创建左子树    CreateBiTree(T->lchild);    //递归创建右子树    CreateBiTree(T->rchild);  }                        }    

数据结构之树(二叉树)的探索


计算二叉树结点总数

思路:如果是空树,则结点个数为0;

         否则,结点个数为左子树的结点个数+右子树的结点个数再+1。


int NodeCount(BiTree T){  if(T == NULL ) return 0;              else return NodeCount(T->lchild)+NodeCount(T->rchild)+1;}


计算叶子结点个数

思路:如果是空树,则叶子结点个数为0;

        否则,为左子树的叶子结点个数+右子树的叶子结点个数。

int LeadCount(BiTree T){//如果是空树返回0   if(T==NULL)   return 0;   //如果是叶子结点返回1  if (T->lchild == NULL && T->rchild == NULL)    return 1; //如果是叶子结点返回1  else return LeafCount(T->lchild) + LeafCount(T->rchild);}


计算二叉树的深度

思路:如果是空树,则深度为0;

        否则,递归计算左子树的深度记为m,递归计算右子树的深度记为n,二叉树的深度则为m与n的较大者加1。

int depthCount(BiTree *T){  if (T==NULL) return 0;  //返回较大者加1  else return max(depthCount(T->lchild),depthCount(T->rchild))+1;}


由中序遍历与后序遍历确定前序遍历

#include<cstdio>#include<iostream>#include<cstring>using namespace std;//in为中序排列 after为后序排列 void beford(string in,string after){    if (in.size()>0){      //后序排列的第一个为根        char ch=after[after.size()-1];        //找根输出        cout<<ch;        //在中序排列中找到位置 递归调用左子树与右子树        int k=in.find(ch);        //substr第一个参数为截取的起始位,第二个参数为长度        beford(in.substr(0,k),after.substr(0,k));        beford(in.substr(k+1),after.substr(k,in.size()-k-1));    }}int main(){    string inord,aftord;    cin>>inord;    cin>>aftord;//读入    beford(inord,aftord);    cout<<endl;    return 0;}


由前序遍历与中序遍历求后序遍历

#include<iostream>#include<string>using namespace std;string str1,str2;//先中序 后前序void maketree(string s,string t){//长度为零直接返回  if(!(int)t.size()) return;//前序排列的第一个为根   char ch=t[0];//在中序遍历中找到根    int k=s.find(ch);    maketree(s.substr(0,k),t.substr(1,k));//递归左子树    maketree(s.substr(k+1),t.substr(k+1));//递归右子树//最后输出根    cout<<ch;  }int main(){  cin>>str1>>str2;  maketree(str1,str2);  return 0;}



数据结构之树(二叉树)的探索
长按二维码识别
精彩内容早知道