心里都是这个'B'树

二叉树

二叉树的节点定义
二叉树的递归非递归建树
二叉树的三种遍历(递归非递归)
二叉树的层次遍历
判断A树是否包含B
二叉树的镜像
二叉树的深度
记录二叉树中和为某一值的路径
二叉树的对称性
判断二叉数是否平衡
结语
致谢

  • 1.节点定义
typedef struct node
{
    struct node *left;
    struct node *right;
    char data;
}*TreeNode,node;
TreeNode a[maxn];
  • 2.二叉树的建立(递归和非递归)

  • 非递归建树
    如何建立一颗上图的二叉树呢,我们从后往前推,去掉所有的指针和联系,将元素拆分放到数组里面,然后再将联系建立起来.

/**非递归按层建树**
将节点先放到数组里,从下标i开始,二叉树的左节点指向下标2*i,右节点指向下标2*i+1,so,存完以后建立索引
**/
void CreatTree()
{
    printf("请按层次遍历输入节点,空结点用#表示:\n");
    printf("请输入%d个节点:\n",pow(2,t)-1);
    //t代表的层数,根据层数计算结点数
    for(int i=1; i<=pow(2,t)-1; i++) 
    {
        a[i]=(TreeNode)malloc(sizeof(node));
        scanf("%c",&a[i]->data);
        a[i]->left=a[i]->right=NULL;
    }//在节点数组里面输入节点的值并初始化left和right
    for(int i=0; i<t-1; i++)  //只需要建立t-1层索引就能建完整棵树
    {
        int tmp=pow(2,i);  //每层节点都是从pow(2,i)到pow(2,j+1)
        for(int j=tmp; j<=pow(2,i+1)-1; j++)
        {
            if(a[j]->data!='#')
            {
                if(a[j*2]->data!='#') a[j]->left=a[j*2];
                if(a[j*2+1]->data!='#') a[j]->right=a[j*2+1];
            }
        }
    }
}
  • 递归先序遍历建树
/**
递归建树,处理好边界情况,还是比较易懂的,先序遍历建树
**/
char ch;
void Creat_Tree(TreeNode &root)
{
    root=(TreeNode)malloc(sizeof(node));
    scanf("%c",&ch);
    if(ch=='#') root=NULL;
    else
    {
        root->data=ch;
        Creat_Tree(root->left);
        Creat_Tree(root->right);
    }
}
  • 根据先序遍历和中序遍历建树

已知先序遍历是 根-左-右 第一个节点是根节点,中序遍历是 左-根-右,根据先序遍历找到的根节点将中序遍历进行划分,根左边的是左子树,根右边的是右子树,然后根据节点个数将先序遍历除根节点以外的划分为左子树和右子树,依此类推,一步步分解下来

/**递归**/
TreeNode Creat(vector<char> pre,vector<char> in)
{
    if(pre.size()==0) return NULL;
    TreeNode root=(TreeNode)malloc(sizeof(node));
    root->data=pre[0];
    root->left=root->right=NULL;
    if(pre.size()==1) return root;
    int tmp=0;
    while(in[tmp++]!=pre[0]);
    vector<char> pre_left=vector<char>(pre.begin()+1,pre.begin()+tmp+1);
    vector<char> pre_right=vector<char>(pre.begin()+tmp+1,pre.end());
    vector<char> in_left=vector<char>(in.begin(),in.begin()+tmp);
    vector<char> in_right=vector<char>(in.begin()+tmp+1,in.end());
    root->left=Creat(pre_left,in_left);
    root->right=Creat(pre_right,in_right);
    return root;
}
  • 3.二叉树的三种遍历

  • 前序遍历

    一直以来对二叉树的遍历都是模模糊糊的,感觉代码简单,但是仔细一想,觉得挺麻烦,就说一下自己的见解,大神们别嘲笑我,前序遍历的顺序是 根-左-右 ,定义一个栈,先把根节点放进去,然后判断左子树是否为空,不是就往左依次存到栈里面,边存边输出,这样先输出的是中间节点,再不然就是左子树,这样就实现了 根-左,所以当左子树为空的时候也就是到左侧最底的时候,再依次得到栈顶元素,打印出右子树,先序遍历完成.

/**前序递归**/
void preorder(TreeNode root)
{
    if(root!=NULL)
    {
        printf("%c ",root->data);
        inorder(root->left);
        inorder(root->right);
    }
}
/**
前序非递归
**/
void pre_order(TreeNode root){
    stack<TreeNode> s;
    TreeNode p=root;
    while(p!=NULL||!s.empty()){
        if(p){
            s.push(p);
            printf("%c ",p->data);
            p=p->left;
        }
        else{
            p=s.front();
            s.pop();
            p=p->right;
        }
    }
}
  • 中序遍历

中序遍历是 左-根-右 ,都是先左节点根节点然后是右节点,所以和前序遍历一样的原理,只不过要先输出左节点,这样首先就要遍历到左子树的最左端,也就是左子树为空的时候,这时候将栈顶元素取出,是最左边的节点,然后输出它,pop掉,然后再往上遍历根节点的右子树,涉及到递归的就要先拿最简单的例子来写代码,然后再慢慢复杂化

/**中序递归**/
void inorder(TreeNode root)
{
    if(root!=NULL)
    {
        inorder(root->left);
        printf("%c ",root->data);
        inorder(root->right);
    }
}
/**中序非递归**/
void in_order(TreeNode root){
    stack<TreeNode> s;
    TreeNode p=root;
    while(p!=NULL||!s.empty()){
        if(p){
            s.push(p);
            p=p->left;
        }
        else{
            p=s.front();
            s.pop();
            printf("%c ",p->data);
            p=p->right;
        }
    }
}
  • 后续遍历

后续遍历不同于前两种遍历是因为是 左-右-根,如何做到先右后根而不使根节点丢失,要想个办法先把根节点保留,这里可以用两种方法,一种用两个栈,更换存入的次序做到 左-右-根

/**后续递归**/
void postorder(TreeNode root){
    if(root!=NULL){
        inorder(root->left);
        inorder(root->right);
        printf("%c ",root->data);
    }
}
/**
后续非递归
1.双栈
s1中是根-左-右,存到s2里是根-右-左,输出是左-右-根
**/
void post_order(TreeNode root){
    stack<TreeNode> s1,s2;
    s1.push(root);
    TreeNode p;
    while(!s1.empty()){
        p=s1.top();
        s1.pop();
        s2.push(p);
        if(p->left) s1.push(p->left);
        if(p->right) s1.push(p->right);
    }
    while(!s2.empty()){
        printf("%c ",s2.top()->data);
        s2.pop();
    }
}
/*
2.记录pre节点
如何先访问左右子树再访问根节点呢,先把根节点入栈,然后将右,左入栈,这
样在调用栈顶元素的时候先访问到的便是左子树,然后是右子树,如果一个节
点的左右子树为空,那么说明这个节点是叶子节点且是按 左-右 顺序的,如
果之前节点是这个节点的左右子树的话,且pre不为空,说明左右子树已经访
问,则输出根节点
*/
void post_order2(TreeNode root){
    stack<TreeNode> s;
    TreeNode pre=NULL,cur;
    s.push(root);
    while(!s.empty()){
        cur=s.top();
        if((cur->left==NULL&&cur->right==NULL)||(pre!=NULL&&(pre==cur->left||pre==cur->right))){
            printf("%c ",cur->data);
            s.pop();
            pre=cur;
        }
        else{
            if(cur->right) s.push(cur->right);
            if(cur->left) s.push(cur->left);
        }
    }
}
  • 4.二叉树的层次遍历
/**
挺简单的,从上往下,遍历每层节点,由于需要先进先出的次序,则选用队列
**/
void level_traversal(TreeNode root){
    queue<TreeNode> q;
    q.push(root);
    while(!q.empty()){
        TreeNode p=q.front();
        q.pop();
        printf("%c ",p->data);
        if(p->left) q.push(p->left);
        if(p->right) q.push(p->right);
    }
}
  • 5.判断A树中是否包含B树

在A树中找到和B树中根节点相等的节点,然后用isSubtree判断B树是否和A树相等,如果将B树遍历完则返回true,否则返回false

bool isSubtree(TreeNode pRootA, TreeNode pRootB)
{
    if (pRootB == NULL) return true;
    if (pRootA == NULL) return false;
    if (pRootB->data == pRootA->data)
    {
        return isSubtree(pRootA->left, pRootB->left)
               && isSubtree(pRootA->right, pRootB->right);
    }
    else return false;
}
bool HasSubtree(TreeNode pRootA, TreeNode pRootB)
{
    if (pRootA == NULL || pRootB == NULL) return false;
    return isSubtree(pRootA, pRootB) ||
           HasSubtree(pRootA->left, pRootB) ||
           HasSubtree(pRootA->right, pRootB);
}
  • 6.二叉树的镜像

源二叉树 8
/ \
6 10
/ \ / \
5 7 9 11
镜像二叉树
8
/ \
10 6
/ \ / \
11 9 7 5

交换每颗树的左右节点即可

void Mirror(TreeNode pRoot)
{
    TreeNode p;
    if(pRoot==NULL) return ;
    p=pRoot->left;
    pRoot->left=pRoot->right;
    pRoot->right=p;
    Mirror(pRoot->left);
    Mirror(pRoot->right);
}

7.二叉树的深度

输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。

/**递归寻找最深的那个点**/
int TreeDepth(TreeNode pRoot)
{
    if(pRoot==NULL)
        return 0;
    int left=TreeDepth(pRoot->left);
    int right=TreeDepth(pRoot->right);
    return max(left,right)+1;
}
  • 8.记录二叉树中和为某一值的路径

输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。

vector<int> vec;
vector<vector<int>> ans;
void dfs(TreeNode root,int temp)
{
    vec.push_back(root->data);
    if(temp-root->data==0&&!root->left&&!root->right)
        ans.push_back(vec);
    if(root->left) dfs(root->left,temp-root->data);
    if(root->right) dfs(root->right,temp-root->data);
    vec.pop_back();

}
vector<vector<int> > FindPath(TreeNode root,int expectNumber)
{
    if(root==NULL) return ans;
    dfs(root,expectNumber);
    return ans;
}
  • 9.二叉树的对称性

如果一个二叉树同此二叉树的镜像是同样的,定义其为对称的。

/**和镜像二叉树的那一题的差别从交换变成了比较**/
bool isSame(TreeNode* p1,TreeNode* p2)
{
    if(p1==NULL&&p2!=NULL)return false;
    if(p2==NULL&&p1!=NULL)return false;
    if(p1==NULL&&p2==NULL)return true;
    if(p1->val==p2->val)return isSame(p1->right,p2->left)
                                   && isSame(p1->left,p2->right);
    else
        return false;
}
bool isSymmetrical(TreeNode* pRoot)
{
    if(pRoot==NULL)return true;
    return isSame(pRoot->left,pRoot->right);
}

10.判断二叉数是否平衡

平衡二叉树的左右子树的高度相差不大于1

/**求树的深度的进化版,求左右子树的最大深度,然后比较,若高度差不超过1,则树平衡**/
int cnt(TreeNode pRoot)
{
    if(pRoot==NULL) return 0;
    int getleft=cnt(pRoot->left);
    if(getleft==-1) return -1;
    int getright=cnt(pRoot->right);
    if(getright==-1) return -1;
    return abs(getleft-getright)<=1?1+max(getleft,getright):-1;
}
bool IsBalanced_Solution(TreeNode pRoot)
{
    return cnt(pRoot)!=-1;
}
  • 总结

个人对二叉树这方面的总结,还不完备,以后会接着编辑,咳咳,有些地方该有图的没加,其实自己画画图更好理解一些

  • 致谢

剑指offer
二叉树的各种操作(递归和非递归遍历,树深度,结点个数等等)(Java)(鑫爷出版,必属精品)

猜你喜欢

转载自blog.csdn.net/hengtian_real/article/details/79833337