二叉树

一、二叉树的逻辑结构

二叉树的定义
二叉树是n(n≥0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树右子树的二叉树组成。
二叉树的特点:
⑴ 每个结点最多有两棵子树;
⑵ 二叉树是有序的,其次序不能任意颠倒。
注意:二叉树和树是两种树结构。

特殊的二叉树

斜树

  1. 所有结点都只有左子树的二叉树称为左斜树;
  2. 所有结点都只有右子树的二叉树称为右斜树;
  3. 左斜树和右斜树统称为斜树。

斜树的特点:

  1. 在斜树中,每一层只有一个结点;
  2. 斜树的结点个数与其深度相同。

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

满二叉树的特点:

  1. 叶子只能出现在最下一层;
  2. 只有度为0和度为2的结点。

满二叉树在同样深度的二叉树中结点个数最多
满二叉树在同样深度的二叉树中叶子结点个数最多

完全二叉树
对一棵具有n个结点的二叉树按层序编号,如果编号为i(1≤i≤n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中的位置完全相同。
在满二叉树中,从最后一个结点开始,连续去掉任意个结点,即是一棵完全二叉树。

完全二叉树的特点

  1. 叶子结点只能出现在最下两层,且最下层的叶子结点都集中在二叉树的左部;
  2. 完全二叉树中如果有度为1的结点,只可能有一个,且该结点只有左孩子。
  3. 深度为k的完全二叉树在k-1层上一定是满二叉树。

二叉树的基本性质

  1. 二叉树的第i层上最多有2的i-1次方个结点(i≥1)。
  2. 一棵深度为k的二叉树中,最多有2的k次方-1个结点,最少有k个结点。
  3. 在一棵二叉树中,如果叶子结点数为n0,度为2的结点数为n2,则有: n0=n2+1。

在有n个结点的满二叉树中,有多少个叶子结点?
叶子结点n0=(n + 1)/2

完全二叉树的基本性质
4. 具有n个结点的完全二叉树的深度为 log2 [n] +1。
5. 对一棵具有n个结点的完全二叉树中从1开始按层序编号,则对于任意的序号为i(1≤i≤n)的结点(简称为结点i),有:
(1)如果i>1,
则结点i的双亲结点的序号为 i/2;
如果i=1,
则结点i是根结点,无双亲结点。
(2)如果2i≤n,
则结点i的左孩子的序号为2i;
如果2i>n,
则结点i无左孩子。
(3)如果2i+1≤n,
则结点i的右孩子的序号为2i+1;
如果2i+1>n,
则结点 i无右孩子。

二叉树的遍历操作
二叉树的遍历是指从根结点出发,按照某种次序访问二叉树中的所有结点,使得每个结点被访问一次且仅被访问一次。
1.前序遍历
若二叉树为空,则空操作返回;
否则:
①访问根结点;
②前序遍历根结点的左子树;
③前序遍历根结点的右子树。
2.中序遍历
若二叉树为空,则空操作返回;
否则:
①中序遍历根结点的左子树;
②访问根结点;
③中序遍历根结点的右子树。
3.后序遍历
若二叉树为空,则空操作返回;
否则:
①后序遍历根结点的左子树;
②后序遍历根结点的右子树。
③访问根结点;
4.层序遍历
二叉树的层次遍历是指从二叉树的第一层(即根结点)开始,从上至下逐层遍历,在同一层中,则按从左到右的顺序对结点逐个访问。

二、二叉树的存储结构及实现

顺序存储结构

二叉树的顺序存储结构就是用一维数组存储二叉树中的结点,并且结点的存储位置(下标)应能体现结点之间的逻辑关系——父子关系。
前序遍历

void Preorder(int root, char data[]){
 if(data[root]!='\0'){
  cout<<data[root] ;   
  Preorder(2*root,data);
  Preorder(2*root+1,data);
 }
 return;
}

中序遍历

void InOrder(int root, char data[]){
 if(data[root]!='\0'){
  InOrder(2*root,data);
  cout<<data[root] ;   
   InOrder(2*root+1,data); 
 }
 return;
}

后序遍历

void PostOrder(int root, char data[]){
 if(data[root]!='\0'){
   PostOrder(2*root,data);
   PostOrder(2*root+1,data);
  cout<<data[root] ;   
 }
 return;
}

构建

void create(char preorder[],char inorder[],int start_p, int end_p,int start_i,int end_i, char data[],int root){
 if(start_p>end_p)
  return ;
 else{
  int k;
  for(int i=start_i;i<=end_i;i++){
   if(inorder[i]==preorder[start_p]){
    k=i;
    break;
   }
  }
  data[root]=preorder[start_p];
  create(preorder,inorder,start_p+1,start_p+k-start_i,start_i,k-1,data, 2*root);
  create(preorder,inorder,start_p+k-start_i+1,end_p,k+1,end_i,data,2*root+1);
 }
 return ;
}

int main(){   
    char * data;
 int total=1;
 char preorder[100],inorder[100];
 cin>>preorder>>inorder;
 int length=0;
 while(preorder[length]!='\0')
  length++;
 data=new char[pow(2,length+1)];
 memset(data,'\0',pow(2,length+1));
 create(preorder,inorder,0,length-1,0,length-1,data,1);
 order(1,data);
 return 0;
}

二叉树的顺序存储结构一般仅存储完全二叉树。

二叉链表
基本思想:令二叉树的每个结点对应一个链表结点,链表结点除了存放与二叉树结点有关的数据信息外,还要设置指示左右孩子的指针。
结点结构: lchild data rchild
data:数据域,存放该结点的数据信息;
lchild:左指针域,存放指向左孩子的指针;
rchild:右指针域,存放指向右孩子的指针。

template <class T>
struct BiNode
{
    T data;
    BiNode<T> *lchild, *rchild;
};

具有n个结点的二叉链表中,有n+1个空指针。
二叉链表存储结构的类声明

template <class T>
class BiTree
{    
  public:
       BiTree(); 
        ~BiTree( );            
        void PreOrder(){PreOrder(root);} 
        void InOrder() {InOrder(root);} 
        void PostOrder() {PostOrder(root);} 
        void LevelOrder(){LeverOrder(root)};
  private:
        BiNode<T> *root; 
        BiNode<T> * Creat( ); 
        void Release(BiNode<T> *root);
        void PreOrder(BiNode<T> *root); 
        void InOrder(BiNode<T> *root); 
        void PostOrder(BiNode<T> *root); 
        void LevelOrder(BiNode<T> *root);
 };

前序遍历——递归算法

template   <class T>
void   BiTree::PreOrder(BiNode<T> *root) 
{
        if (root ==NULL)  return;     
        else {
            cout<<root->data;         
            PreOrder(           );    
            PreOrder(           );    
        }
 }

前序遍历——非递归算法
二叉树前序遍历的非递归算法的关键:
在前序遍历过某结点的整个左子树后,如何找到该结点的右子树的根指针。
解决办法:在访问完该结点后,将该结点的指针保存在栈中,以便以后能通过它找到该结点的右子树。
思想:
遇到一个结点,就访问该结点,并把此结点推入栈中,然后遍历它的左子树;
遍历完它的左子树后,从栈顶托出这个结点,并按照它的右链接指示的地址再去遍历该结点的右子树结构。

template <class T>
void BiTree::PreOrder(BiNode<T> *root) {
  SeqStack<BiNode<T> *>  s;
     while (root!=NULL | | !s.empty())     {
         while (root!= NULL)         {
             cout<<root->data;
             s.push(root);
             root=root->lchild;  
         }
         if (!s.empty()) { 
             root=s.pop();
             root=root->rchild;  
         }
     }
}

二叉树的建立
设二叉树中的结点均为一个字符。假设扩展二叉树的前序遍历序列由键盘输入,root为指向根结点的指针,二叉链表的建立过程是:

  1. 按扩展前序遍历序列输入结点的值
  2. 如果输入结点值为“#”,则建立一棵空的子树
  3. 否则,根结点申请空间,将输入值写入数据域中
  4. 以相同方法的创建根结点的左子树
  5. 以相同的方法创建根结点的右子树
template <class T>
BiTree ::BiTree(){ 
      root=creat()}
//不传参,带返回值
template <class T>
BiNode<T> * BiTree ::Creat(){
     BiNode<T> *root; char ch;
    cin>>ch;
    if (ch=='# ')     root=NULL; 
    else {
        root=new BiNode<T>; 
        root->data=ch;
        root->lchild=creat(); 
        root->rchild= creat(); 
    }  
  return root
}
template<class T>
BiTree<T>::BiTree( )
{
 Creat(root);
}

//传参,不带返回值
template <class T>
void BiTree<T>::Creat(BiNode<T> * &root  )
{
     T ch;
     cout<<"请输入创建一棵二叉树的结点数据"<<endl;
     cin>>ch;
         if (ch=="#") root = NULL;
         else{ 
            root = new BiNode<T>;       //生成一个结点
                root->data=ch;
               Creat(root->lchild );    //递归建立左子树
               Creat(root->rchild);    //递归建立右子树
    } 
}

中序遍历——递归算法

template <class T>
void BiTree::InOrder (BiNode<T> *root)
{
         if (root==NULL) return;     
         else {
               InOrder(root->lchild); 
               cout<<root->data; 
               InOrder(root->rchild);
         }
}

非递归中序遍历二叉树
遇到一个结点,就把它推入栈中,并去遍历它的左子树
遍历完左子树后,从栈顶托出这个结点并访问之,然后按照它的右链接指示的地址再去遍历该结点的右子树。

template <class T>
void BiTree::InOrderwithoutD (BiNode<T> *root)
  {
      stack< BiNode<T> * > aStack;
      while(root)
      {
          aStack.push(root);
          root=root->lchild;
      }
      if(!aStack.empty())
      {
          root=aStack.top();
          aStack.pop();
          cout<<root->data;
          root=root->rchild;
      }
}

后序遍历——递归算法

template <class T>
void BiTree::PostOrder(BiNode<T> *root)
{ 
    if (root==NULL) return; 
    else {
         PostOrder(root->lchild); 
         PostOrder(root->rchild); 
         cout<<root->data;          
    }
}

非递归后序遍历二叉树
遇到一个结点,把它推入栈中,遍历它的左子树
左子树遍历结束后,还不能马上访问处于栈顶的该结点,而是要再按照它的右链接结构指示的地址去遍历该结点的右子树
遍历遍右子树后才能从栈顶托出该结点并访问之
方案一

栈中的元素类型定义StackElement
  enum Tags{Left,Right}; //特征标识定义
 template <class T>
 class StackElement  //栈元素的定义
 {
 public:
 BiTreeNode<T>* pointer;     //指向二叉树结点的指针
 Tags tag; //特征标识申明
 }; 
#include <stack>
Using namespace std;
template<class T>
void BiTree<T>::PostOrderWithoutRecusion(BiTreeNode<T>* root){
 StackElement<T> element;
 stack<StackElement<T > > aStack;//栈申明
 BiTreeNode<T>* pointer;
 if(root==NULL)
  return;//空树即返回
else    pointer=root;    
 while(true){
   while(pointer!=NULL){//进入左子树
  element.pointer=pointer;
  element.tag=Left; //沿左子树方向向下周游
  aStack.push(element);
  pointer=pointer->lchild;  
  }
     element=aStack.pop();
            pointer=element.pointer;
            while(element.tag==Right){
        cout<<pointer->data;
        if(aStack.empty())  return;
     else{
        element=aStack.pop();
       pointer=element.pointer;
     }//end else
        } //endwhile
 element.tag=Right; 
    aStack.push(element);
    pointer=pointer->rchild(); 
     }//end while
 }

方案二

void tree::T_print(bnode *bt)
{
    stack<bnode*> s;
    bnode *cur, *pre=NULL;
    if (root==NULL) return;
    s.push(bt);
    while (!s.empty())    {
        cur=s.top();
        if ((cur->Lchild==NULL&&cur->Rchild==NULL)||(pre!=NULL&&(pre==cur->Lchild||pre==cur->Rchild)))        //第一个括号说明访问的节点没有孩子。
        {
            cout<<cur->data;            s.pop();            pre=cur;
        }
        else
        {
            if (cur->Rchild!=NULL)            s.push(cur->Rchild);
            if (cur->Lchild!=NULL)            s.push(cur->Lchild);
        }
    }
}

层序遍历

#include <queue>
using namespace std;
template<class T>
Void BiTree<T>::LevelOrder(BinaryTreeNode<T>* root){
 queue<BiTreeNode<T>*> aQueue;
 if(root)
  aQueue.push(root);
 while(!aQueue.empty())
{
  root=aQueue.front(); //取队列首结点
   aQueue.pop();
                     cout<<pointer->data;//访问当前结点
  if(root->lchild) //左子树进队列
   aQueue.push(root->lchild);
  if(root->rchild) //右子树进队列
   aQueue.push(root->rchild);  
 }//end while
}

析构函数

template<class T>
void BiTree<T>::Release(BiNode<T>* root)
{
  if (root != NULL){                  
      Release(root->lchild);   //释放左子树
      Release(root->rchild);   //释放右子树
      delete root;
  }  
}
template<class T>
BiTree<T>::~BiTree(void)
{
 Release(root);
}

求二叉树结点个数

template<class T>
BiTree<T>::~BiTree(void)
{
 Release(root);
}
void Count(BiNode *root){
    if (root) {
         Count(root->lchild);
         number+ +;  //number为数据成员
         Count(root->rchild);
   }
}
template<class T>
int BiTree<T>::count(BiNode<T>* root){
 int number=0;
 if (root==NULL)
  number=0;
 else
  number=count(root->lchild)+count(root->rchild)+1;
 return number;
}

树中叶子节点的数目

template<class T>
int BiTree<T>::leafcount(BiNode<T>* root){
 int number=0;
 if (root==NULL)
  number=0;
 else if(root->lchild==NULL && root->rchild==NULL)
  number=1;
 else
     number=leafcount(root->lchild)+leafcount(root->rchild);
 return number;
}

计算树的高度

template<typename T> 
 int BiTree<T>::cal_height(BiTreeNode<T> * root){
 int lheight=0,rheight=0;
 if (root==0)    return 0; 
 lheight=cal_height(root->lchild);
 rheight=cal_height(root->rchild);
 if (lheight>rheight) return lheight+1;
 else   return rheight+1;
}

输出中缀表达式,并加上相应的括号

void BiTree<T>::In_Expression(BiNode<T>* root){
 if(root)
 {
      if(root!=this->root&&root->lchild!=0&&root->rchild!=0)
              cout<<"(";
    In_Expression(root->lchild);
    cout<<root->data;
       In_Expression(root->rchild);
     if(root!=this->root&&root->lchild!=0&&root->rchild!=0)
   cout<<")";
 }
 
}

二叉树逆时针旋转90后输出

template <class T>
void BiTree<T>::Left_Rotate(BiNode<T>* root,int level)
{
 if(root)
 {
  Left_Rotate(root->rchild,level+1);
  for(int i=0;i<level;i++)
   cout<<"\t";
  cout<<root->data<<endl;
  Left_Rotate(root->lchild,level+1);
 }
}

三叉链表
在二叉链表的基础上增加了一个指向双亲的指针域。
lchild data parent parent
data、lchild和rchild三个域的含义同二叉链表的结点结构;
parent域为指向该结点的双亲结点的指针。
结点数据类型声明:

template<class T>
struct Node
{
 T data;
 Node<T> * lchild, *rchild,*parent;
};
template <class T>
BiNode<T> * BiTree<T>::Creat(BiNode<T> * &root ,BiNode<T> *parent)
{
 T ch;
 cout<<"请输入创建一棵二叉树的结点数据"<<endl;
 cin>>ch;
    if (ch=="#") root = NULL;
    else{ 
      root = new BiNode<T>;       //生成一个结点
          root->data=ch;
      root->parent=parent;
         Creat(root->lchild,root );    //递归建立左子树
         Creat(root->rchild,root);    //递归建立右子树
    } 
    return root;
}
template<class T>
BiTree<T>::BiTree( int i)
{
    number=0;
    Creat(root,NULL);
}

森林转换为二叉树
⑴ 将森林中的每棵树转换成二叉树;
⑵ 从第二棵二叉树开始,
依次把后一棵二叉树的根结点作为前一棵二叉树根结点的右孩子,
当所有二叉树连起来后,此时所得到的二叉树就是由森林转换得到的二叉树。
二叉树转换为树或森林
⑴ 加线——若某结点x是其双亲y的左孩子,则把结点x的右孩子、右孩子的右孩子、……,都与结点y用线连起来;
⑵ 去线——删去原二叉树中所有的双亲结点与右孩子结点的连线;
⑶ 层次调整——整理由⑴、⑵两步所得到的树或森林,使之层次分明。
森林的遍历
⑴前序(根)遍历:前序遍历森林即为前序遍历森林中的每一棵树。
⑵后序(根)遍历:后序遍历森林即为后序遍历森林中的每一棵树。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值