LeetCode 91 - 100

LeetCode 91. 解码方法

分析

有点类似于编辑距离问题, 所以考虑编辑距离合法的情况, 用dp去分析
合法的情况是指12可以看成了两个数12或者12
注意,
f[i] 既可以从f[i - 1]转化过来, 也可以从f[i - 2]转化过来
还需要注意什么时候能从f[i - 1]和f[i - 2]转化过来
在这里插入图片描述

代码

class Solution {
public:
    int numDecodings(string s) {
        int n = s.size();
        s = ' ' + s;
        vector<int> f(n + 1);
        f[0] = 1;
        for (int i = 1; i <= n; i ++ ){
            if (s[i] >= '1' && s[i] <= '9')  f[i] += f[i - 1];
            // 当可以从f[i - 2]转化过来的时候, i > 1
            if  (i > 1){
                int t = (s[i - 1] - '0') * 10 + (s[i] - '0');
                if (t >= 10 && t <= 26) f[i] += f[i - 2];
            }
        }
        return f[n];
    }
};

LeetCode 92. 反转链表 II

分析

直接画图, 即可, 注意最后一步链表节点指向的时候, 先做后面节点的指向, 再做前面节点的指向
在这里插入图片描述

代码

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        auto dummy = new ListNode(-1);
        dummy->next = head;

        auto a = dummy;
        for (int i = 0; i < m - 1; i ++ ) a = a->next;
        auto b = a->next, c = b->next;
        for (int i = 0; i < n - m; i ++ ){
            auto t = c->next;
            c->next = b;
            b = c, c = t;
        }
        a->next->next = c;
        a->next = b;
        return dummy->next;
    }
};

LeetCode 93. 复原IP地址

分析

直接用递归去解决, 注意递归中要传入参数, 遍历到第几个位置u, 以及当前完成了分隔.的数目k, 还需要考虑 前导0 , 以及已经有4个分隔的剪枝

代码

class Solution {
public:
    vector<string> ans;
    vector<string> restoreIpAddresses(string s) {
        dfs(s, 0, 0, "");// 2. 表示遍历到第几个位置, 3.表示完成了几个分隔 4. 当前已经完成的字符串
        return ans;
    }

    void dfs(string&s, int u, int k, string path){
        if (u == s.size()){
            if (k == 4){
                path.pop_back();
                ans.push_back(path);
            }
            return;
        }
        if (k == 4) return ; // 剪枝, 已经分隔4个了, 直接退出 , 比如11111111111111111111 就会超时
        for (int i = u, t = 0; i < s.size(); i ++ ){
            if (i > u && s[u] == '0') break; // s[u] == '0' 前导0 说明此分支不合法
            // i > u是因为 0000 可以转化为0.0.0.0合法, 所以i == u 的时候 s[u] = 0是合法的.
            t = t * 10 + s[i] - '0';
            if (t <= 255) dfs(s, i + 1, k + 1, path + to_string(t) + '.');
            else break;
        }
    }
};

LeetCode 94. 二叉树的中序遍历

分析

先看前序遍历
再见自己写的这篇文章模拟一遍

每次当前节点不空, 则遍历左子树
while循环借宿
然后回到到根节点, 即栈顶元素, 遍历根节点
然后到右子树看看

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<int> inorderTraversal(TreeNode* root) {
        vector<int> res;
        stack<TreeNode*> stk;
        while (root || stk.size()){
            while (root){ // 当前节点不空, 先遍历左子树
                stk.push(root);
                root = root->left;
            }
            // 左子树遍历完成, 回溯到根节点, 即栈顶元素就是根节点
            root = stk.top(); stk.pop();
            res.push_back(root->val); // 遍历根节点
            root = root->right; // 到右子树看看
        }
        return res;
    }
};

LeetCode 95. 不同的二叉搜索树 II

分析

在1 ~ n中递归的建树, 由于1 ~ n 已经是排好序的数列, 所以 任何一个片段[l, r]都是一个二叉搜索树, 因此递归建立左子树 和递归建立右子树即可, 每次递归返回已经建好的树的根节点
dfs(l, r)表示在区间[l, r]之间建好树
因为任何的左子树情况 都可以和任何右子树的情况 合并称为一种情况, 因此是乘法原理.
res是根节点的集合.
在这里插入图片描述

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    vector<TreeNode*> generateTrees(int n) {
        return dfs(1, n);
    }
    vector<TreeNode*> dfs(int l, int r){
        if (l > r) return {NULL};
        vector<TreeNode*> res;
        for (int i = l; i <= r; i ++ ){
            auto left = dfs(l, i - 1), right = dfs(i + 1, r);
            // 此时计算出的left是[l, i - 1]形成的森林(很多树)的所有根节点的集合.
            // 乘法原理
            for (auto l : left) 
                for (auto r : right){
                    auto root = new TreeNode(i);
                    root->left = l, root->right = r;
                    res.push_back(root);
                }
        }
        return res;
    }
};

LeetCode 96. 不同的二叉搜索树

分析

类似上一题, 先枚举区间长度i, 在枚举间隔j, 用乘法原理
f[i] += f[j - 1] * f[i - j]

代码

class Solution {
public:
    int numTrees(int n) {
        vector<int> f(n + 1);
        f[0] = 1;
        for (int i = 1; i <= n; i ++ )
            for (int j = 1; j <= i; j ++ )
                f[i] += f[j - 1] * f[i - j];
        return f[n];
    }
};

LeetCode 97. 交错字符串

分析

类似dp编辑距离去分析, 注意要剪枝, s3.size() != n + m的时候需要剪枝
在这里插入图片描述

代码

class Solution {
public:
    bool isInterleave(string s1, string s2, string s3) {
        int n = s1.size(), m = s2.size();
        if (s3.size() != n + m) return false; 
        // 注意不能与下面这句话交换顺序, 否则s3.size() 多了
        s1 = ' ' + s1, s2 = ' ' + s2, s3 = ' ' + s3;

        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        // 注意从0开始循环, 因为可以s1 一个字符也不取, 或者s2一个也不取
        // 否则,从1开始会漏掉上面的情况.
        for (int i = 0; i <= n; i ++ )        
            for (int j = 0; j <= m; j ++ ){
                if (!i && !j) f[i][j] = true;
                else {
                    if (i && s1[i] == s3[i + j]) f[i][j] = f[i - 1][j];
                    if (j && s2[j] == s3[i + j]) f[i][j] = f[i][j] || f[i][j - 1];
                }
            }
        return f[n][m];
    }
};

LeetCode 98. 验证二叉搜索树

分析

按照定义来判断, 左子树的最大值是否大于当前节点的值, 右子树的最小值是否小于当前节点的值
遍历的时候, 求下最小值和最大值

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        if (!root) return true;
        return dfs(root)[0];
    }
    vector<int> dfs(TreeNode* root){
        vector<int> res({1, root->val, root->val}); //  1. 当前整棵树是否符合定义 2. 左子树的最大值, 3. 右子树的最小值
        if (root->left){
            auto t = dfs(root->left); // 递归左子树的情况
            // 如果左子树不合法  
            // 或者 左子树中最大值 大于等于 当前根节点的值 也不合法
            if (!t[0] || t[2] >= root->val) res[0] = 0; 
            res[1] = min(res[1], t[1]);
            res[2] = max(res[2], t[2]);
        }
        if (root->right){
            auto t = dfs(root->right);
            if (!t[0] || t[1] <= root->val) res[0] = 0;
            res[1] = min(res[1], t[1]);
            res[2] = max(res[2], t[2]);
        }
        return res;
    }
};

code(更简单的做法)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        return dfs(root, nullptr, nullptr);
    }
    bool dfs(TreeNode* root, int* minn, int* maxx) {
        if (!root) return true;
        if (minn != nullptr && root->val <= *minn) return false;
        if (maxx != nullptr && root->val >= *maxx) return false;
        return dfs(root->left, minn, &root->val) && dfs(root->right, &root->val, maxx);
    }
};

LeetCode 99. 恢复二叉搜索树

分析

如何判断, 哪两个数被交换了
如果交换的是相邻两个数, 那么直接找逆序对即可
如果交换的不是相邻两个数, 可以发现交换的是第一个逆序对的第一个数第二个逆序对的第二个数
在这里插入图片描述
因此, 中序遍历输出, 找出错误的逆序对, 就可以找到这两个数

注意题目要求
进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?

Morris遍历(常数空间)

参考yxc题解
可以在当前点的前驱节点连一根线索, 方便回溯.
主要步骤, 从根节点开始遍历,
1.如果没有左子树, 遍历当前点, 进入右子树
2.如果有左子树, 找到当前的前驱节点, 找到前驱节点后, 我们需要判断 我们是从当前点的上方下来的, 还是从 前驱节点通过红色线索上去的.
(1) 如果前驱节点的右儿子是空的, 说明左子树还没遍历, 我们是从上面下来的, 则进入左子树, 并将前驱节点的右儿子置为当前点.
(2)如果前驱节点的右儿子是当前点, 说明我们是从前驱节点上来的, 左子树已经被遍历过, 则将前驱节点线索 清空, 打印当前点的值, 然后进入右子树继续遍历.
在这里插入图片描述
在这里插入图片描述

morris 时间复杂度 O(n)

因为在寻找前驱节点的时候, 当前根节点所连的边必定会被搜1次, 有n个点, 所以搜了n次

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode* root) {
        TreeNode* first = NULL, *second, *prep = NULL;
        while (root){
            if (!root->left){
                // 如果没有左子树 遍历当前点
                if (prep && prep->val > root->val){
                    if (!first) first = prep, second = root;
                    else second = root;
                }
                // 进入到右子树
                prep = root;
                root = root->right;
            }
            else {
                TreeNode* p = root->left;
                while (p->right && p->right != root) p = p->right;
                if (!p->right){ // 前驱节点还没连到当前点, 说明是从上面下来的   
                    p->right = root; // 连到当前点, 方便回溯
                    root = root->left; // 遍历左子树
                }
                else { // 否则, 是从下面上去的
                    p->right = NULL; // 清除线索
                    // 遍历当前点
                    if (prep && prep->val > root->val){
                        if (!first) first = prep, second = root;
                        else second = root;
                    }
                    prep = root;
                    root = root->right;
                }
            }
        }
        swap(first->val, second->val);
    }
};

代码(简单做法)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    void recoverTree(TreeNode* root) {
        dfs(root);
        swap(first->val, second->val);
    }
    TreeNode* first, *second, *prev;
    void dfs(TreeNode* root){
        if (!root) return ;
        dfs(root->left);
        if (prev && prev->val > root->val){
            if (!first )first = prev, second = root;
            else second = root;
        }
        prev = root; // 一定要加这句话
        dfs(root->right);
    }
};

LeetCode 100. 相同的树

分析

直接递归做即可
注意 p->val != q->val 可以和(!p && q || p && !q) 放在一起

代码

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool isSameTree(TreeNode* p, TreeNode* q) {
        if (!p && !q) return true;
        else if (!p && q || !q && p || p->val != q->val) return false;
        return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
    }

};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值