Leetcode题库(61-80)

这个系列就记录下我做Leetcode的过程,保持点手感。
最近备考,不一定更新了。

Leetcode61. 旋转链表

思路:
题目相当于让你把头部k个节点链接到尾部。
代码:

/**
 * 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* rotateRight(ListNode* head, int k) {
        if(head == nullptr || !k) return head;
        int len = 1;
        ListNode* tmp = head, *st;
        while(tmp->next != nullptr) ++len, tmp = tmp->next;
        tmp->next = head;
        k %= len;
        for(int i = 0;i < len - k;i++) tmp = tmp->next;
        st = tmp->next;
        tmp->next = nullptr;
        return st;
    }
};



Leetcode62. 不同路径

思路: 二维dp,当前位置路径数量决定于左侧点路径数量加上上方点路径数量。
代码:

class Solution {
public:
    int dp[101][101];
    int uniquePaths(int m, int n) {
        dp[1][0] = 1;
        for(int i = 1;i <= m;i++){
            for(int j = 1;j <= n;j++) dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
        }
        return dp[m][n];
    }
};



Leetcode63. 不同路径 II

思路: 二维dp。当前位置路径数量决定于左侧点路径数量加上上方点路径数量。如果当前位置为石头,则当前位置路径数量为0。
代码:

class Solution {
public:
    int dp[101][101] = {0, 1};
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
        int m = obstacleGrid.size(), n = obstacleGrid[0].size();
        for(int i = 1;i <= m;i++){
            for(int j = 1;j <= n;j++) dp[i][j] = (1 - obstacleGrid[i - 1][j - 1]) * (dp[i - 1][j] + dp[i][j - 1]);
        }
        return dp[m][n];
    }
};



Leetcode64. 最小路径和

思路: 二维dp。当前位置路径和取决于左侧和上方最小路径和,我们只要每次比较获得最小值即可。
代码:

class Solution {
public:
    int minPathSum(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        for(int i = 0;i < n;i++){
            for(int j = 0;j < m;j++) grid[i][j] += (!i && !j) ? 0 : min(i ? grid[i - 1][j] : 1e9, j ? grid[i][j - 1] : 1e9);
        }
        return grid[n - 1][m - 1];
    }
};



Leetcode65. 有效数字

思路: 模拟。可以将问题分解为查看一个整数串中数字的长度(如果不为整数串返回-1),查看一个串是否为浮点数。然后对于各种情况处理。
代码:

class Solution {
public:
    int integerLength(string s){
        if(s.length() <= 0) return 0;
        int st = 0;
        // 处理符号开头的情况 st为数字串开始下标
        if(s[0] == '+' || s[0] == '-') st = 1;
        // 处理存在非数字的情况
        for(int i = st;i < s.length();i++) if(s[i] < '0' || s[i] > '9') return -1;
        return s.length() - st;
    }
    bool isFloat(string s){
    	// 小数点指针
        int point = -1;
        for(int i = 0;i < s.length();i++){
            if(s[i] == '.'){
                point = i;
                break;
            }
        }
        // 若不存在小数点
        if(point < 0){
        	// 判断当前字符串是否为整数
            if(integerLength(s) > 0) return true;
            else return false;
        }
        // l为小数点左侧整数串长度 r为小数点右侧整数串长度
        int l = integerLength(s.substr(0, point)), r = integerLength(s.substr(point + 1, s.length() - point - 1));
        // 左/右侧不为整数串
        if(l < 0 || r < 0) return false;
        // 左右侧长度都为0(仅有一个.的情况)
        if(l == 0 && r == 0) return false;
        // 小数点右侧跟着符号的情况
        if(point + 1 < s.length() && (s[point + 1] == '-' || s[point + 1] == '+')) return false;
        return true;
    }
    bool isNumber(string s) {
    	// e的位置
        int e = -1;
        for(int i = 0;i < s.length();i++){
            if(s[i] == 'e' || s[i] == 'E'){
                e = i;
                break;
            }
        }
        // 如果没有e
        if(e < 0){
            if(isFloat(s)) return true;
            return false;
        }
        // 如果e左侧不为小数(包括整数)
        if(!isFloat(s.substr(0, e))) return false;
        // 如果e右侧不为小数
        if(integerLength(s.substr(e + 1, s.length() - e - 1)) <= 0) return false;
        return true;
    }
};



Leetcode66. 加一

思路: 从后往前模拟即可。若当前位置大于9,取模,更改进位。注意判断最终有进位的情况。
代码:

class Solution {
public:
    vector<int> plusOne(vector<int>& digits) {
        digits[digits.size() - 1]++;
        int tmp = 0, add = 0;
        for(int i = digits.size() - 1;i >= 0;i--) tmp = digits[i] + add, digits[i] = tmp % 10, add = tmp / 10;
        if(add) digits.insert(digits.begin(), add);
        return digits;
    }
};



Leetcode67. 二进制求和

思路: 从低位到高位模拟即可。为了方便,我将两个字符串反转后进行计算。
代码:

class Solution {
public:
    string addBinary(string a, string b) {
        std::reverse(a.begin(), a.end());
        cout << a << '\n';
        std::reverse(b.begin(), b.end());
        cout << b << '\n';
        int add = 0, pl = 0, tmp = 0;
        string ans;
        for(;pl < a.length() && pl < b.length();pl++){
            tmp = add + a[pl] - '0' + b[pl] - '0';
            add = tmp >> 1;
            ans.append(1, (char)(tmp % 2 + '0'));
        }
        for(;pl < a.length();pl++){
            tmp = add + a[pl] - '0';
            add = tmp >> 1;
            ans.append(1, (char)(tmp % 2 + '0'));
        }
        for(;pl < b.length();pl++){
            tmp = add + b[pl] - '0';
            add = tmp >> 1;
            ans.append(1, (char)(tmp % 2 + '0'));
        }
        while(add) ans.append(1, (char)(add % 2 + '0')), add >>= 1;
        std::reverse(ans.begin(), ans.end());
        return ans;
    }
};



Leetcode68. 文本左右对齐

思路: 按要求模拟级即可。
代码:

class Solution {
public:
    vector<string> fullJustify(vector<string>& words, int maxWidth) {
        vector<string> ans;
        string tmpStr;
        int l = 0, r = 0, sum, avg, add, tmp;
        while(l < words.size()){
        	// 计算已统计的单词总长度
            sum = words[l].size();
            // 当前遍历到的单词下标
            r = l + 1;
            // sum += 1 + words[r].size()其中的 1 是为了给每个单词之间初始化一个空格
            while(r < words.size() && sum + words[r].size() + 1 <= maxWidth) sum += 1 + words[r].size(), ++r;
            // 当前单词串
            tmpStr = words[l];
            tmp = maxWidth - sum + (r - l - 1);
            // 最后一行左对齐
            if(r >= words.size()){
                for(int i = l + 1;i < r;i++) tmpStr += " " + words[i];
                tmpStr.append(maxWidth - tmpStr.length(), ' ');
            }else if(r - l == 1) tmpStr.append(tmp, ' ');  // 如果一行只有一个单词则左对齐
            else{
            	// 平均空格数
                avg = tmp / (r - l - 1);
                // 前add个需要多加一个空格
                add = tmp - avg * (r - l - 1);
                for(int i = l + 1;i < r;i++){
                	// 给单词补上前面的空格
                    tmpStr.append(avg + (add > 0 ? 1 : 0), ' ');
                    tmpStr += words[i];
                    --add;
                }
            }
            ans.push_back(tmpStr);
            l = r;
        }
        return ans;
    }
};



Leetcode69. x 的平方根

思路: 二分答案,找到最大符合条件的答案。
代码:

class Solution {
public:
    int mySqrt(int x) {
        int ans = 0, l = 0, r = 46340, mid;
        while(l <= r){
            mid = (l + r) >> 1;
            if(mid * mid <= x) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        return ans;
    }
};



Leetcode70. 爬楼梯

思路: dp。对于每一阶楼梯而言,对于下一阶以及下下阶的贡献等于自身的方案数。
代码:

class Solution {
public:
    int dp[47] = {1};
    int climbStairs(int n) {
        for(int i = 0;i < n;i++){
            dp[i + 2] += dp[i];
            dp[i + 1] += dp[i];
        }
        return dp[n];
    }
};



Leetcode71. 简化路径

思路: 模拟。
代码:

class Solution {
public:
    string ans[2000];
    int cnt = -1;
    string simplifyPath(string path) {
        int p = 0, len = path.length();
        string out = "";  // 存储答案
        string tmp;
        while(p < len){
        	// 将单词前/去除
            while(p < len && path[p] == '/') ++p;
            tmp = "";
            // 取出当前单词(暂且将..以及.看做单词取出)
            while(p < len && path[p] != '/') tmp = tmp + path[p++];
            // 如果当前单词为空或者用.指向当前目录, 则不变
            if(tmp.length() == 0 || (tmp.length() == 1 && tmp[0] == '.')) continue;
            // 若用..返回上级目录则返回
            if(tmp.length() == 2 && tmp[0] == tmp[1] && tmp[0] == '.'){
                if(cnt >= 0) --cnt;
            }else ans[++cnt] = tmp;
        }
        // 输出
        if(cnt < 0) out = "/";
        else for(int i = 0;i <= cnt;i++) out += "/" + ans[i];
        return out;
    }
};



Leetcode72. 编辑距离

思路: dp。dp[i][j]代表word1.substr(0,i)word2.substr(0,j)匹配的最小花费。每次遍历到word1[i]word2[j]有四种处理情况:

  1. word1[i] == word2[j]:那么相当于word1.substr(0,i-1)word2.substr(0,j-1)匹配的最小花费,即dp[i][j]==dp[i-1][j-1]
  2. 更改word1[i]使得word1[i] == word2[j]:那么与情况1相同,dp[i][j]==dp[i-1][j-1]
  3. 删除word1[i]:那么相当于word1.substr(0,i-1)word2.substr(0,j)匹配的最小花费,即dp[i][j]==dp[i-1][j]
  4. 插入一个字符作为word1[i]:那么相当于使word2删除最后一个字符word2[j],变成了word1.substr(0,i)word2.substr(0,j-1)匹配的最小花费,即dp[i][j]==dp[i][j-1]

需要注意下,需要处理纯纯插入或者纯纯删除的情况,即dp[i][0]dp[0][j](这两种对于每增加一个字符长度,仅多需要处理一次,即dp[i][0]=idp[0][j]=j)。

class Solution {
public:
    int dp[501][501];
    int minDistance(string word1, string word2) {
        for(int i = 0;i <= word1.length();i++) dp[i][0] = i;
        for(int i = 0;i <= word2.length();i++) dp[0][i] = i;
        for(int i = 1;i <= word1.length();i++){
            for(int j = 1;j <= word2.length();j++){
                if(word1[i - 1] == word2[j - 1]) dp[i][j] = dp[i - 1][j - 1];
                else dp[i][j] = min(dp[i - 1][j - 1], min(dp[i - 1][j], dp[i][j - 1])) + 1;
            }
        }
        return dp[word1.length()][word2.length()];
    }
};



Leetcode73. 矩阵置零

思路: 使用了两个变量存储第一行中每列(col)第一列中每行(row)是否存在0。先将第一行第一列处理完,然后处理matrix[1:-1, 1:-1]中每行每列是否存在0,如果存在,直接将对应行第一列、列第一行的数置为0(作为标记存在)。然后如果第一列中某一行为0,则置所有当前行的数字为0;如果第一行中某一列为0,则置所有当前列的数字为0。然后用col、row特殊处理下第一行第一列即可。
代码:

class Solution {
public:
    void setZeroes(vector<vector<int>>& matrix) {
    	// 第一行中每列(col)第一列中每行(row)是否存在0
        bool row = false, col = false;
        for(int i = 0;i < matrix.size() && !row;i++) if(!matrix[i][0]) row = true;
        for(int i = 0;i < matrix[0].size() && !col;i++) if(!matrix[0][i]) col = true;
        // 处理matrix[1:-1, 1:-1]中每行每列是否存在0
        for(int i = 1;i < matrix.size();i++){
            for(int j = 1;j < matrix[0].size();j++) if(!matrix[i][j]) matrix[i][0] = matrix[0][j] = 0;
        }
        for(int i = 1;i < matrix.size();i++){
            if(!matrix[i][0]) for(int j = 1;j < matrix[0].size();j++) matrix[i][j] = 0;
        }
        for(int i = 1;i < matrix[0].size();i++){
            if(!matrix[0][i]) for(int j = 1;j < matrix.size();j++) matrix[j][i] = 0;
        }
        // 用col/row特殊处理下第一行第一列
        if(row) for(int i = 0;i < matrix.size();i++) matrix[i][0] = 0;
        if(col) for(int i = 0;i < matrix[0].size();i++) matrix[0][i] = 0;
    }
};



Leetcode74. 搜索二维矩阵

思路: 二分。由于矩阵每行有序,每列有序,并且下一行值必定大于上一行,所以先根据行首值二分,获得target可能行号ans,然后在行ans中查看是否存在target。
代码:

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
    	// ans用于存放可能得行号
        int l = 0, r = matrix.size() - 1, mid, ans = 0;
        // 二分获取行号
        while(l <= r){
            mid = (l + r) >> 1;
            if(matrix[mid][0] <= target) ans = mid, l = mid + 1;
            else r = mid - 1;
        }
        // 二分判断行中是否有target
        l = 0, r = matrix[ans].size() - 1;
        while(l <= r){
            mid = (l + r) >> 1;
            if(matrix[ans][mid] == target) return true;
            else if(matrix[ans][mid] < target) l = mid + 1;
            else r = mid - 1;
        }
        return false;
    }
};



Leetcode75. 颜色分类

思路: 要求仅使用常数空间一趟扫描,那我们可以使用双指针。指针l指向已经排好的0的最后一个位置,指针r指向已经排好的2的第一个位置。然后遍历,发现0的时候添加到左边,发现2添加到右边。
代码:

class Solution {
public:
    void sortColors(vector<int>& nums) {
        int l = -1,r = nums.size();
        for(int i = 0;i < r;i++){
        	// 如果是0直接放到前面, 由于前面必为排好的0, 所以不用考虑移动后当前位置值为2的情况以及需要移动左指针的情况
            if(nums[i] == 0) swap(nums[++l], nums[i]);
            // 处理为2的情况
            if(nums[i] == 2){
            	// 由于后面的没处理过, 所以需要先处理连续2的情况
                while(r > i + 1 && nums[r - 1] == 2) --r;
                swap(nums[--r], nums[i]);
                // 如果换过来啦一个0, 那么当前位置还要和前面换
                if(nums[i] == 0) swap(nums[++l], nums[i]);
            }
        }
    }
};



Leetcode76. 最小覆盖子串

思路: 滑动窗口。预先处理出来t字符串各类字母的个数,存储在timesT中,由于 A A AAscII编码为 65 65 65,所以我们减去 65 65 65作为基值(可以略微减小点空间)。在s上维护一个窗口,用timesS记录窗口中各个字符的数量(同样是减去 65 65 65),遍历s的时候将当前遍历到的字符加入窗口中进行统计(遍历到的位置i作为窗口右端点),此时更新窗口左端点l,如果左侧端点的字符在窗口中出现次数大于在t字符串中出现的次数,那么将其删除。将窗口缩小后判断当前窗口是否满足所有字符串条件,满足就对最小长度minLength以及最小长度窗口左位置minL进行更新。
代码:

class Solution {
public:
	// 最小窗口长度minLength  最小长度窗口左位置minL
    int minLength = 1e6, minL = -1;
    // 窗口中各类字母的个数timesS  t中各类字母的个数timesT
    int timesS[60], timesT[60];
    string minWindow(string s, string t) {
    	// 记得初始化
        memset(timesS, 0, sizeof (timesS));
        memset(timesT, 0, sizeof (timesT));
        // 当前左窗口位置l  f用于判断当前窗口内字母是否符合t要求
        int l = 0, f;
        // 统计t字符情况
        for(int i = 0;i < t.length();i++) timesT[t[i] - 65]++;
        for(int i = 0;i < s.length();i++){
        	// 扩展维护窗口
            timesS[s[i] - 65]++;
            // 缩减左窗口. 如果左侧端点的字符在窗口中出现次数大于在t字符串中出现的次数,那么将其删除
            while(l <= i && timesS[s[l] - 65] > timesT[s[l] - 65]) timesS[s[l] - 65]--, l++;
            f = 1;
            for(int j = 0;j < 60 && f;j++){
                if(timesT[j] > timesS[j]) f = 0;
            }
            // 符合标准加入答案
            if(f && i - l + 1 < minLength) minLength = i - l + 1, minL = l;
        }
        if(minL == -1) return "";
        return s.substr(minL, minLength);
    }
};



Leetcode77. 组合

思路: 递归。(我还以为是所有排列组合,交上去才发现是无逆序对形式的组合)。直接看代码吧。
代码:

class Solution {
public:
	// 存储当前排列
    vector<int> tmpV;
    // 存储答案
    vector<vector<int>> ans;
    // 递归获得当前排列下一个数的所有可能组合
    // n为能取的最大的数  k为当前排列还需要添加几个数  st为当前位置的数字的最小值
    void nextVector(int n, int k, int st){
    	// i为当前取值
        for(int i = st;i <= n;i++){
            tmpV.push_back(i);
            // 如果存入的是最后一个数字, 那么加入答案序列
            if(k == 1) ans.push_back(tmpV);
            // 如果还能填入数字, 那么继续递归填入数字(其实这一步可以剪枝的, 判断剩下的数字是否足以填满k-1个格子, 但我懒得写了:))
            else if(st < n) nextVector(n, k - 1, i + 1);
            // 回溯
            tmpV.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        tmpV.clear();
        ans.clear();
        nextVector(n, k, 1);
        return ans;
    }
};



Leetcode78. 子集

思路: 递归。和上一题其实差不多。
代码:

class Solution {
public:
	// 用于存储传入的nums变量
    vector<int> first;
    // 当前的序列
    vector<int> tmpV;
    // 答案
    vector<vector<int>> ans;
    // 处理传入序列中从下标为st开始的值
    void addVector(int st){
    	// 从st开始的每个值都可添加到当前位置
        for(int i = st;i < first.size();i++){
            tmpV.push_back(first[i]);
            ans.push_back(tmpV);
            // 如果当前遍历到的不是最后一个下标, 那么可以继续递归
            if(i < first.size() - 1) addVector(i + 1);
            // 回溯
            tmpV.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
    	// 记得初始化
        first = nums;
        tmpV.clear();
        ans.clear();
        ans.emplace_back();
        addVector(0);
        return ans;
    }
};



Leetcode79. 单词搜索

思路: dfs+回溯嗯跑。用dfs判断当前点位周围是否存在可以满足board[i][j]==word[p]的位置,如果有,则到那个位置跑下一个字母的搜索。
代码:

class Solution {
public:
	// 设置了个全局变量免得一直传参
    vector<vector<char>> initBoard;
    // 设置了个全局变量免得一直传参
    string initWord;
    // 判断位置(i,j)是否被访问过, vis[i][j]==0未访问, vis[i][j]==1已访问
    int vis[8][8];
    // 方向, dir[i][0]是当前位置x变化量, dir[i][1]是当前位置y变化量
    int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
    // dfs x为当前x坐标, y为当前y坐标, p为当前搜索word中下标为p的字符
    int find(int x, int y, int p){
    	// 满足条件就返回
        if(p >= initWord.length()) return 1;
        // 四个方向去寻找
        for(int i = 0;i < 4;i++){
        	// 下一个单元格位置(xx,yy)
            int xx = x + dir[i][0], yy = y + dir[i][1];
            // 判断是否越界
            if(xx < 0 || xx >= initBoard.size() || yy < 0 || yy >= initBoard[0].size()) continue;
            // 判断是否被访问过  是否为word[p]
            if(!vis[xx][yy] && initBoard[xx][yy] == initWord[p]){
            	// 标记已访问
                vis[xx][yy] = 1;
                // 找到了直接返回
                if(find(xx, yy, p + 1)) return 1;
                // 回溯
                vis[xx][yy] = 0;
            }
        }
        return 0;
    }
    bool exist(vector<vector<char>>& board, string word) {
    	// 初始化
        initBoard = board;
        initWord = word;
        memset(vis, 0, sizeof(vis));
        // 将每个格子作为初始点进行寻找
        for(int i = 0;i < board.size();i++){
            for(int j = 0;j < board[0].size();j++){
                if(board[i][j] == word[0]){
                	// 标记已访问
                    vis[i][j] = 1;
                    // 找到了直接返回
                    if(find(i, j, 1)) return true;
                    // 回溯
                    vis[i][j] = 0;
                }
            }
        }
        return false;
    }
};



Leetcode80. 删除有序数组中的重复项 II

思路: 原数组有序(单调不减),相当于给你这么的一个数组,将其按序添加到栈中,使栈顶三个数字相同的时候将顶部数字出栈。
代码:

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
    	// 当前栈的大小为ed+1
        int ed = -1;
        // 将数组中的数字按序添加到栈中
        for(int i = 0;i < nums.size();i++){
        	// 入栈
            nums[++ed] = nums[i];
            // 如果顶部三个相同则出栈
            if(ed > 1 && nums[ed - 2] == nums[ed]) --ed;
        }
        return ed + 1;
    }
};



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值