二叉树

二叉树

高度:节点到叶子节点的最长边数
深度:根节点到这个节点的边数
层数:深度+1
树的高度:根节点的高度

二叉树的遍历

//DFS:先序遍历,后序遍历,中序遍历,递归
①打印当前节点
②打印左子树
③打印右子树

前序遍历:对于树中的任意节点来说,打印顺序:①②③
中序遍历:对于树中的任意节点来说,打印顺序:②①③
后序遍历:对于树中的任意节点来说,打印顺序:②③①

//BFS:按层遍历,通常使用队列完成

class Solution {
public:
    vector<int> levelOrder(TreeNode* root) {

        vector<int> v;

        if(root == NULL) return v;

        queue<TreeNode*> q; //先入先出
        q.push(root);

        while(!q.empty())
        {
            TreeNode* tmp = q.front();
            q.pop();
            
            v.push_back(tmp->val);

            if(tmp->left) q.push(tmp->left);
            if(tmp->right) q.push(tmp->right);
        }
        return v;
    }
};

从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
在这里插入图片描述
//BFS按层遍历

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {

        vector<vector<int>> vv;

        if(root == NULL) return vv;

        queue<TreeNode*> q;
        q.push(root);

        while(!q.empty())
        {
            vector<int> v; //循环体里的临时变量每次循环结束都会自动销毁
            int size = q.size();
            for(int i = 0;i < size;++i)
            {
                TreeNode* tmp = q.front();
                q.pop();

                v.push_back(tmp->val);

                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            vv.push_back(v);
        }
        return vv;

    }
};

//DFS 先序遍历 按depth索引存放

class Solution {
public:

    vector<vector<int>> vv;

    vector<vector<int>> levelOrder(TreeNode* root) {

        //DFS
        dfs(root,0);
        return vv;    

    }
    void dfs(TreeNode* root,int depth)
    {
        if(root == NULL) return;
        if(vv.size() == depth)
        {
            vector<int> v;
            vv.push_back(v);
        }
        vv[depth].push_back(root->val);
        dfs(root->left,depth+1);
        dfs(root->right,depth+1);
    }
};

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

例如:

给定二叉树 [3,9,20,null,null,15,7],返回它的最大深度 3 。

class Solution {

public:
    int maxDepth(TreeNode* root) {
        
        //递归 
        //关系式:当前节点最大深度 = max(左边节点最大深度,右边节点最大深度)+1,终止条件:节点为空
        if(root == NULL) 
        {
            return 0;
        }

        int left = maxDepth(root->left);
        int right = maxDepth(root->right);

        return max(left,right)+1;
        
    }
};
class Solution {

public:
    int maxDepth(TreeNode* root) {
    
        //利用队列先进先出的特点 按层遍历
        if(root == NULL) return 0;

        int depth = 0;
        queue<TreeNode*> q;
        q.push(root);

        while(!q.empty()) 
        {
            depth++;
            //queue<TreeNode*> tmp;
            int size = q.size(); //为什么这里一定要定义,不然q.size()是一个会变的值
            for(int i = 0;i < size;++i)
            {
                TreeNode* tmpNode = q.front();
                if(tmpNode->left) q.push(tmpNode->left);
                if(tmpNode->right) q.push(tmpNode->right);
                q.pop();
            }            
            /*while(!q.empty())
            {
                TreeNode* tmpNode = q.front();
                if(tmpNode->right) tmp.push(tmpNode->right);
                if(tmpNode->left) tmp.push(tmpNode->left);
                q.pop();
            }

            swap(q,tmp);*/
        }
        return depth;       
    

面试题55 - II. 平衡二叉树

对于N个节点的平衡二叉树,高度接近logN
题目描述:
输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
在这里插入图片描述
思路一:

从顶至底,遍历节点,计算深度差。

计算每一个节点的左右子树深度,
深度差若大于1,则直接返回false,
否则,继续判断其左子树和右子树是否平衡。
特殊情况:若root为空 返回true

这种方法计算深度时重复遍历了很多节点,
总体时间复杂度 == 每层执行复杂度 × 层数复杂度 = O(N×logN) 。

class Solution {
public:
    //计算节点深度
    int maxDepth(TreeNode* root)
    {
        if(root == NULL) return 0;

        return max(maxDepth(root->left),maxDepth(root->right)) + 1; 
    }

    bool isBalanced(TreeNode* root) {

        if(root == NULL) return true;

        if(abs(maxDepth(root->left) - maxDepth(root->right)) > 1) return false; //abs() 绝对值函数
        return isBalanced(root->left) && isBalanced(root->right);
        //当前节点满足左右子树的深度相差不超过1,且其左右子树均为平衡二叉树,则return true


        /*int left = maxDepth(root->left);
        int right = maxDepth(root->right);

        int sub = left > right?(left - right):(right - left);

        if(sub > 1) return false;
        if(isBalanced(root->left) && isBalanced(root->right)) return true;

        return false;*/
    }
};

思路二:
后序遍历,自底向上,即判断当前节点之前已知左右子树是否是平衡树
每个节点只遍历了常数次,减少了时间复杂度
时间复杂度O(N)

重点:以-1为不是平衡树的标记
若为平衡二叉树则返回深度,方便当前root的计算,否则返回-1

class Solution {
public:
    
    //若为平衡二叉树则返回深度,否则返回-1
    int postOrder(TreeNode* root)
    {
        if(root == NULL) return 0;

        int left = postOrder(root->left);
        int right = postOrder(root->right);

        if(left == -1 || right == -1) return -1;
        return abs(left-right) <= 1 ? max(left,right) + 1:-1;

    }
    

    bool isBalanced(TreeNode* root) {

        return postOrder(root) + 1;
    }
};

面试题32 - III. 从上到下打印二叉树 III

题目描述:请实现一个函数按照之字形顺序打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右到左的顺序打印,第三行再按照从左到右的顺序打印,其他行以此类推。
在这里插入图片描述
1.revere函数

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {

        vector<vector<int>> vv;

        if(root == NULL) return vv;

        int depth = 0;
        queue<TreeNode*> q;
        q.push(root);

        while(!q.empty())
        {
            depth++;
            vector<int> v; 
            int size = q.size();
            for(int i = 0;i < size;++i)
            {
                TreeNode* tmp = q.front();
                q.pop();

                v.push_back(tmp->val);

                if(tmp->left) q.push(tmp->left);
                if(tmp->right) q.push(tmp->right);
            }
            if(depth%2 == 0) reverse(v.begin(),v.end());
            vv.push_back(v);
        }
        return vv;

    }
};

2.deque双端队列

class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {

        vector<vector<int>> vv;

        if(root == NULL) return vv;

        bool foe = 0; //0 奇数层 1 偶数层
        deque<TreeNode*> d;
        d.push_front(root);

        while(!d.empty())
        {
            vector<int> v; //循环体里的临时变量每次循环结束都会自动销毁

            for(int i = d.size();i > 0;--i) //这样写可以少定义一个size变量
            {
                TreeNode* tmp;
                if(foe == 0) //奇数层 前取后插 先插左
                {
                    tmp = d.front();              
                    d.pop_front();

                    if(tmp->left) d.push_back(tmp->left);
                    if(tmp->right) d.push_back(tmp->right);
                }
                else //偶数层 后取前插 先插右
                {
                    tmp = d.back();
                    d.pop_back();

                    if(tmp->right) d.push_front(tmp->right);
                    if(tmp->left) d.push_front(tmp->left);
                }
                v.push_back(tmp->val);
            }
            foe = !foe;
            vv.push_back(v);
        }
        return vv;
    }
};

面试题34. 二叉树中和为某一值的路径
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
在这里插入图片描述
解:
DFS
//题目要求:寻找一条从根节点到叶子节点的路径,路径和为sum。
//注意:必须为叶子节点,所以即使达到了sum,只要没达到叶子节点就继续往下找。
//注意:可能出现负数。

优化
//递归少传一个tmpsum变量:可使用sum - val 的方式 (√)
//递归少传一个vector容器,可使用回溯时pop_back的方法 (√)

 //DFS
 //题目要求:寻找一条从根节点到叶子节点的路径,路径和为sum。
 //注意:必须为叶子节点,所以即使达到了sum,只要没达到叶子节点就继续往下找。可能出现负数。
 //优化
 //递归少传一个tmpsum变量:可使用sum - val 的方式 (√)
 //递归少传一个vector容器,可使用回溯时pop_back的方法 (√)
//只要没找到路径和等于sum的叶子节点,就继续往下找
//回溯前pop掉当前节点
class Solution {
public:
    vector<vector<int>> vv;
    vector<int> way;
    
    vector<vector<int>> pathSum(TreeNode* root, int sum) {

        dfs(root, sum);
        return vv;

    }

    void dfs(TreeNode* node, int sum)
    {
        if(node == NULL) return;

        sum -= node->val;
        way.push_back(node->val);

        if(sum == 0 && node->left == NULL && node->right == NULL) vv.push_back(way);

        dfs(node->left,sum);
        dfs(node->right,sum);
        
        way.pop_back();       
        return;
    }
};

二叉搜索树

一、
①左子树所有值均小于根节点,右子树所有值均大于根节点,且树中的所有节点均满足这一条件。
②二叉搜索树中序遍历得到的数组即为升序的。

二、
常规操作:插入,查找,删除(三种情况:无子节点,只有一个子节点,有两个子节点)
时间复杂度:
退化成链表:O(n);
完全二叉树:O(logn)。

面试题54. 二叉搜索树的第k大节点
给定一棵二叉搜索树,请找出其中第k大的节点。

在这里插入图片描述
解:

class Solution {
public:
    
    int res,k = 0;

    //二叉树中序右中左遍历为降序排序
    int kthLargest(TreeNode* root, int k) {
        this->k = k; //将传进来的参数 存为类公共变量
        dfs(root); 
        return res;
    }
    void dfs(TreeNode* root)
    {
        if(root == NULL) return;
        
        dfs(root->right);
        
        if(k == 0) return; //已经找到目标节点,递归终止
        if(k == 1) res = root->val; //k减到1时遍历的节点即为遍历到的第k个节点
        k--;
        
        dfs(root->left);       
        return;
    }

    //二叉搜索树中序遍历得到的数组即为有序的
    /*vector<int> v;

    int kthLargest(TreeNode* root, int k) {

        //if(root == NULL) return -1;
        forhead(root); 
        return v[v.size() - k];
    }
    void forhead(TreeNode* root)
    {
        if(root == NULL) return;

        forhead(root->left);
        v.push_back(root->val);
        forhead(root->right);
        return;
    }*/
};

面试题26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构)

B是A的子结构, 即 A中有出现和B相同的结构和节点值。

在这里插入图片描述
解:
(1)先序遍历A,找到A中与B根节点匹配的节点tmpA(isSubStructure(TreeNode* A, TreeNode* B))
(2)判断B是否是以tmpA为根节点的树的子结构(recur(TreeNode* A, TreeNode* B))

对于isSubStructure(TreeNode* A, TreeNode* B)
1.特殊情况:A或B为空 直接返回false
2.返回情况:三种,B与A匹配,B与A的左节点匹配,B与A的右节点匹配,返回true
若都不匹配,返回false

对于recur(TreeNode* A, TreeNode* B)
1.终止条件
B为空,说明B树已全部匹配成功,返回true
A为空,匹配失败,返回false
2.返回情况
AB节点值不等时,匹配失败,返回false
A的左右节点与B的左右节点均匹配成功,返回true

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {

        if(A == NULL || B == NULL) return false;
        
        //先序遍历A,找到与B相同的第一个节点
        if(recur(A,B)) return true;
        
        if(isSubStructure(A->left,B)||
        isSubStructure(A->right,B)) return true;
        return false;
    }
    //判断B是不是以节点A为根节点的树的子树
    bool recur(TreeNode* A, TreeNode* B)
    {
        if(B == NULL) return true;
        if(A == NULL || A->val != B->val) return false;

        if(recur(A->left,B->left) && recur(A->right,B->right)) return true;
        return false;
    }
};

面试题27. 二叉树的镜像

请完成一个函数,输入一个二叉树,该函数输出它的镜像。

在这里插入图片描述

//层序遍历交换左右节点
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {

        if(root == NULL) return NULL;

        queue<TreeNode*> q;
        q.push(root);
        while(!q.empty())
        {
            TreeNode* tmp = q.front();
            swap(tmp->right,tmp->left);
            if(tmp->left) q.push(tmp->left);
            if(tmp->right) q.push(tmp->right);
            q.pop();
        }
        return root;
    }
};

//递归修改
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* root) {

        if(root == NULL) return NULL;

        TreeNode* tmp = root->left;
        root->left = mirrorTree(root->right);
        root->right = mirrorTree(tmp);
        return root;        
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值