【leetcode】算法题记录(71-80)

简化路径

在这里插入图片描述
解题思路,看到题目要求,很容易的就想到了利用栈来解决问题,使用栈存储地址路径,然后通过遍历路径获得每一层地址的字符串,然后根据地址字符串判断操作,如果是..,则当栈不为空的情况出栈,如果是.或者空则不操作,否则入栈。
AC代码:

class Solution {
public:
    string simplifyPath(string path) {
        if(path.size() == 0) return "/";
        if(path[path.size() -1] != '/') path = path.append("/");
        stack<string> name;
        auto l = path.begin(); 
        for(auto r = path.begin(); r != path.end(); ++r) {
            if((*l == '/' && *r != '/') || (l == r)) continue;
            else {
                string tmp(l, r);
                cout << l - path.begin() << " " << r - path.begin() << " ";
                cout << tmp << endl;
                if(tmp == "/..") {
                    if(!name.empty()) name.pop();
                } else if(!(tmp == "/" || tmp == "/.")) {
                    name.push(tmp);
                }
                l = r;
            }
        }
        string res;
        while(!name.empty()) {
            res = name.top() + res;
            name.pop();

        }
        return res.size() == 0? "/" : res;
    }
};

编辑距离

在这里插入图片描述
看到这道题的时候脑子一片空白,然后想到的是先求出最长公共字符串,可惜可惜啊,都想到最长公共字符串了,都和动态规划沾上关系了,我这竟然还没有想到就直接用动态规划解决,毕竟三种操作都放在那里,告诉你如何状态转移了,默哀三秒。
步入正题,这道题确实是有点意思的,对于动态规划来说,说不明显但是在用完之后怎么看怎么明显。首先明确状态为何?
定义状态数组dp[i][j]表示word1的第i个字符和word2的第j个字符的编辑距离。
初始化:当word1为0的时候,编辑距离就是word2的长度;同理当word2为0的时候,编辑距离就是word1的长度。
状态转移:dp[i][j]有三种方式可以到达,分别为word1插入,word1删除,word1替换。这里会有奇怪的地方,word1删除,岂不是不能做到,毕竟在走到dp[i][j]的时候是不知道dp[i+1][j]的值。但是word1删除等价于word2插入。所以三种操作代表三种转移方案。
状态转移方程:dp[i][j] = min(dp[i-1][j], dp[i][j-1], dp[i-1][j-1]) + 1。不过还是有需要注意的地方,就是替换的时候,如果两个字符串的第i-1个字符和第j-1个字符是相等的,那么我是不需要进行操作的。所以word1替换的操作需要根据字符是否相等来决定是否+1操作。
AC代码:

class Solution {
public:
    int minDistance(string word1, string word2) {
        int n = word1.size(), m = word2.size();
        if(n * m == 0) return n + m;
        int dp[n+1][m+1];
        //default
        memset(dp, 0, sizeof(dp));
        for(int i = 0; i < m+1; ++i) dp[0][i] = i;
        for(int i = 0; i < n+1; ++i) dp[i][0] = i;

        //State transition
        for(int i = 1; i < n+1; ++i) {
            for(int j = 1; j < m+1; ++j) {
                int inert = dp[i-1][j] + 1;
                int delet = dp[i][j-1] + 1;
                int replace = dp[i-1][j-1];
                if(word1[i-1] != word2[j-1]) replace += 1;
                dp[i][j] = min(inert, min(delet, replace));
            }
        }

        return dp[n][m];
    }
};

矩阵置零

在这里插入图片描述
第一反应就是利用两个set分别保存将要被置为0的行和列,然后看到题目要求:

进阶:

一个直接的解决方案是使用  O(mn) 的额外空间,但这并不是一个好的解决方案。
一个简单的改进方案是使用 O(m + n) 的额外空间,但这仍然不是最好的解决方案。
你能想出一个常数空间的解决方案吗?

不错,第一反应就是一个简单的改进方案,囧。
然后苦苦思索常数空间,想到的一个方案就是在找到0之后先不置为0,用一个别的数字先代替,当然第一反应是-1,但是在写代码的时候又考虑到没说数组中不存在-1的数字,随推翻。然后再次思索,随失败。不甘心的参考了解题,神奇。
最后是通过以第一行第一列作为标记,然后单独考虑第一行第一列,最后虽然额外空间是常数空间,但是怎么都觉得这个代码比较臃肿。
AC代码:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
        bool fr = false, fc = false;
        int m = matrix.size();
        int n = (*matrix.begin()).size();
        //判断第一行是否存在0
        for(int i = 0; i < n; ++i) {
            if(matrix[0][i] == 0) {
                fr = true;
                break;
            }
        }
        //判断第一列是否存在0
        for(int j = 0; j < m; ++j) {
            if(matrix[j][0] == 0) {
                fc = true;
                break;
            }
        }
        //除了第一行第一列,遍历数组,如果存在0,则将此位置对应的首个行和列置为0
        for(int i = 1; i < m; ++i) {
            for(int j = 1; j < n; ++j) {
                if(matrix[i][j] == 0) {
                    matrix[0][j] = 0;
                    matrix[i][0] = 0;
                }
            }
        }
        //判断该位置的首个行和列是否为0,是则修改此位置为0
        for(int i = 1; i < m; ++i) {
            for(int j = 1; j < n; ++j) {
                if(matrix[0][j] == 0 || matrix[i][0] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        //单独考虑的第一行第一列
        if(fr) {
            for(int i = 0; i < n; ++i) {
                matrix[0][i] = 0;
            }
        }
        if(fc) {
            for(int j = 0; j < m; ++j) {
                matrix[j][0] = 0;
            }
        }
    }
};

搜索二维矩阵

在这里插入图片描述
看到排序数组,而且查找值,提高效率。不用思考就是二分查找,矩阵又如何,两次二分查找就可以解决了,先找行,再找列,找到返回true,否则返回false。
AC代码:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        int m = matrix.size();
        if(m == 0) return false;
        int n = (*matrix.begin()).size() - 1;
        if(n == -1) return false;
        int targetm = 0, targetn = 0;
        while(targetm < m) {
            int midm = targetm + (m - targetm) / 2;
            if(matrix[midm][0] == target) return true;
            else if(matrix[midm][0] < target) {
                targetm = midm + 1;
            }else {
                m = midm;
            }
        }
        targetm = targetm == 0 ? 0 : targetm-1;
        while(targetn <= n) {
            int midn = targetn + (n - targetn) / 2;
            if(matrix[targetm][midn] == target) return true;
            else if(matrix[targetm][midn] < target) {
                targetn = midn + 1;
            }else {
                n = midn - 1;
            }
        }
        return false;
    }
};

PS,二维数组其实也是一维数组,所以这道题二分查找可以更简洁,left = 0;right = m*n-1;mid = left + (right - left) / 2,对应的值是matrix[mid/n][mid%n]

颜色分类

在这里插入图片描述
解题思路,这道题不就是排序吗,可以利用快速排序的思想,以1为基准,小于1的放左边,大于1的放右边。这样的话我们可以维护1的左右边界,遍历一次数组即可完成。
AC代码:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        if(nums.size() < 2) return;
        //left记录1的左边界,right记录1的右边界
        int left = 0, right = nums.size() - 1;
        for(int i = 0; i <= right; ++i) {
            if(nums[i] == 0) {
                swap(nums[left++], nums[i]);
            }
            if(nums[i] == 2) {
                //i--保证从后面交换过来的数字再次判断是否为0
                swap(nums[i--], nums[right--]);
            }
        }
    }
};

最小覆盖子串

在这里插入图片描述
解题思路,这道题明显的是利用滑动窗口来解决,但是奈何思考半天不知道如何操作,大概的思路了解到位,但是在写的时候磕磕绊绊,最终还是参考借鉴了一番,继续加油把~
AC代码:

class Solution {
public:
    string minWindow(string s, string t) {
        if(s.size() * t.size() == 0) return "";
        unordered_map<char, int> need, window;
        for (auto c : t) need[c]++;

        int left = 0, right = 0;
        int valid = 0;
        // 记录最小覆盖子串的起始索引及长度
        int start = 0, len = INT_MAX;
        while (right < s.size()) {
            // c 是将移入窗口的字符
            char c = s[right];
            // 右移窗口
            right++;
            // 进行窗口内数据的一系列更新
            if (need.count(c)) {
                window[c]++;
                if (window[c] == need[c])
                    valid++;
            }

            // 判断左侧窗口是否要收缩
            while (valid == need.size()) {
                // 在这里更新最小覆盖子串
                if (right - left < len) {
                    start = left;
                    len = right - left;
                }
                // d 是将移出窗口的字符
                char d = s[left];
                // 左移窗口
                left++;
                // 进行窗口内数据的一系列更新
                if (need.count(d)) {
                    if (window[d] == need[d])
                        valid--;
                    window[d]--;
                }
            }
        }
        // 返回最小覆盖子串
        return len == INT_MAX ?
            "" : s.substr(start, len);
    }
};

组合

在这里插入图片描述
这道题和全排列很像,只不过是规定了数字上限以及数组个数的,所以不难想到,这道题可以利用回溯法来解决。
这里需要注意的是,循环的终止条件。
i <= n - (k - path.size()) + 1;(k - path.size())表示数组中还需要填写的数字个数。例如当n=10,k=5,且此时path.size()=2;那么i能取到的最大值并非是10,而是8。因为数组还需要填写三个数字,所以最大是8。

AC代码:

class Solution {
public:
    vector<vector<int>> res;

    void dfs(int n, int k, int start, vector<int>& path) {
        if(path.size() == k) {
            res.push_back(path);
            return;
        }
        for(int i = start; i <= n - (k - path.size()) + 1; ++i) {
            path.push_back(i);
            dfs(n, k, i + 1, path);
            path.pop_back();
        }
    }

    vector<vector<int>> combine(int n, int k) {
        if(n <= 0 || k <= 0 || n < k) return res;
        vector<int> path;
        dfs(n, k, 1, path);
        return res;
    }
};

子集

在这里插入图片描述
解题思路,因为上一道是用的回溯。所以在看到这道题的时候,不难会想到使用多次的回溯就可以解决,所以解题方案是使用回溯法。
AC代码:

class Solution {
public:
    vector<vector<int>> res;

    void dfs(int size, int start, vector<int>& nums, vector<int>& path) {
        if(path.size() == size) {
            res.push_back(path);
            return;
        }
        for(int i = start; i < nums.size() - (size - path.size()) + 1; ++i) {
            path.push_back(nums[i]);
            dfs(size, i+1, nums, path);
            path.pop_back();
        }
    }

    vector<vector<int>> subsets(vector<int>& nums) {
        res.push_back({});
        vector<int> path;
        for(int i = 1; i <= nums.size(); ++i) {
            dfs(i, 0, nums, path);
        }
        return res;
    }
};

单词搜索

在这里插入图片描述
这道题的思路和之前的n皇后问题类似,需要对某一个位置上下左右进行搜索。所以不难想到需要用回溯的方法解决,最后还需要注意的是,同一个单元格内的字母不允许被重复使用,所以在回溯的时候需要对数组操作标记。

AC代码:

class Solution {
public:
    bool dfs(int x, int y, int index, vector<vector<char>>& board, string& word) {
        if(index == word.size()) return true;
        //cout << x << " " << y << " " << index << " " << word[index] << endl;

        char tmp = board[x][y];
        board[x][y] = '0';
        //上
        if(x - 1 >= 0 && board[x-1][y] == word[index]) 
            if(dfs(x-1, y, index+1, board, word)) 
                return true;
        //下
        if(x + 1 < board.size() && board[x+1][y] == word[index]) 
            if (dfs(x+1, y, index+1, board, word))
                return true;
        //左
        if(y - 1 >= 0 && board[x][y-1] == word[index]) 
            if (dfs(x, y-1, index+1, board, word))
                return true;
        //右
        if(y + 1 < (*board.begin()).size() && board[x][y+1] == word[index]) 
            if (dfs(x, y+1, index+1, board, word))
                return true;

        board[x][y] = tmp;

        return false;
    }

    bool exist(vector<vector<char>>& board, string word) {
        int m =board.size(), n = (*board.begin()).size();
        for(int i = 0; i < m; ++i) {
            for(int j = 0; j < n; ++j) {
                if(board[i][j] == word[0]) {
                    if(dfs(i, j, 1, board, word)) return true;
                }
            }
        }
        return false;
    }
};

删除排序数组中的重复项 II

在这里插入图片描述
解题思路,想到的是双指针,前面的指针搜索每个数字出现的次数,后面的指针重新写数组。
AC代码

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int ope = 0, scan = 0;
        int times = 1, pre = -1;
        while(scan < nums.size()) {
            pre = nums[scan];
            ++scan;
            while(scan < nums.size() && pre == nums[scan]) {
                ++scan;
                ++times;
            }
            // cout << pre << " " << times << " " << scan << endl;
            int n = times > 2 ? 2 : times;
            while(n > 0) {
                nums[ope++] = pre;
                n--;
            }
            // cout << "res: " << ope << endl; 
            // for(int i = 0; i < ope; ++i) cout << nums[i] << " ";
            // cout << endl;
            times = 1;
        }
        return ope;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值