代码随想录:二叉树

二叉树理论基础篇

1. 二叉树种类

满二叉树

满二叉树:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。深度为k,有2^k-1个节点。

完全二叉树

在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层(h从1开始),则该层包含 1~ 2^(h-1) 个节点。

二叉搜索树

 二叉搜索树是一个有序树。

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
  • 它的左、右子树也分别为二叉排序树

平衡二叉搜索树

是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

2. 二叉树存储方式

链式存储或顺序存储

如果父节点的数组下标是 i,那么它的左孩子就是 i * 2 + 1,右孩子就是 i * 2 + 2。

3. 二叉树遍历方式

深度优先DFS:前序 中序 后序

广度优先BFS:层序遍历

 4. 二叉树的定义

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};

二叉树的递归遍历

思路:

递归三部曲:

确定返回值和参数表

确定终止递归条件

确定单层递归逻辑

代码:

前序遍历为例子:根左右

class Solution {
public:
    void MyTraversal(TreeNode* root, vector<int>& res)
    {
        if(!root) return;
        res.push_back(root->val); // 根
        if(root->left) MyTraversal(root->left, res); // 左孩子
        if(root->right) MyTraversal(root->right,res); // 右孩子
    }

    vector<int> preorderTraversal(TreeNode* root) {
        vector<int> res;
        MyTraversal(root, res);
        return res;    
    }
};

二叉树的层序遍历

思路:

借助队列FIFO性质

先把根节点入队

开始循环1,循环终止条件:队空

{

        记录当前队列大小count(就是该层的数据个数)

        开始循环2,循环终止条件:count减为0(当前层弹空)

        {

                根结点pop,左右节点push

                count--;

        }

}

102二叉树的层序遍历

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

        queue<TreeNode*> MyQueue;
        if(root) MyQueue.push(root); // 根节点入队

        // 循环1:队弹空
        while(!MyQueue.empty())
        {
            int count = MyQueue.size();// 记录当前层数据个数
            vector<int> tmp_res; // 收集当前层出队数据

            // 循环2:弹出一层的元素
            // 这里一定要使用固定大小count,不要使用MyQueue.size(),因为MyQueue.size是不断变化的
            for(int i = 0; i < count;++i)
            {
                TreeNode* cur_node = MyQueue.front(); 
                if(!cur_node) break;
                tmp_res.push_back(cur_node->val);

                MyQueue.pop(); // 弹出当前节点
                if(cur_node->left) MyQueue.push(cur_node->left); // 左孩子入队
                if(cur_node->right) MyQueue.push(cur_node->right); // 右孩子入队
            }

            res.push_back(tmp_res);
        }

        return res;
    }

226.翻转二叉树

力扣题目链接

思路:

前序遍历和后续遍历都可以,中序遍历相当于翻转了两次会还原

    TreeNode* invertTree(TreeNode* root) {
        if(!root) return root;

        TreeNode* tmp_node = root->left;
        root->left = root->right;
        root->right = tmp_node;
        invertTree(root->left);
        invertTree(root->right);

        return root;
    }

101. 对称二叉树

力扣题目链接

思路:

要判断左右子树是否对称,递归函数返回值及参数列表为:

 bool CompareSubTree(TreeNode* left, TreeNode* right)

确定终止条件:

左右子树都为空,返回true

左右子树一个为空一个不为空,返回false

左右子树都不为空但是数值不同,返回false

确定递归逻辑:

先判断左子树,再判断右子树,根节点是左子树判断结果&&右子树判断结果

    bool CompareSubTree(TreeNode* leftnode, TreeNode* rightnode)
    {
        if(!leftnode && !rightnode) return true;
        else if(leftnode && !rightnode) return false;
        else if(!leftnode && rightnode) return false;
        else if(leftnode->val != rightnode->val) return false;

        // 错误写法:这里判断的是左子树是否是对称,右子树是否对称
        // bool left_is_symmetric = CompareSubTree(leftnode->left, leftnode->right);
        // bool right_is_symmetric = CompareSubTree(rightnode->left, rightnode->right);
        // return left_is_symmetric&&right_is_symmetric;

        // 正确写法:应该判断左子树和右子树是否镜像,即外侧是否相等,内侧是否相等
        bool outside_is_symmetric = CompareSubTree(leftnode->left, rightnode->right);
        bool inside_is_symmetric = CompareSubTree(leftnode->right, rightnode->left);
        return outside_is_symmetric&&inside_is_symmetric;

    }

    bool isSymmetric(TreeNode* root) {
        if(!root) return true;

        return CompareSubTree(root->left, root->right);
    }

104.二叉树的最大深度

力扣题目链接

思路:

本题可以使用前序(中左右),也可以使用后序遍历(左右中),使用前序求的就是深度,使用后序求的是高度。

    int maxDepth(TreeNode* root) {
        if(!root) return 0;
        int leftheight = maxDepth(root->left);
        int rightheight= maxDepth(root->right);
        int rootheight = 1 + max(leftheight, rightheight);
        return rootheight;
    }

111.二叉树的最小深度

力扣题目链接

思路:

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点,左右孩子都为空的节点才是叶子节点

错误思考:会出现上述问题

int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;

代码:

 int minDepth(TreeNode* root) {
        if(!root) return 0;

        // 后续遍历
        int left_height = minDepth(root->left);
        int right_heigt = minDepth(root->right);

        // 注意左右孩子一空一不空的的节点不是叶子节点,高度是非空的一侧+1
        int root_height = 0;
        if(root->left && !root->right)
        {
            root_height = left_height + 1;
        }
        else if(!root->left && root->right)
        {
            root_height = right_heigt+ 1;
        }
        else
        {
            root_height = min(left_height, right_heigt)+ 1;
        }

        return root_height;

    }

110.平衡二叉树

力扣题目链接

class Solution {
public:
    int TreeHeight(TreeNode* root)
    {
        if(!root) return 0;
        int left_tree = TreeHeight(root->left);
        int right_tree = TreeHeight(root->right);
        return 1+max(left_tree, right_tree);
    }

    bool isBalanced(TreeNode* root) {
        if(!root) return true;

        int left_tree = TreeHeight(root->left);
        int right_tree = TreeHeight(root->right);
        if(abs(left_tree - right_tree) > 1) return false;

        bool left_is_balanced = isBalanced(root->left);
        bool right_is_balanced = isBalanced(root->right);
        return left_is_balanced&&right_is_balanced;
    }
};

257. 二叉树的所有路径

力扣题目链接

class Solution {
public:
    vector<int> path; // 记录当前路径
    vector<string> res; // 记录全部路径

    void backtracing(TreeNode* root)
    {
        if(!root) return;

        // 叶子节点
        if(!root->left && !root->right)
        {
            string path_str;
            for(int i = 0; i  < path.size();++i)
            {
                path_str += to_string(path[i]);
                path_str += "->";
            }
            
            path_str += to_string(root->val);
            res.push_back(path_str);
        }

        path.push_back(root->val); // 中

        // 左
        if(root->left) 
        {
            backtracing(root->left);
            if(!path.empty()) path.pop_back(); // 回溯
        }

        // 右
        if(root->right)
        {
            backtracing(root->right);
            if(!path.empty()) path.pop_back(); // 回溯
        }
    }

    vector<string> binaryTreePaths(TreeNode* root) {
        backtracing(root);
        return res;
    }
};

404.左叶子之和

力扣题目链接

思路:

判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。

class Solution {
public:
    TreeNode* pre_node; // 记录前一个节点

     void traversal(TreeNode* cur_node, int& sum)
     {
        if(!cur_node) return;

        // 叶子节点,且是前一个节点的左节点
        if(!cur_node->left && !cur_node->right && pre_node &&pre_node->left == cur_node)
            sum += cur_node->val;

        pre_node = cur_node;
        traversal(cur_node->left, sum);
        traversal(cur_node->right, sum);
     }

    int sumOfLeftLeaves(TreeNode* root) {
        int res = 0;
        traversal(root, res);
        return res;
    }
};

222.完全二叉树的节点个数

力扣题目链接

class Solution {
public:
    int countNodes(TreeNode* root) {
        if(!root) return 0;
        int left_num = 0;
        if(root->left) left_num = countNodes(root->left);
        int right_num = 0;
        if(root->right) right_num = countNodes(root->right);
        return left_num + right_num + 1;
    }
};

513.找树左下角的值

力扣题目链接

思路:

class Solution {
public:
    int maxDepth = INT_MIN;
    int result;
    void traversal(TreeNode* root, int depth) {
        if (root->left == NULL && root->right == NULL) {
            if (depth > maxDepth) {
                maxDepth = depth;
                result = root->val;
            }
            return;
        }
        if (root->left) {
            depth++;
            traversal(root->left, depth);
            depth--; // 回溯
        }
        if (root->right) {
            depth++;
            traversal(root->right, depth);
            depth--; // 回溯
        }
        return;
    }
    int findBottomLeftValue(TreeNode* root) {
        traversal(root, 0);
        return result;
    }
};

112. 路径总和

力扣题目链接

class Solution {
public:
    int path = 0;
    bool MyTraversal(TreeNode* root, int targetSum)
    {
        if(!root) return false;
        
        if(!root->left && !root->right)
        {
            if(path == targetSum) return true;
            else return false;
        }

        if(root->left)
        {
            path += root->left->val;
            if(MyTraversal(root->left, targetSum)) return true;
            path -= root->left->val;
        }

        if(root->right)
        {
            path += root->right->val;
            if(MyTraversal(root->right, targetSum)) return true;
            path -= root->right->val;
        }

        return false;
    }

    bool hasPathSum(TreeNode* root, int targetSum) {
        if(!root) return false;
        path+= root->val;
        return MyTraversal(root, targetSum);
    }
};

11

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值