代码随想录Day18-二叉树:力扣第117m、257e、404e、513m、112e题

117m. 填充每个节点的下一个右侧节点指针 II

题目链接
代码随想录文章讲解链接

方法一:BFS

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() : val(0), left(NULL), right(NULL), next(NULL) {}

    Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {}

    Node(int _val, Node* _left, Node* _right, Node* _next)
        : val(_val), left(_left), right(_right), next(_next) {}
};
*/
class Solution {
public:
    Node* connect(Node* root) {
        queue<Node*> que;

        if (root == NULL) return root;
        que.push(root);
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                Node* node = que.front();
                que.pop();
                if (i < size - 1) node->next = que.front();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return root;
    }
};

方法二:利用next指针逐层遍历

用时:29m27s

思路

利用当前层已经通过next指针相连的性质,将下一层的节点连起来。
设置一个虚拟头结点dummy,用于指向下一层最左端的节点,初始化时让其指向NULL。
在当前层中,从左往右寻找下一层的子节点,将其逐个相连。
下一层的节点相连完成后,下一层的节点变为当前层节点,然后继续往下处理。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
C++代码
class Solution {
public:
    Node* connect(Node* root) {
        Node* cur = root;
        Node* dummy = new Node();
        while (cur != NULL) {
            dummy->next = NULL;  // 用于指向下一层最左边的节点
            Node* pre = dummy;
            while (cur != NULL) {
                if (cur->left) {
                    pre->next = cur->left;
                    pre = pre->next;
                }
                if (cur->right) {
                    pre->next = cur->right;
                    pre = pre->next;
                }
                cur = cur->next;
            }
            cur = dummy->next;  // 进入下一层,cur更新为下一层最左边的节点
        }
        return root;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


257e. 二叉树的所有路径

题目链接
代码随想录文章讲解链接

方法一:DFS+回溯

思路

在递归的同时维护一个字符串str
递归的逻辑:

  1. 首先向str中添加当前节点的数值。
  2. 如果当前节点左右节点都为空,说明该节点是叶子节点,那么str中的内容就是从根节点到达当前叶子节点的路径,将str添加到答案数组res中。
  3. 如果左右节点不全为空,说明此时还不是叶子节点,需要继续递归不为空的子节点。
  4. 最后进行回溯操作,将当前节点的路径从str中删除,因为回到上一层递归后,此时的路径已经不包括当前的节点了。
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        string str;
        traversal(root, res, str);
        return res;
    }

    void traversal(TreeNode* node, vector<string>& res, string& str) {
        // 路径添加当前节点
        if (!str.empty()) str += "->";
        str += to_string(node->val);

        // 若是叶子节点则记录路径,否则左右子节点递归
        if (node->left == nullptr && node->right == nullptr) res.push_back(str);
        else {
            if (node->left) traversal(node->left, res, str);
            if (node->right) traversal(node->right, res, str);
        }

        // 回溯
        int pos = str.rfind("->");
        if (pos >= 0) str.erase(pos, str.size() - pos);
    }
};

方法二:BFS

用时:6m36s

思路

在二叉树BFS的基础上,额外创建一个队列用于存放路径。
nodeQue用于存放节点,pathQue用于存放路径,且对于两个队列中同一位置的元素n_ip_ip_i表示从根节点到节点n_i的路径。
循环内部:
若nodeQue队头的节点是叶子节点,则pathQue队头的路径就是根节点到该叶子节点的路径,将该路径保存至结果数组res中。
若nodeQue队头的节点不是叶子节点,则将其左右节点入队nodeQue,并且pathQue也入队相应的路径,即为(当前路径)->(子节点数值)。

  • 时间复杂度: O ( n 2 ) O(n^2) O(n2),每次循环中,拷贝字符串的时间复杂的为 O ( n ) O(n) O(n)
  • 空间复杂度: O ( n 2 ) O(n^2) O(n2),路径的长度随着树的深度越深而越长,nodeQue的空间复杂度为 O ( n ) O(n) O(n),pathQue的空间复杂度为 O ( n 2 ) O(n^2) O(n2)
C++代码
class Solution {
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        vector<string> res;
        queue<TreeNode*> nodeQue;  // 存放节点的队列
        queue<string> pathQue;  // 存放字符串的队列

        // BFS
        nodeQue.push(root);
        pathQue.push(to_string(root->val));
        while (!nodeQue.empty()) {
            TreeNode* node = nodeQue.front();
            string path = pathQue.front();
            nodeQue.pop();
            pathQue.pop();
            if (node->left == nullptr && node->right == nullptr) res.push_back(path);  // 当前节点为叶子节点,将路径添加到答案数组中
            else {
                if (node->left) {
                    nodeQue.push(node->left);
                    pathQue.push(path + "->" + to_string(node->left->val));  // 在当前路径的基础上,增加左子节点
                }
                if (node->right) {
                    nodeQue.push(node->right);
                    pathQue.push(path + "->" + to_string(node->right->val));  // 在当前路径的基础上,增加右子节点
                }
            }
        }
        return res;
    }
};

看完讲解的思考

回溯都来了,这是简单题?

代码实现遇到的问题

无。


404e. 左叶子之和

题目链接
代码随想录文章讲解链接

方法一:DFS

思路

用curSum记录当前左叶子节点之和。
对于当前节点,若左节点存在,且左节点是叶子节点,则将curSum加上左节点的数值;否则左节点继续递归。然后再对右节点递归。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        int res = 0;
        _sumOfLeftLeaves(root, res);
        return res;
    }

    void _sumOfLeftLeaves(TreeNode* node, int& curSum) {
        if (node->left) {
            if (node->left->left == nullptr && node->left->right == nullptr) curSum += node->left->val;
            else _sumOfLeftLeaves(node->left, curSum);
        }
        if (node->right) _sumOfLeftLeaves(node->right, curSum);
    }
};

方法二:BFS

用时:5m3s

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int sumOfLeftLeaves(TreeNode* root) {
        queue<TreeNode*> que;
        int res = 0;

        que.push(root);
        while (!que.empty()) {
            TreeNode* node = que.front();
            que.pop();
            if (node->left) {
                if (node->left->left == nullptr && node->left->right == nullptr) res += node->left->val;
                else que.push(node->left);
            }
            if (node->right) que.push(node->right);
        }
        return res;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


513. 找树左下角的值

题目链接
代码随想录文章讲解链接

方法一:BFS

用时:5m10s

思路
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    int findBottomLeftValue(TreeNode* root) {
        queue<TreeNode*> que;
        int res;

        que.push(root);
        while (!que.empty()) {
            res = que.front()->val;
            int size = que.size();
            for (int i = 0; i < size; ++i) {
                TreeNode* node = que.front();
                que.pop();
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return res;
    }
};

方法二:DFS

思路

使用curHeight来记录最高的节点的高度,使用curVal来记录最高高度时最左边节点的值。用height来记录当前节点的高度,每一层递归都加一。
如果当前节点的高度大于最大高度,则更新curHeightcurVal

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    void dfs(TreeNode *root, int height, int &curVal, int &curHeight) {
        if (root == nullptr) {
            return;
        }
        height++;
        dfs(root->left, height, curVal, curHeight);
        dfs(root->right, height, curVal, curHeight);
        // 如果当前节点的高度大于最大高度,则更新curHeight和curVal
        // 值的比较用“大于”,这样只有第一达到最大高度时才会被记录,而我们事先遍历左节点,所以记录的一定是最大高度最左边的节点的值
        if (height > curHeight) {
            curHeight = height;
            curVal = root->val;
        }
    }

    int findBottomLeftValue(TreeNode* root) {
        int curVal, curHeight = 0;
        dfs(root, 0, curVal, curHeight);
        return curVal;
    }
};

看完讲解的思考

代码实现遇到的问题


112e. 路径总和

题目链接
代码随想录文章讲解链接

方法一:DFS递归法(更改当前和)

用时:18m38s

思路

用curSum来记录根节点到当前节点的路径和。

  1. 首先更新curSum,让curSum加上当前节点的值。
  2. 如果当前节点是叶子节点,则返回路径和是否等于目标值。如果不是叶子节点,则继续递归。
  3. 如果左节点存在,并且存在经过左节点的路径的路径和等于目标值,则返回true。
    如果右节点存在,并且存在经过右节点的路径的路径和等于目标值,则返回true。
  4. 如果经过左右节点的路径的路径和都不等于目标值,则返回false。
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        return root == nullptr ? false : dfs(root, 0, targetSum);
    }

    bool dfs(TreeNode* node, int curSum, int targetSum) {
    	// 更新根节点到当前节点的路径和
        curSum += node->val;
        // 如果当前节点是叶子节点,则返回路径和是否等于目标值
        if (node->left == nullptr && node->right == nullptr) return curSum == targetSum;
        // 如果左或右节点存在,并且存在路径和等于目标值,则返回true,否则返回false
        if (node->left && dfs(node->left, curSum, targetSum)) return true;
        if (node->right && dfs(node->right, curSum, targetSum)) return true;
        return false;
    }
};

方法二:DFS递归法(更改目标值)

思路

方法一是在DFS中,不断更新当前的路径和,当到达叶子节点后判断当前和是否等于目标值。
可以换一个角度来使用DFS解本题,每次递归更新目标值,每向下递归一个节点,则目标值减去当前结点的值,到达叶子节点时,若叶子结点的值与剩余的目标值相同,则说明根节点到当前的叶子结点的路径和与初始目标值相等。
递归逻辑:

  1. 如果是空结点,返回false。
  2. 如果是叶子节点,并且叶子结点的值等于剩余的目标值,则返回true。
  3. 不是叶子节点的话,则向左右节点递归,此时目标值需要减去当前结点的值。
  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr) return false;
        if (root->left == nullptr && root->right == nullptr) return root->val == targetSum;
        return hasPathSum(root->left, targetSum - root->val) || hasPathSum(root->right, targetSum - root->val);
    }
};

方法三:DFS迭代法

思路

用栈实现DFS,栈存储的元素是pair,pair记录着当前节点以及根节点到当前节点的路径和。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr) return false;
        stack<pair<TreeNode*, int>> st;

        st.push(pair<TreeNode*, int>(root, root->val));
        while (!st.empty()) {
            TreeNode* node = st.top().first;
            int curSum = st.top().second;
            st.pop();
            if (node->left == nullptr && node->right == nullptr && curSum == targetSum) return true;
            if (node->left) st.push(pair<TreeNode*, int>(node->left, curSum + node->left->val));
            if (node->right) st.push(pair<TreeNode*, int>(node->right, curSum + node->right->val));
        }
        return false;
    }
};

方法四:BFS

时间:12m51s

思路

在BFS的基础上,加多一个队列用于记录路径和。

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n)
C++代码
class Solution {
public:
    bool hasPathSum(TreeNode* root, int targetSum) {
        if (root == nullptr) return false;
        queue<TreeNode*> nodeQue;
        queue<int> sumQue;

        nodeQue.push(root);
        sumQue.push(root->val);
        while (!nodeQue.empty()) {
            TreeNode* node = nodeQue.front();
            int curSum = sumQue.front();
            nodeQue.pop();
            sumQue.pop();
            if (node->left == nullptr && node->right == nullptr && curSum == targetSum) return true;
            if (node->left) {
                nodeQue.push(node->left);
                sumQue.push(curSum + node->left->val);
            }
            if (node->right) {
                nodeQue.push(node->right);
                sumQue.push(curSum + node->right->val);
            }
        }
        return false;
    }
};

看完讲解的思考

无。

代码实现遇到的问题

无。


最后的碎碎念

感觉二叉树的题目搞来搞去基本都是在DFS和BFS的基础上变动。

你可能感兴趣的:(代码随想录,leetcode,算法,职场和发展,c++,数据结构)