面试150——第五周

目录

翻转二叉树

对称二叉树

从前序和中序遍历序列构建二叉树

从中序与后续遍历序列构建二叉树

填充每个节点的下一个右侧节点指针

二叉树展开为链表

路径总和

求根节点到叶子节点路径之和

二叉树中的最大路径和

完全二叉树的节点个数

二叉搜索树迭代器

二叉树的最近公共祖先

二叉树的右视图

二叉树的层平均值

二叉树的层序遍历

二叉树的锯齿形层序遍历

二叉搜索树的最小绝对差

二叉搜索树中第k小的元素

验证二叉搜索树

岛屿数量


翻转二叉树

解法:递归

(后序遍历,如果前序遍历要保留节点交换才行,也就是dfs写法)

最后最左子树的叶子节点left和最右子树的叶子结点right

当前节点的左右节点分别是right,left

返回当前节点

cpp 复制代码
class Solution
{
public:
    void dfs(TreeNode *root)
    {
        if (root == nullptr)
            return;
        TreeNode *left = root->left;
        TreeNode *right = root->right;
        root->left = right;
        root->right = left;
        dfs(root->left);
        dfs(root->right);
    }
    TreeNode *invertTree(TreeNode *root)
    {
        // dfs(root);
        // return root;
        if (root == nullptr)
            return root;
        TreeNode *left = invertTree(root->left);
        TreeNode *right = invertTree(root->right);
        root->left = right;
        root->right = left;
        return root;
    }
};

对称二叉树

解法:递归

传入左子树和右子树

如果当前节点都为空,返回 true;

有一个节点为空或者val值不相等,返回 false;

左子树的左节点与右子树的右节点比较,左子树的右节点与右子树的左节点比较

cpp 复制代码
class Solution
{
public:
    bool dfs(TreeNode *root1, TreeNode *root2)
    {
        if (root1 == nullptr && root2 == nullptr)
            return true;
        if ((root1 == nullptr || root2 == nullptr) ||
            root1->val != root2->val)
            return false;
        return dfs(root1->left, root2->right) && dfs(root1->right, root2->left);
    }
    bool isSymmetric(TreeNode *root)
    {
        return dfs(root->left, root->right);
    }
};

从前序和中序遍历序列构建二叉树

解法:递归

根据前序遍历第一个节点为根节点为出发点

在中序遍历中找到该节点,就可以把区间分为左子树区间和右子树区间

同时得到左子树节点个数,也可以把前序遍历区间分为左子树区间和右子树区间,这样就可以通过左右子树区间递归构建左右子树,从而构建出完整二叉树出来

cpp 复制代码
class Solution
{
public:
    TreeNode *dfs(vector<int> &root1, int begin1, int end1, vector<int> &root2, int begin2, int end2)
    {
        if (begin1 > end1)
            return nullptr;
        // if(begin2>end2) return nullptr;
        // if(begin1 == end1) return new TreeNode(root1[begin1]);
        // if(begin2 == end2) return new TreeNode(root2[begin2]);
        int val = root1[begin1];
        TreeNode *root = new TreeNode(val);
        for (int i = begin2; i <= end2; i++)
        {
            if (root2[i] == val)
            {
                int leftNodeCnt = i - 1 - begin2 + 1;
                root->left = dfs(root1, begin1 + 1, begin1 + 1 + leftNodeCnt - 1, root2, begin2, i - 1);
                root->right = dfs(root1, begin1 + leftNodeCnt + 1, end1, root2, i + 1, end2);
            }
        }
        return root;
    }
    TreeNode *buildTree(vector<int> &preorder, vector<int> &inorder)
    {
        return dfs(preorder, 0, preorder.size() - 1, inorder, 0, inorder.size() - 1);
    }
};

从中序与后续遍历序列构建二叉树

上题思路一样的

cpp 复制代码
class Solution
{
public:
    TreeNode *dfs(vector<int> &root1, int begin1, int end1, vector<int> &root2, int begin2, int end2)
    {
        if (begin1 > end1)
            return nullptr;
        int rootVal = root2[end2];
        TreeNode *root = new TreeNode(rootVal);
        for (int i = begin1; i <= end1; i++)
        {
            if (root1[i] == rootVal)
            {
                int leftCnt = i - 1 - begin1 + 1;
                root->left = dfs(root1, begin1, i - 1, root2, begin2, begin2 + leftCnt - 1);
                root->right = dfs(root1, i + 1, end1, root2, begin2 + leftCnt, end2 - 1);
                break;
            }
        }
        return root;
    }
    TreeNode *buildTree(vector<int> &inorder, vector<int> &postorder)
    {
        return dfs(inorder, 0, inorder.size() - 1, postorder, 0, postorder.size() - 1);
    }
};

填充每个节点的下一个右侧节点指针

解法1:递归

使用数组prev来保存每一层的第一个节点 -> prev.size() == 当前深度depth

遇到其它节点node时,就把 prev[depth] 的next进行更新,prev[depth] 位置更新为 node

然后不断递归左右子树....

cpp 复制代码
class Solution
{
public:
    vector<Node *> depthOne;
    void dfs(Node *root, int depth)
    {
        if (root == nullptr)
            return;
        if (depthOne.size() == depth)
        {
            depthOne.push_back(root);
        }
        else
        {
            depthOne[depth]->next = root;
            depthOne[depth] = root;
        }
        dfs(root->left, depth + 1);
        dfs(root->right, depth + 1);
    }
    Node *connect(Node *root)
    {
        dfs(root, 0);
        return root;
    }
};

解法2:bfs

使用二叉树的层序遍历,来把每层的next进行设置

cpp 复制代码
class Solution
{
public:
    Node *connect(Node *root)
    {
        if (root == nullptr)
            return root;
        queue<Node *> q;
        q.push(root);
        Node head;

        while (!q.empty())
        {
            Node *tail = &head;
            int sz = q.size();
            while (sz--)
            {
                Node *node = q.front();
                q.pop();
                if (node->left)
                {
                    tail->next = node->left;
                    tail = node->left;
                    q.push(node->left);
                }
                if (node->right)
                {
                    tail->next = node->right;
                    tail = node->right;
                    q.push(node->right);
                }
            }
            tail->next = nullptr;
        }
        return root;
    }
};

解法3:模拟

使用链表模拟层序遍历

  • 使用哨兵位头节点head的方法储存下一层链表节点
  • 初始化 cur,指向 root
  • 循环遍历每层节点:
  • 循环遍历cur是否为空:把 cur 左节点和右节点(不为空)加入到head后面,更新 cur 为cur->next(这里可以直接更新,因为循环当层节点之前的next已经初始化好了
  • 为空代表当前层遍历完成,更新cur为下一层的头节点,也就是head->next,这里还要顺便把head->next 置空(因为循环遍历到倒数第二层节点时,最后一层节点是更新完了的,不置空会出现死循环
cpp 复制代码
class Solution
{
public:
    Node *connect(Node *root)
    {
        if (root == nullptr)
            return root;

        Node head;
        Node *cur = root;
        while (cur)
        {
            // tail链接的是下一层节点的next
            Node *tail = &head;
            while (cur)
            {
                if (cur->left)
                {
                    tail->next = cur->left;
                    tail = cur->left;
                }
                if (cur->right)
                {
                    tail->next = cur->right;
                    tail = cur->right;
                }
                cur = cur->next; // 当前层的每个节点的next已经连接好了的
            }
            // 更新下一层的链表头节点
            cur = head.next;
            // 不更新最后一层会死循环
            head.next = nullptr;
        }
        return root;
    }
};

二叉树展开为链表

解法1:头插法

借助变量记录当前的尾节点,与当前节点进行头插,最后更新尾节点

cpp 复制代码
class Solution
{
public:
    TreeNode *dfs(TreeNode *root)
    {
        if (root == nullptr)
            return root;
        TreeNode *leftTail = dfs(root->left);
        TreeNode *retTail = dfs(root->right);
        if (leftTail)
            leftTail->right = root->right;
        if (root->left)
            root->right = root->left;
        root->left = nullptr;
        if (retTail)
            return retTail;
        if (leftTail)
            return leftTail;
        return root;
    }
    void flatten(TreeNode *root)
    {
        dfs(root);
    }
};

解法2:递归

比如例子1:

从root开始递归左子树,左子数按照 2->3->4 的顺序链接完成后,返回leftTail = 4;

递归右子树也是同理,返回retTail = 6(注意这里返回6是因为:可能root不是当前根节点,递归完成后要向上返回)

按照三步:节点4 链接 root 右节点,root右节点 链接 root 左节点,root 左节点为空

返回时:retTail 不为空返回;为空考虑 leftTail:leftTail 不为空返回;都为空返回 root

cpp 复制代码
class Solution
{
public:
    TreeNode *dfs(TreeNode *root)
    {
        if (root == nullptr)
            return root;
        TreeNode *leftTail = dfs(root->left);
        TreeNode *retTail = dfs(root->right);
        if (leftTail)
            leftTail->right = root->right;
        // 如果左节点不存在,root有节点保持不变即可
        if (root->left)
            root->right = root->left;
        root->left = nullptr;
        // 右子树最右节点不为空时返回
        // 为空考虑左子树的最右节点
        // 否则就返回根节点root
        if (retTail)
            return retTail;
        if (leftTail)
            return leftTail;
        return root;
    }
    void flatten(TreeNode *root)
    {
        dfs(root);
    }
};

路径总和

解法:递归

二叉树的前序遍历,在当前根节点上判断:

当前 targetSum 减去根节点的值是否等于0 且 当前节点是不是叶子节点,是就找到了答案,返回true

在左子树和右子树中找任意一种答案就可以返回true了

边界:当节点为空时返回 false(千万不要因为 targetSum 小于0时返回 false,因为 targetSum 可以是 负数啊)

cpp 复制代码
class Solution
{
public:
    bool hasPathSum(TreeNode *root, int targetSum)
    {
        if (root == nullptr)
            return false;
        if ((targetSum - root->val) == 0 && root->left == nullptr && root->right == nullptr)
            return true;
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};

求根节点到叶子节点路径之和

解法:递归

依旧前序遍历,dfs 返回当前叶子节点的值,tmp 代表当前节点对应的值

当遍历到叶子节点时,tmp * 10 + 叶子节点的值 就是当前叶子节点的值,结果返回

否则就要递归左子树与右子树,将它们的叶子节点值之和进行返回

cpp 复制代码
class Solution
{
public:
    // int sum = 0;
    int dfs(TreeNode *root, int tmp)
    {
        if (root == nullptr)
        {
            return 0;
        }
        if (root->left == nullptr && root->right == nullptr)
        {
            return (tmp * 10 + root->val);
        }
        return dfs(root->left, tmp * 10 + root->val) +
               dfs(root->right, tmp * 10 + root->val);
    }
    int sumNumbers(TreeNode *root)
    {
        return dfs(root, 0);
    }
};

二叉树中的最大路径和

解法:递归

dfs 返回的单链(没有拐弯的直径)最大路径和,与 0 取max(防止全是负数不选的情况)

使用变量 ret 枚举二叉树中的最大路径和,也就是左右子树单链加上当前节点的值

cpp 复制代码
class Solution
{
public:
    int ret = INT_MIN;
    int dfs(TreeNode *root)
    {
        if (root == nullptr)
            return 0;
        // left 为 0 说明左子树不选
        int left = max(dfs(root->left), 0);
        int right = max(dfs(root->right), 0);
        ret = max(ret, left + root->val + right);
        return max(left, right) + root->val;
    }
    int maxPathSum(TreeNode *root)
    {
        dfs(root);
        return ret;
    }
};

完全二叉树的节点个数

解法:递归

二叉树的前序遍历枚举节点个数

cpp 复制代码
class Solution {
public:
    int countNodes(TreeNode* root) {
        if(root == nullptr) return 0;
        return countNodes(root->left) + countNodes(root->right) + 1;
    }
};

二叉搜索树迭代器

解法:递归

中序遍历递归后的结果保存在栈/队列中,依次取出即可

cpp 复制代码
class BSTIterator
{
public:
    queue<TreeNode *> q;
    void initQueue(TreeNode *root)
    {
        if (root == nullptr)
            return;
        initQueue(root->left);
        q.push(root);
        initQueue(root->right);
    }
    BSTIterator(TreeNode *root)
    {
        initQueue(root);
    }

    int next()
    {
        int tmp = q.front()->val;
        q.pop();
        return tmp;
    }

    bool hasNext()
    {
        if (q.size())
            return true;
        return false;
    }
};

二叉树的最近公共祖先

cpp 复制代码
class Solution
{
public:
    TreeNode *lowestCommonAncestor(TreeNode *root, TreeNode *p, TreeNode *q)
    {
        if (root == nullptr || root == p || root == q)
        {
            return root;
        }
        TreeNode *lefFind = lowestCommonAncestor(root->left, p, q);
        TreeNode *rightFind = lowestCommonAncestor(root->right, p, q);
        // p 和 q 各自分布在左右子树
        if ((lefFind != nullptr) && (rightFind != nullptr))
        {
            return root;
        }
        // 右子树有结果返回,即使当前只有一个节点
        if (lefFind == nullptr)
            return rightFind;
        if (rightFind == nullptr)
            return lefFind;
        return nullptr;
    }
};

二叉树的右视图

解法:bfs

使用bfs:队列 + 两层循环的方式作一次层序遍历,每次外层循环中队列的最后一个节点就是答案,收集后进行返回

cpp 复制代码
class Solution
{
public:
    vector<int> rightSideView(TreeNode *root)
    {
        vector<int> ret;
        if (root == nullptr)
            return ret;
        queue<TreeNode *> q;
        q.push(root);
        while (!q.empty())
        {
            int sz = q.size();
            while (sz--)
            {
                TreeNode *top = q.front();
                q.pop();
                if (sz == 0)
                {
                    ret.push_back(top->val);
                }
                if (top->left)
                    q.push(top->left);
                if (top->right)
                    q.push(top->right);
            }
        }
        return ret;
    }
};

二叉树的层平均值

解法:bfs

层序遍历求每层(队列)平均值

cpp 复制代码
class Solution
{
public:
    vector<double> averageOfLevels(TreeNode *root)
    {
        vector<double> ret;
        if (root == nullptr)
            return ret;
        queue<TreeNode *> q;
        q.push(root);
        while (!q.empty())
        {
            int sz = q.size();
            double size = q.size(), sum = 0;
            while (sz--)
            {
                TreeNode *top = q.front();
                q.pop();
                sum += top->val;
                if (sz == 0)
                {
                    ret.push_back(sum / size);
                }
                if (top->left)
                    q.push(top->left);
                if (top->right)
                    q.push(top->right);
            }
        }
        return ret;
    }
};

二叉树的层序遍历

解法:dfs

演都不演了,直接告诉你要求层序遍历

cpp 复制代码
class Solution
{
public:
    vector<vector<int>> levelOrder(TreeNode *root)
    {
        vector<vector<int>> ret;
        if (root == nullptr)
            return ret;
        queue<TreeNode *> q;
        q.push(root);
        while (!q.empty())
        {
            int sz = q.size();
            vector<int> tmp;
            while (sz--)
            {
                TreeNode *top = q.front();
                q.pop();
                tmp.push_back(top->val);
                if (top->left)
                    q.push(top->left);
                if (top->right)
                    q.push(top->right);
            }
            ret.push_back(tmp);
        }
        return ret;
    }
};

二叉树的锯齿形层序遍历

解法:bfs

使用 tmp 来进行标记,如果当前需要从右往左收集节点,tmp为真,将收集的节点进行反转后保存

cpp 复制代码
class Solution
{
public:
    vector<vector<int>> zigzagLevelOrder(TreeNode *root)
    {
        vector<vector<int>> ret;
        if (root == nullptr)
            return ret;
        queue<TreeNode *> q;
        q.push(root);
        bool tmp = false;
        while (!q.empty())
        {
            int sz = q.size();
            vector<int> ret1;
            while (sz--)
            {
                TreeNode *top = q.front();
                q.pop();
                ret1.push_back(top->val);
                if (top->left)
                    q.push(top->left);
                if (top->right)
                    q.push(top->right);
            }
            if (tmp)
            {
                reverse(ret1.begin(), ret1.end());
            }
            ret.push_back(ret1);
            tmp = !tmp;
        }
        return ret;
    }
};

二叉搜索树的最小绝对差

解法:dfs

利用二叉搜索树中序遍历的有序性进行递归,收集每两个数之间的差值的最小值返回

cpp 复制代码
class Solution
{
public:
    int prev = -1, ret = INT_MAX;
    void dfs(TreeNode *root)
    {
        if (root == nullptr)
            return;
        dfs(root->left);
        if (prev != -1)
        {
            ret = min(ret, abs(root->val - prev));
        }
        prev = root->val;
        dfs(root->right);
    }
    int getMinimumDifference(TreeNode *root)
    {
        dfs(root);
        return ret;
    }
};

二叉搜索树中第k小的元素

解法:递归

使用变量记录中序遍历的第几个,当 cnt == 0时使用 ret 记录答案,递归完成之后返回 ret;

但也可以不使用 ret,让 dfs 返回值作为答案返回:

当 root 为空时,返回 -1 (题目明确val为正数)

递归左子树和右子树保存结果,当其中一方返回结果不为-1时直接返回

当cnt为0时也是直接返回

cpp 复制代码
class Solution
{
public:
    int ret, cnt;
    void dfs(TreeNode *root)
    {
        if (root == nullptr)
            return;
        dfs(root->left);
        if ((--cnt) == 0)
        {
            ret = root->val;
        }
        dfs(root->right);
    }
    int kthSmallest(TreeNode *root, int k)
    {
        cnt = k;
        dfs(root);
        return ret;
    }
};

class Solution
{
public:
    int cnt;
    int dfs(TreeNode *root)
    {
        if (root == nullptr)
        {
            return -1;
        }
        int left = dfs(root->left);
        if (left != -1)
        {
            return left;
        }
        cnt -= 1;
        if(cnt == 0)
        {
            return root->val;
        }
        // 不管有没有结果都是返回右子树的返回值
        return dfs(root->right);
    }
    int kthSmallest(TreeNode *root, int k)
    {
        cnt = k;
        return dfs(root);
    }
};

验证二叉搜索树

解法:递归

利用中序遍历有序特性,使用变量标记上一个节点的值进行比较,中序遍历过程中都是 prev < root->val,则证明是一颗二叉搜索树

cpp 复制代码
class Solution
{
public:
    long long prevVal = LONG_MIN;
    bool isValidBST(TreeNode *root)
    {
        if (root == nullptr)
        {
            return true;
        }
        bool leftBool = isValidBST(root->left);
        if (!leftBool)
        {
            return false;
        }
        if (prevVal >= root->val)
        {
            return false;
        }
        prevVal = root->val;
        return isValidBST(root->right);
    }
};

岛屿数量

解法:递归

遍历岛屿,找到'1'且没有标记过,从该位置开始上下左右进行递归遍历,把遇到的所有'1'使用二维数组进行标记...递归的次数就是岛屿的数量

cpp 复制代码
class Solution {
public:
    bool vis[301][301] = {false};
    int dx[4] = {0,0,1,-1};
    int dy[4] = {1,-1,0,0};
    int n,m,ret=0;
    void dfs(vector<vector<char>>& grid, int i, int j)
    {
        vis[i][j] = true;
        for(int k = 0; k < 4; k++)
        {
            int x = dx[k] + i,y = dy[k] + j;
            if(x >=0 && x < n && y >=0 && y < m && !vis[x][y] && grid[x][y] == '1')
            {
                dfs(grid, x, y);
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        n = grid.size(),m = grid[0].size();
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < m; j++)
            {
                if(grid[i][j] == '1' && !vis[i][j])
                {
                    ret++;
                    dfs(grid, i, j);
                }
            }
        }
        return ret;
    }
};

以上便是全部内容,有问题欢迎在评论区指正,感谢观看!

相关推荐
晚霞的不甘2 小时前
Flutter for OpenHarmony 可视化教学:A* 寻路算法的交互式演示
人工智能·算法·flutter·架构·开源·音视频
望舒5133 小时前
代码随想录day25,回溯算法part4
java·数据结构·算法·leetcode
C++ 老炮儿的技术栈3 小时前
Qt 编写 TcpClient 程序 详细步骤
c语言·开发语言·数据库·c++·qt·算法
KYGALYX3 小时前
逻辑回归详解
算法·机器学习·逻辑回归
铉铉这波能秀3 小时前
LeetCode Hot100数据结构背景知识之集合(Set)Python2026新版
数据结构·python·算法·leetcode·哈希算法
踢足球09293 小时前
寒假打卡:2026-2-8
数据结构·算法
IT猿手3 小时前
基于强化学习的多算子差分进化路径规划算法QSMODE的机器人路径规划问题研究,提供MATLAB代码
算法·matlab·机器人
千逐-沐风3 小时前
SMU-ACM2026冬训周报3rd
算法
铉铉这波能秀4 小时前
LeetCode Hot100数据结构背景知识之元组(Tuple)Python2026新版
数据结构·python·算法·leetcode·元组·tuple