二叉树(3)

二叉树的子树
在二叉树中以任何一个节点为头部的整棵树称作二叉树的子树。
这里写图片描述
这里写图片描述

平衡二叉树(AVL树)
1.空树是平衡二叉树
2.如果一棵树不为空,并且其中所有的子树都满足各自的左子树与右子树的高度都不超过1。
这里写图片描述
给定一颗二叉树的头节点head,判断一棵树是否是平衡二叉树。

#include<iostream>
#include<string>
#include<stack>
#include<queue>
using namespace std;

class BinaryTreeNode  
{
public:
    char data;  
    BinaryTreeNode *Left;  
    BinaryTreeNode *Right;  
}; 

//创建二叉树,顺序依次为中间节点->左子树->右子树

void createBiTree(BinaryTreeNode* &T)   //这里加上&意思是传递的参数为指针的引用,括号里面等价于 BiTreeNode* &T
 {                                 //这样的意义在于在函数执行过后,传递进来的指针会发生改变(引用的作用),不可以去掉&
     char c;
     cin >> c;
     if('#' == c)               //当遇到#时,令树的根节点为NULL,从而结束该分支的递归
         T = NULL;
     else
     {
         T = new BinaryTreeNode;
         T->data=c;
         createBiTree(T->Left);
         createBiTree(T->Right);
     }
 }

int treeDepth(BinaryTreeNode* root){
    if(root==NULL){
        return 0;
    }
    if(root->Left==NULL){
        return treeDepth(root->Right)+1;
    }
    if(root->Right==NULL){
        return treeDepth(root->Left)+1;
    }
    return treeDepth(root->Right)>treeDepth(root->Left)?treeDepth(root->Right)+1:treeDepth(root->Left)+1;
}

bool isAVLtree(BinaryTreeNode* root){
    if(root==NULL)
        return true;
    if(abs(treeDepth(root->Right)-treeDepth(root->Left))>1)
      return false; 
    return isAVLtree(root->Left)&&isAVLtree(root->Right); 
}

int main(){
    BinaryTreeNode* T;               //声明一个指向二叉树根节点的指针               
    createBiTree(T);               //abd###ce##fg###
    cout<<"二叉树创建完成!"<<endl; 
    cout<<"二叉树深度:"<<endl; 
    cout<<treeDepth(T)<<endl;
    cout<<endl;
    if(isAVLtree(T)==true){
       cout<<"true"<<endl;
    }
    else
       cout<<"false"<<endl;

    return 0;
}

思路为二叉树的后序遍历。(?)

搜索二叉树
搜索二叉树的特征:
每颗子树的头节点的值都比各自左子树上的所有节点值要大,也都比各自右子树上的所有节点值要小。
搜索二叉树按照中序遍历得到的序列,一定是从小到大排列的。
红黑树、平衡搜索二叉树(AVL树)等,其实都是搜索二叉树的不同实现。

给定一颗二叉树的头节点head,判断一棵树是否是搜索二叉树。
1.改写二叉树的中序遍历
2.遍历到每个节点的值时,如果一直比上一个遍历的节点值要大,则是搜索二叉树,否则,不是搜索二叉树。
3.为了方便同时得到当前节点,和上一个遍历的节点,二叉树中序遍历非递归的实现比较合适。

#include<iostream>
#include<string>
#include<stack>
#include<queue>
using namespace std;


class BinaryTreeNode  
{
public:
    char data;  
    BinaryTreeNode *Left;  
    BinaryTreeNode *Right;  
}; 

//创建二叉树,顺序依次为中间节点->左子树->右子树

void createBiTree(BinaryTreeNode* &T)   //这里加上&意思是传递的参数为指针的引用,括号里面等价于 BiTreeNode* &T
 {                                 //这样的意义在于在函数执行过后,传递进来的指针会发生改变(引用的作用),不可以去掉&
     char c;
     cin >> c;
     if('#' == c)               //当遇到#时,令树的根节点为NULL,从而结束该分支的递归
         T = NULL;
     else
     {
         T = new BinaryTreeNode;
         T->data=c;
         createBiTree(T->Left);
         createBiTree(T->Right);
     }
 }

bool sousuomidorder(BinaryTreeNode* &T){
    stack<BinaryTreeNode*> s;
    char last=0;
    BinaryTreeNode* cur=T;
    while(cur!=NULL||!s.empty()){
        while(cur!=NULL){
            s.push(cur);
            cur=cur->Left;
         }
        BinaryTreeNode* node=s.top();
        s.pop();
        char now=node->data;
        if(now<last){
            return false;   
        }
        last=node->data;
        cout<<node->data;
        cur=node->Right;
    }
    return true;
 }

int main(){
    BinaryTreeNode* T;               //声明一个指向二叉树根节点的指针               
    createBiTree(T);               //abd###ce##fg###
    cout<<"二叉树创建完成!"<<endl; 

    cout<<"判断是否是搜索二叉树:"<<endl;

    if(sousuomidorder(T)==true){
       cout<<"true"<<endl;
    }
    else
       cout<<"false"<<endl;
    return 0;
}

满二叉树
满二叉树是除了最后一层的节点无任何子节点外,剩下每一层上的节点都有两个子节点。
层数为L,节点数为N,N=2^L-1

完全二叉树
是指,除了最后一层之外,其他每一层的节点数都是满的。最后一层如果也满了,是一颗满二叉树,也是完全二叉树。最后一层如果不满,缺少的节点也全部的集中在右边,那也是一颗完全二叉树。
这里写图片描述

给定一颗二叉树的头节点head,判断一棵树是否是完全二叉树。
1、采用按层遍历二叉树的方式,从每层的左边向右边依次遍历所有的节点。
2、如果当前的节点有右孩子,但没有左孩子,直接返回false。
3、如果当前节点并不是左右孩子全有,那之后的节点必须都为叶节点,否则返回false。
4、遍历过程中如果不返回false,遍历结束后返回true即可。

#include<iostream>
#include<string>
#include<stack>
#include<queue>
using namespace std;


class BinaryTreeNode  
{
public:
    char data;  
    BinaryTreeNode *Left;  
    BinaryTreeNode *Right;  
}; 

//创建二叉树,顺序依次为中间节点->左子树->右子树

void createBiTree(BinaryTreeNode* &T)   //这里加上&意思是传递的参数为指针的引用,括号里面等价于 BiTreeNode* &T
 {                                 //这样的意义在于在函数执行过后,传递进来的指针会发生改变(引用的作用),不可以去掉&
     char c;
     cin >> c;
     if('#' == c)               //当遇到#时,令树的根节点为NULL,从而结束该分支的递归
         T = NULL;
     else
     {
         T = new BinaryTreeNode;
         T->data=c;
         createBiTree(T->Left);
         createBiTree(T->Right);
     }
 }

bool wanquanerchashu(BinaryTreeNode* &T){
    queue<BinaryTreeNode*> q1;
    q1.push(T);
    char yeflag=false;
    while(!q1.empty()){
        BinaryTreeNode* cur=q1.front();
        q1.pop();
        cout<<cur->data;
        if(yeflag==true){//判断后面的各节点是不是都是叶节点
          if(cur->Left!=NULL||cur->Right!=NULL){
              return false;
          }
        }       
        if(cur->Left==NULL&&cur->Right!=NULL){
            return false;
        }
        if(cur->Left==NULL||cur->Right==NULL){
            yeflag=true;//启动判断后面的各节点是不是都是叶节点标识
        }
        if(cur->Left!=NULL){
            q1.push(cur->Left);
        }
        if(cur->Right!=NULL){
            q1.push(cur->Right);
        }
    }
    return true;
}

int main(){
    BinaryTreeNode* T;               //声明一个指向二叉树根节点的指针               
    createBiTree(T);               //abd###ce##fg###
    cout<<"二叉树创建完成!"<<endl; 
    cout<<"判断完全二叉树:"<<endl;
    if(wanquanerchashu(T)==true){
       cout<<"true"<<endl;
    }
    else
       cout<<"false"<<endl;

    return 0;
}

注意
面试中,二叉树节点类型仅包括:数据项、左孩子、右孩子。
工程上的二叉树节点类型,往往多一条指向父节点的指针。
一般默认面试中的二叉树节点结构不包括指向父节点的指针,除非特别说明。

后继节点
一个节点的后继节点是指,这个节点在中序遍历序列中的下一个节点。

前驱节点
一个节点的前驱节点是指,这个节点在中序遍历序列中的上一个节点。

案例
现在有一种新的二叉树节点类型,定义如下。

public class Node{
     public int valuepublic Node left;
     public Node right;
     public Node parent;
     public Node(int data){
            this.value=data;
     }
}

该结构比普通二叉树节点结构多了一个指向父节点的parent指针。假设有一颗这种类型的节点组成的二叉树,树中每个节点的parent指针都正确指向自己的父节点,头节点的parent指向空。只给定在二叉树中的某个节点node。该节点不一定是头节点,可能是树中任何一个节点,请实现返回后继节点的函数。

普通方法:
1.不断向上找到头节点
2.利用头结点进行中序遍历,进而获得后续节点。

普通方法要遍历所有节点,如果二叉树节点为N,时间复杂度为O(N),额外空间复杂度为O(N)。

最优解法:
情况一:如果node有右子树,那么后继节点就是右子树上最左边的节点。
情况二:如果node没有右子树,那么先看node是不是node父节点的左孩子,如果是左孩子,那么此时node的父节点就是node的后继节点,如果是右孩子,就向上寻找node的后继节点,假设向上移动的节点记为s,s的父节点记为p,如果发现s是p的左孩子,那么节点p就是node节点的后继节点,否则就一直向上移动。
情况三:如果一直向上,都移动到空节点了,还是没有发现node的后继节点,说明node根本不存在后继节点,返回空即可。

如果node节点的node后继节点之间的实际距离为L,最优解法只用走过L个节点,时间复杂度为O(L),额外空间复杂度为O(1),也就是常数级别。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值