leetcode 1 ~ 100

文章目录

        • [1. 两数之和(用哈希表减少查找的时间复杂度)](#1. 两数之和(用哈希表减少查找的时间复杂度))
        • [2. 两数相加(高精度加法)](#2. 两数相加(高精度加法))
        • 3.无重复字符的最长子串:(模板:经典的滑动窗口算法)
        • [5. 最长回文子串(枚举)](#5. 最长回文子串(枚举))
        • [6. Z 自形变换(找规律)](#6. Z 自形变换(找规律))
        • 7.整数反转
        • [8. 字符串转换整数(atoi)](#8. 字符串转换整数(atoi))
        • [9. 回文数](#9. 回文数)
        • 11.盛水最多的容器(贪心(基于双指针来实现))
        • [12. 整数转罗马数字](#12. 整数转罗马数字)
        • [13. 罗马数字转整数](#13. 罗马数字转整数)
        • [14. 最长公共前缀](#14. 最长公共前缀)
        • [15. 三数之和(双指针 + 去重)](#15. 三数之和(双指针 + 去重))
        • [16. 最接近的三数之和(双指针算法)](#16. 最接近的三数之和(双指针算法))
        • [17. 电话号码的字母组合(标准 DFS)](#17. 电话号码的字母组合(标准 DFS))
        • [18. 四数之和(双指针算法)](#18. 四数之和(双指针算法))
        • [19. 删除链表的倒数第 N 个结点(前后双指针寻找结点 + dummy 避免特判)](#19. 删除链表的倒数第 N 个结点(前后双指针寻找结点 + dummy 避免特判))
        • [20. 有效的括号(栈的简单应用)](#20. 有效的括号(栈的简单应用))
        • [21. 合并两个有序链表(模拟归并排序中的二路归并操作)](#21. 合并两个有序链表(模拟归并排序中的二路归并操作))
        • [22. 括号生成(DFS)](#22. 括号生成(DFS))
        • [24. 两两交换链表中的节点(链表结点交换算法)](#24. 两两交换链表中的节点(链表结点交换算法))
        • [26. 删除有序数组中的重复项(经典双指针算法)](#26. 删除有序数组中的重复项(经典双指针算法))
        • [27. 移除元素(简单的双指针算法)](#27. 移除元素(简单的双指针算法))
        • [31. 下一个排列:](#31. 下一个排列:)
        • [32. 最长有效括号](#32. 最长有效括号)
        • 33.搜索旋转排序数组:
        • 34.在排序数组中查找元素的第一个和最后一个位置:
        • 35.搜索插入位置:
        • 36.有效的数独:
        • 37.解数独:
        • 38.外观数列:
        • 39.组合总和:
        • 40.组合总和:
        • 41.缺失的第一个正数:
        • 42.接雨水:
        • 43.字符串相乘:
        • 44.通配符匹配:
        • [45.跳跃游戏 II:](#45.跳跃游戏 II:)
        • 46.全排列:
        • [47.全排列 II:](#47.全排列 II:)
        • 48.旋转图像:
        • 49.字母异位词分组:
        • [50.Pow(x, n):](#50.Pow(x, n):)
        • [51. N 皇后(DFS)](#51. N 皇后(DFS))
        • [52. N 皇后 II(DFS,和 51 题完全一样,只是不需要记录方案是什么)](#52. N 皇后 II(DFS,和 51 题完全一样,只是不需要记录方案是什么))
        • [53. 最大子数组和(动态规划)](#53. 最大子数组和(动态规划))
        • [54. 螺旋矩阵](#54. 螺旋矩阵)
        • 55.跳跃游戏:
        • [56. 区间合并(模板:区间合并)](#56. 区间合并(模板:区间合并))
        • [57. 插入区间](#57. 插入区间)
        • [58. 最后一个单词的长度(模拟题)](#58. 最后一个单词的长度(模拟题))
        • [59. 螺旋矩阵 II(方向数组的简单应用)](#59. 螺旋矩阵 II(方向数组的简单应用))
1. 两数之和(用哈希表减少查找的时间复杂度)
cpp 复制代码
class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> dict; // val : index
        for (int i = 0; i < nums.size(); i++)
        {
            if (dict.count(target - nums[i]))
                return {dict[target - nums[i]], i};
            else
                dict[nums[i]] = i;
        }

        return {};
    }
};

(注:本题也可以用双指针算法。)

2. 两数相加(高精度加法)
cpp 复制代码
/**
 * 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* addTwoNumbers(ListNode* l1, ListNode* l2) {
        ListNode* dummy = new ListNode(-1);
        ListNode* p = dummy;
        int t = 0;
        while (l1 || l2 || t)
        {
            if (l1) {
                t += l1->val;
                l1 = l1->next;
            }
            if (l2) {
                t += l2->val;
                l2 = l2->next;
            }
            p->next = new ListNode(t % 10);
            p = p->next;
            t /= 10;
        }

        return dummy->next;
    }
};
3.无重复字符的最长子串:(模板:经典的滑动窗口算法)
cpp 复制代码
class Solution {
public:
    int lengthOfLongestSubstring(string s) {
        int res = 0;
        unordered_map<char, int> ht; // ht: hashtable
        for (int l = 0, r = 0; r < s.size(); r++)
        {
            ht[s[r]]++;
            while (ht[s[r]] > 1)
            {
                ht[s[l]]--;
                l++;
            }

            res = max(res, r - l + 1);
        }

        return res;
    }
};
5. 最长回文子串(枚举)
cpp 复制代码
class Solution {
public:
    string longestPalindrome(string s) {
        string res = "";
        for (int i = 0; i < s.size(); i++) {
            // 看回文串的长度是否是奇数
            int l = i - 1, r = i + 1;
            while (l >= 0 && r < s.size() && s[l] == s[r]) l--, r++;
            if (res.size() < r - (l + 1)) res = s.substr(l + 1, r - (l + 1));

            // 看回文串的长度是否是偶数
            l = i, r = i + 1;
            while (l >= 0 && r < s.size() && s[l] == s[r]) l--, r++;
            if (res.size() < r - (l + 1)) res = s.substr(l + 1, r - (l + 1));
        }

        return res;
    }
};
6. Z 自形变换(找规律)
cpp 复制代码
class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows == 1) return s; // numRows = 1会导致base为0,需要特判
        string res = "";
        
        int base = 2 * numRows - 2;
        for (int k = 0; k < numRows; k++)
            for (int i = 0; i < s.size(); i++)
                if ((i - k) % base == 0 || (i + k) % base == 0)
                    res += s[i];

        return res;
    }
};


// 每一趟会有 numRows + (nuRows - 2) = 2 * numRows - 2 = base 个数
// 故最终第0行应该是满足 (i + 0) % base == 0的数
// 第1行是 (i + 1) % base == 0的数
// 第 k 行是 (i + k) % base == 0的数
// k的范围是从 0 到 numRows - 1
7.整数反转
cpp 复制代码
class Solution {
public:
    int reverse(int x) {
        int res = 0;
        bool is_minus = (x < 0);
        while (x)
        {
            if (is_minus && res < (INT_MIN - x % 10) / 10) return 0;
            if (!is_minus && res > (INT_MAX - x % 10) / 10) return 0;
            res = res * 10 + x % 10;
            x /= 10;
        }

        return res;
    }
};
8. 字符串转换整数(atoi)
cpp 复制代码
class Solution {
public:
    int myAtoi(string s) {
        int res = 0;
        bool is_minus = false;
        for (int i = 0; i < s.size(); i++)
        {
            if (s[i] == ' ') continue;
            else if (s[i] == '+' || s[i] == '-')
            {
                is_minus = (s[i] == '-');
                if (i + 1 < s.size() && !isdigit(s[i + 1])) return res;
                else continue;
            }
            else if (isdigit(s[i]))
            {
                while (i < s.size() && isdigit(s[i]))
                {
                    int t = s[i] - '0';
                    if (is_minus && res > (INT_MAX - t) / 10) return INT_MIN;
                    if (!is_minus && res > (INT_MAX - t) / 10) return INT_MAX;
                    res = res * 10 + t;
                    i++;
                }
                if (is_minus) return -res;
                else return res;
            }
            else return res;
        }

        return res;
    }
};
9. 回文数
cpp 复制代码
class Solution {
public:
    bool isPalindrome(int x) {
        if (x < 0) return false;
        if (!x) return true;

        long long res = 0; // 翻转后有可能会溢出
        int tmp = x;
        while (tmp)
        {
            res = res * 10 + tmp % 10;
            tmp /= 10;
        }

        return res == x;
    }
};
11.盛水最多的容器(贪心(基于双指针来实现))
cpp 复制代码
class Solution {
public:
    int maxArea(vector<int>& height) {
        int res = 0;
        int i = 0, j = height.size() - 1;
        while (i < j)
        {
            res = max(res, min(height[i], height[j]) * (j - i));
            if (height[i] < height[j]) i++;
            else j--;
        }

        return res;
    }
};
12. 整数转罗马数字
cpp 复制代码
class Solution {
public:
    string intToRoman(int num) {
        unordered_map<int, string> dict{
            {1, "I"}, {4, "IV"}, {5, "V"}, {9, "IX"}, 
            {10, "X"}, {40, "XL"}, {50, "L"}, {90, "XC"}, 
            {100, "C"}, {400, "CD"}, {500, "D"}, {900, "CM"}, 
            {1000, "M"}
        };
        vector<int> base{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};

        string res = "";
        for (auto b : base)
        {
            if (!num) break;
            int cnt = num / b;
            num %= b;
            if (cnt)
            {
                for (int i = 0; i < cnt; i++)
                    res += dict[b];
            }
        }

        return res;
    }
};
13. 罗马数字转整数
cpp 复制代码
class Solution {
public:
    int romanToInt(string s) {
        // 从右往左遍历,定义一个哈希表表示大小关系,正常情况下右边小于等于左边;
        // 如果右边大于左边,说明出现了特殊规则
        // unordered_map<char, int> pri{{'I', 1}, {'V', 2}, {'X', 3}, {'L', 4}, \
        // {'C', 5}, {'D', 6}, {'M', 7}}; /* 后记:val表的功能可以替代pri,故可以不用专门定义pri */
        unordered_map<char, int> val{{'I', 1}, {'V', 5}, {'X', 10}, {'L', 50}, \
        {'C', 100}, {'D', 500}, {'M', 1000}};
        int res = 0;
        for (int i = s.size() - 1; i >= 0;)
        {
            if (i && val[s[i]] > val[s[i - 1]]) // e.g. IV
            {
                res += val[s[i]] - val[s[i - 1]];
                i -= 2;
            }
            else
            {
                res += val[s[i]];
                i--;
            }
        }

        return res;
    }
};
14. 最长公共前缀
cpp 复制代码
class Solution {
public:
    string longestCommonPrefix(vector<string>& strs) {
        string cp = strs[0];
        for (auto& s : strs)
            for (int i = 0; i < cp.size(); i++)
                if (cp[i] != s[i]) 
                {
                    cp.erase(cp.begin() + i, cp.end());
                    break;   
                }
     
        return cp;
    }
};
15. 三数之和(双指针 + 去重)

本题双指针算法的判定依据:

对于每一个固定的 i,当 j 增大时,k 必减小。

cpp 复制代码
class Solution {
public:
    vector<vector<int>> threeSum(vector<int>& nums) {
        sort(nums.begin(), nums.end()); // sort是双指针算法和去重的前提
        vector<vector<int>> res;

        for (int i = 0; i < nums.size() - 2; i++)
        {
            if (i && nums[i] == nums[i - 1]) continue; // 对i去重
            for (int j = i + 1, k = nums.size() - 1; j < nums.size() - 1; j++)
            {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue; // 对j去重
                while (j < k && nums[i] + nums[j] + nums[k] > 0) k--;
                if (j == k) break; // 注意:两指针不能重合!
                if (nums[i] + nums[j] + nums[k] == 0)
                    res.push_back({nums[i], nums[j], nums[k]});
            }
        }

        return res;
    }
};
16. 最接近的三数之和(双指针算法)
cpp 复制代码
class Solution {
public:
    int threeSumClosest(vector<int>& nums, int target) {
        sort(nums.begin(), nums.end());
        pair<int, int> res(INT_MAX, INT_MAX);
        for (int i = 0; i < nums.size() - 2; i++)
        {
            if (i && nums[i] == nums[i - 1]) continue;
            for (int j = i + 1, k = nums.size() - 1; j < k; j++)
            {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                while (j < k - 1 && nums[i] + nums[j] + nums[k - 1] >= target) k--;

                int sum = nums[i] + nums[j] + nums[k];
                res = min(res, make_pair(abs(sum - target), sum));
                if (j < k - 1)
                {
                    sum = nums[i] + nums[j] + nums[k - 1];
                    res = min(res, make_pair(target - sum, sum));
                }
            }
        }

        return res.second;
    }
};
17. 电话号码的字母组合(标准 DFS)
cpp 复制代码
class Solution {
public:
    string s;
    unordered_map<char, string> dict{
        {'2', "abc"},
        {'3', "def"},
        {'4', "ghi"},
        {'5', "jkl"},
        {'6', "mno"},
        {'7', "pqrs"},
        {'8', "tuv"},
        {'9', "wxyz"}
    };
    vector<string> res;

    vector<string> letterCombinations(string digits) {
        if (digits.empty()) return {};
        
        dfs(digits, 0);

        return res;
    }

    void dfs(string digits, int idx)
    {
        if (idx == digits.size())
        {
            res.push_back(s);
            return;
        }

        for (char c : dict[digits[idx]])
        {
            s += c;

            dfs(digits, idx + 1);

            s.pop_back();
        }
    }
};
18. 四数之和(双指针算法)
cpp 复制代码
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> res;
        if (nums.size() < 4) return res;

        sort(nums.begin(), nums.end());


        for (int i = 0; i < nums.size() - 3; i++)
        {
            if (i && nums[i] == nums[i - 1]) continue;
            for (int j = i + 1; j < nums.size() - 2; j++)
            {
                if (j > i + 1 && nums[j] == nums[j - 1]) continue;
                for (int k = j + 1, m = nums.size() - 1; k < m; k++)
                {
                    if (k > j + 1 && nums[k] == nums[k - 1]) continue;
                    while (k < m - 1 && (long)nums[i] + nums[j] + nums[k] + nums[m - 1] >= target) 
                        m--; // 注意:不加long会有数据溢出问题,下同

                    if ((long)nums[i] + nums[j] + nums[k] + nums[m] == target)
                        res.push_back({nums[i], nums[j], nums[k], nums[m]});
                }
            }
        }

        return res;
    }
};
19. 删除链表的倒数第 N 个结点(前后双指针寻找结点 + dummy 避免特判)
cpp 复制代码
/**
 * 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* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(-1, head);
        ListNode* left = dummy;
        ListNode* right = head;
        for (int i = 0; i < n; i++)
            right = right->next;

        while (right != nullptr)
        {
            left = left->next;
            right = right->next;
        }

        left->next = left->next->next;

        ListNode* res = dummy->next;
        delete dummy; // 删除哑节点,释放内存

        return res;
    }
};
20. 有效的括号(栈的简单应用)
cpp 复制代码
class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for (auto c : s)
        {
            if (c == '(' || c == '{' || c == '[')
                stk.push(c);
            else
            {
                // if (stk.empty() || (c == ')' && stk.top() != '(') || (c == '}' && stk.top() != '{') || (c == ']' && stk.top() != '['))
                if (stk.empty() || abs(stk.top() - c) > 2) // (){}[] = 40 41 123 125 91 93
                    return false;
                else 
                    stk.pop();
            }
        }

        return stk.empty();
    }
};
21. 合并两个有序链表(模拟归并排序中的二路归并操作)
cpp 复制代码
/**
 * 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* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dummy = new ListNode(-1);
        ListNode* p = dummy;
        ListNode* p1 = list1;
        ListNode* p2 = list2;
        while (p1 && p2)
        {
            if (p1->val <= p2->val)
            {
                p->next = p1;
                p1 = p1->next;
            }
            else
            {
                p->next = p2;
                p2 = p2->next;
            }
            
            p = p->next;
        }

        if (p1) p->next = p1;

        if (p2) p->next = p2;

        return dummy->next;
    }
};
22. 括号生成(DFS)
cpp 复制代码
class Solution {
public:
    string s;
    vector<string> res;

    vector<string> generateParenthesis(int n) {
        // 左括号用了i个,右括号用了j个,当i == j == n时,即可
        dfs(n, 0, 0);

        return res;
    }

    void dfs(int n, int left, int right)
    {
        if (left == n && right == n)
        {
            res.push_back(s);
            return;
        }

        // 左分支的条件如果满足,就转向左分支
        if (left < n)
        {
            s += '(';
            dfs(n, left + 1, right); // 在左分支里继续进行递归
            s.pop_back(); // 和回溯
        }

        //如果右分支的条件满足,就转向右分支
        if (right < left)
        {
            s += ')';
            dfs(n, left, right + 1); // 在右分支里继续进行递归
            s.pop_back(); // 和回溯
        }
    }
};
24. 两两交换链表中的节点(链表结点交换算法)
cpp 复制代码
/**
 * 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* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode(-1, head);
        ListNode* p = dummy;
        while (p->next && p->next->next)
        {
            ListNode* left = p->next; // 必须要先用两个临时结点把p->next
            ListNode* right = p->next->next; // 和p->next->next保存起来
            p->next = right;
            left->next = right->next;
            right->next = left;
            p = left; // <=> p = p->next->next;
        }

        return dummy->next;
    }
};
26. 删除有序数组中的重复项(经典双指针算法)
cpp 复制代码
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int k = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            if (!i || nums[i] != nums[i - 1])
                nums[k++] = nums[i];
        }

        return k;
    }
};
27. 移除元素(简单的双指针算法)

第一种实现方式:两个指针都从左往右走

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int k = 0; // k是新数组的长度,初始时为0
        for (int i = 0; i < nums.size(); i++)
            if (nums[i] != val)
                nums[k++] = nums[i];
        
        return k;
    }
};

第二种实现方式:两个指针从两头出发往中间走

cpp 复制代码
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int n = nums.size();
        int i = -1, j = n;
        while (i < j)
        {
            do i++; while (i < j && nums[i] != val);
            do j--; while (i < j && nums[j] == val);
            if (i < j) swap(nums[i], nums[j]);
        }

        return i;
    }
};
// 注:这种实现方式可以应对以下两种边界情况:
// (1)nums为空;
// (2)nums仅含1个元素
31. 下一个排列:
cpp 复制代码
class Solution {
public:
    void nextPermutation(vector<int>& nums) {
        int n = nums.size();

        // 从后往前找到第一对升序的元素
        int i = n - 1;
        while (i >= 1 && nums[i - 1] >= nums[i]) i--;

        if (i == 0) // 如果不存在升序元素对,
            reverse(nums.begin(), nums.end()); // 直接将降序翻转为升序
        else // 如果存在升序元素对,
        {
            int j = i; i--; // 此时nums[i - 1], nums[i]即为从后往前第一对升序元素

            // 再次从后往前,寻找第一个大于nums[i]的元素
            int k = n - 1;
            while (k > i && nums[i] >= nums[k]) k--;

            // 找到后将nums[i]和nums[k]交换
            swap(nums[i], nums[k]);

            // 此时[j, end)必降序,将其翻转为升序即可
            reverse(nums.begin() + j, nums.end());
        }
    }
};
32. 最长有效括号
cpp 复制代码
class Solution {
public:
    int longestValidParentheses(string s) {
        int res = 0;
        stack<int> stk;

        for (int i = 0, start = -1; i < s.size(); i++)
        {
            if (s[i] == '(') stk.push(i);
            else // 来右括号
            {
                if (!stk.empty()) // 来右括号且栈不空,说明栈顶有左括号
                {
                    stk.pop(); // 将左括号弹出,
                    if (!stk.empty()) // 如果弹出后栈顶仍有左括号,
                        res = max(res, i - stk.top()); // 则左括号的下一个位置到i的长度即为当前i对应的最大有效括号长度
                    else 
                        res = max(res, i - start); // 如果将栈顶左括号弹出后栈空,则从start + 1到i皆为有效长度
                }
                else // 来右括号且栈空
                {
                    start = i;
                }
            }
        }

        return res;
    }
};
33.搜索旋转排序数组:
cpp 复制代码
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int l = 0, r = nums.size() - 1;
        while (l <= r)
        {
            int mid = l + r >> 1;
            if (nums[mid] == target) return mid;

            // 我们要做的就是确定每次的mid到底是在左半段还是右半段中
            if (nums[l] <= nums[mid]) // 说明mid在左半段,则左半段可以二分
            {
                if (nums[l] <= target && target < nums[mid])
                    r = mid - 1;
                else
                    l = mid + 1;
            }
            else // 说明mid在右半段,则右半段可以二分
            {
                if (nums[mid] < target && target <= nums[r])
                    l = mid + 1;
                else
                    r = mid - 1;
            }
        }

        return -1;
    }
};
34.在排序数组中查找元素的第一个和最后一个位置:
cpp 复制代码
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        vector<int> res;
        if (nums.empty()) return {-1, -1};
        int l = 0, r = nums.size() - 1;
        while (l < r)
        {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        if (nums[l] != target) return {-1, -1};
        else
        {
            res.push_back(l);

            int l = 0, r = nums.size() - 1;
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                if (nums[mid] <= target) l = mid;
                else r = mid - 1;
            }

            res.push_back(l);
        }

        return res;
    }
};
35.搜索插入位置:
cpp 复制代码
// 找到从前往后第一个大于等于target的位置
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int l = 0, r = nums.size(); // 注意:此处不再是nums.size() - 1
        while (l < r)
        {
            int mid = l + r >> 1;
            if (nums[mid] >= target) r = mid;
            else l = mid + 1;
        }

        return l;
    }
};
36.有效的数独:
cpp 复制代码
class Solution {
public:
    bool row[9][9], col[9][9], block[3][3][9];

    bool isValidSudoku(vector<vector<char>>& board) {
        memset(row, 0, sizeof row);
        memset(col, 0, sizeof col);
        memset(block, 0, sizeof block);

        for (int i = 0; i < 9; i++)
            for (int j = 0; j < 9; j++)
                if (board[i][j] != '.')
                {
                    int t = board[i][j] - '1';
                    if (row[i][t] || col[j][t] || block[i / 3][j / 3][t])
                        return false;
                    else
                        row[i][t] = col[j][t] = block[i / 3][j / 3][t] = true;
                }
        
        return true;
    }
};
37.解数独:
cpp 复制代码
class Solution {
public:
    bool row[9][9], col[9][9], block[3][3][9];

    void solveSudoku(vector<vector<char>>& board) {
        memset(row, 0, sizeof row);
        memset(col, 0, sizeof col);
        memset(block, 0, sizeof block);

        int n = board.size(), m = board[0].size();

        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                if (board[i][j] != '.')
                {
                    int t = board[i][j] - '1';
                    row[i][t] = col[j][t] = block[i / 3][j / 3][t] = true;
                }

        dfs(board, 0, 0);
    }

    bool dfs(vector<vector<char>>& board, int x, int y)
    {
        int n = board.size(), m = board[0].size();

        if (y == m) { y = 0; x++; }
        if (x == n) return true;

        if (board[x][y] != '.') return dfs(board, x, y + 1);
        
        for (int num = 0; num < 9; num++)
        {
            if (!row[x][num] && !col[y][num] && !block[x / 3][y / 3][num])
            {
                board[x][y] = num + '1';
                row[x][num] = col[y][num] = block[x / 3][y / 3][num] = true;
                if (dfs(board, x, y + 1)) return true;
                board[x][y] = '.';
                row[x][num] = col[y][num] = block[x / 3][y / 3][num] = false;
            }
        }

        return false;
    }
};
38.外观数列:
cpp 复制代码
class Solution {
public:
    string countAndSay(int n) {
        string s = "1";

        for (int i = 0; i < n - 1; i++)
        {
            string tmp = "";
            for (int j = 0; j < s.size();)
            {
                int k = j + 1;
                while (k < s.size() && s[j] == s[k]) k++;
                tmp += to_string(k - j) + s[j];
                j = k;
            }

            s = tmp;
        }

        return s;
    }
};
39.组合总和:
  • 第一种 DFS:逐数字枚举
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(candidates, 0, target);

        return res;    
    }

    void dfs(vector<int>& candidates, int u, int target)
    {
        if (target == 0)
        {
            res.push_back(path);
            return;
        } // 注意:此if与后面if的顺序不能颠倒!

        if (u == candidates.size()) return;

        // skip current position
        dfs(candidates, u + 1, target);

        // choose current position
        if (target - candidates[u] >= 0)
        {
            path.push_back(candidates[u]);
            dfs(candidates, u, target - candidates[u]);
            path.pop_back();
        }
    }
};
  • 第二种 DFS:按第 i 个数选多少个来搜索
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;

    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        dfs(candidates, 0, target);

        return res;    
    }

    void dfs(vector<int>& candidates, int u, int target)
    {
        if (target == 0)
        {
            res.push_back(path);
            return;
        } // 注意:此if与后面if的顺序不能颠倒!

        if (u == candidates.size()) return;

        for (int i = 0; i * candidates[u] <= target; i++)
        {
            dfs(candidates, u + 1, target - i * candidates[u]);
            path.push_back(candidates[u]);
        }

        for (int i = 0; i * candidates[u] <= target; i++)
            path.pop_back();
    }
};
40.组合总和:
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;

    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        dfs(candidates, 0, target);

        return res;    
    }

    void dfs(vector<int>& candidates, int idx, int target)
    {
        if (target == 0)
        {
            res.push_back(path);
            return;
        }

        if (idx == candidates.size()) return;

        int prev = -1;
        for (int i = idx; i < candidates.size(); i++)
        {
            if (prev != candidates[i] && candidates[i] <= target)
            {
                prev = candidates[i];
                path.push_back(candidates[i]);

                dfs(candidates, i + 1, target - candidates[i]);

                path.pop_back();
            }
        }
    }
};
41.缺失的第一个正数:
cpp 复制代码
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        for (int i = 0; i < n; i++)
        {
            while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i])
                swap(nums[nums[i] - 1], nums[i]);
        }

        for (int i = 0; i < n; i++)
        {
            if (nums[i] != i + 1)
                return i + 1;
        }

        return n + 1;
    }
};
42.接雨水:
cpp 复制代码
class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        int l = 0, r = height.size() - 1;
        int lmax = 0, rmax = 0;
        while (l < r)
        {
            lmax = max(lmax, height[l]);
            rmax = max(rmax, height[r]);
            if (lmax <= rmax)
            {
                ans += lmax - height[l];
                l++;
            }
            else
            {
                ans += rmax - height[r];
                r--;
            }
        }

        return ans;
    }
};
43.字符串相乘:
cpp 复制代码
class Solution {
public:
    string multiply(string num1, string num2) {
        int n = num1.size(), m = num2.size();
        vector<int> A, B;

        for (int i = n - 1; i >= 0; i--) A.push_back(num1[i] - '0');
        for (int i = m - 1; i >= 0; i--) B.push_back(num2[i] - '0');

        vector<int> C(n + m);
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++)
                C[i + j] += A[i] * B[j];
        
        // 集中处理进位
        int t = 0;
        for (int i = 0; i < C.size(); i++)
        {
            t += C[i];
            C[i] = t % 10;
            t /= 10;
        }

        while (C.size() > 1 && C.back() == 0) C.pop_back();

        string res;
        for (int i = C.size() - 1; i >= 0; i--) res += C[i] + '0';

        return res;
    }
};
44.通配符匹配:
cpp 复制代码
class Solution {
public:
    bool isMatch(string s, string p) {
        int n = s.size(), m = p.size();
        s = ' ' + s, p = ' ' + p;
        vector<vector<bool>> f(n + 1, vector<bool>(m + 1));
        f[0][0] = true;

        for (int i = 0; i <= n; i++) // i必须从0开始,因为f[0][j]是有意义的
            for (int j = 1; j <= m; j++) // j可以从1开始,因为f[i][0]必为false
                if (p[j] == '*')
                    f[i][j] = f[i][j - 1] || (i && f[i - 1][j]);
                else
                    f[i][j] = (s[i] == p[j] || p[j] == '?') && (i && f[i - 1][j - 1]);
        
        return f[n][m];
    }
};
45.跳跃游戏 II:
cpp 复制代码
class Solution {
public:
    int jump(vector<int>& nums) {
        int n = nums.size();
        vector<int> f(n);

        for (int i = 1, j = 0; i < n; i++)
        {
            while (j + nums[j] < i) j++;
            f[i] = f[j] + 1;
        }

        return f[n - 1];
    }
};
46.全排列:
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    bool st[10];
    
    vector<vector<int>> permute(vector<int>& nums) {
        dfs(nums, 0);

        return res;
    }

    void dfs(vector<int>& nums, int u)
    {
        if (u == nums.size())
        {
            res.push_back(path);
            return;
        }

        for (int i = 0; i < nums.size(); i++)
        {
            if (!st[i])
            {
                path.push_back(nums[i]);
                st[i] = true;

                dfs(nums, u + 1);

                path.pop_back();
                st[i] = false;
            }
        }
    }
};
47.全排列 II:
cpp 复制代码
class Solution {
public:
    vector<int> path;
    vector<vector<int>> res;
    bool st[10];

    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<int> tmp = nums;
        sort(tmp.begin(), tmp.end());

        dfs(tmp, 0);

        return res;
    }

    void dfs(vector<int>& nums, int u)
    {
        if (u == nums.size())
        {
            res.push_back(path);
            return;
        }

        int prev = -11;
        for (int i = 0; i < nums.size(); i++)
        {
            if (prev != nums[i] && !st[i])
            {
                prev = nums[i];
                path.push_back(nums[i]);
                st[i] = true;

                dfs(nums, u + 1);

                path.pop_back();
                st[i] = false;
            }
        }
    }
};
48.旋转图像:
cpp 复制代码
class Solution {
public:
    void rotate(vector<vector<int>>& matrix) {
        /* 两次对称即可:先沿反对角线对称,再沿中轴左右对称*/
        int n = matrix.size();
        for (int i = 0; i < n; i++)
            for (int j = 0; j < i; j++)
                swap(matrix[i][j], matrix[j][i]);
        
        for (int i = 0; i < n; i++)
            for (int j = 0; j < n / 2; j++)
                swap(matrix[i][j], matrix[i][n - 1 - j]);
    }
};
49.字母异位词分组:
cpp 复制代码
class Solution {
public:
    vector<vector<string>> groupAnagrams(vector<string>& strs) {
        unordered_map<string, vector<string>> map;
        for (auto& str : strs)
        {
            string tmp = str;
            sort(tmp.begin(), tmp.end());
            map[tmp].push_back(str);
        }

        vector<vector<string>> res;
        for (auto& item : map) res.push_back(item.second);

        return res;
    }
};
50.Pow(x, n):
cpp 复制代码
class Solution {
public:
    double myPow(double x, int n) {
        typedef long long LL;
        bool is_minus = n < 0;
        double res = 1;
        for (LL k = abs(LL(n)); k; k >>= 1)
        {
            if (k & 1) res *= x;
            x *= x;
        }
        if (is_minus) res = 1 / res;
        return res;
    }
};
51. N 皇后(DFS)
cpp 复制代码
class Solution {
public:
    bool col[10], dg[20], udg[20];
    vector<vector<string>> res;

    vector<vector<string>> solveNQueens(int n) {
        // 初始化棋盘
        vector<string> g;
        for (int i = 0; i < n; i++)
            g.push_back(string(n, '.'));

        dfs(0, n, g);

        return res;
    }

    void dfs(int u, int n, vector<string>& g)
    {
        if (u == n)
        {
            res.push_back(g);
            return;
        }

        for (int i = 0; i < n; i++)
        {
            if (!col[i] && !dg[u - i + n] && !udg[u + i])
            {
                g[u][i] = 'Q';
                col[i] = dg[u - i + n] = udg[u + i] = true;

                dfs(u + 1, n, g);

                g[u][i] = '.';
                col[i] = dg[u - i + n] = udg[u + i] = false;
            }
        }
    }
};
52. N 皇后 II(DFS,和 51 题完全一样,只是不需要记录方案是什么)
cpp 复制代码
class Solution {
public:
    bool col[10], dg[20], udg[20];
    int res = 0;

    int totalNQueens(int n) {
        dfs(0, n);

        return res;
    }

    void dfs(int u, int n)
    {
        if (u == n)
        {
            res++;
            return;
        }

        for (int i = 0; i < n; i++)
        {
            if (!col[i] && !dg[u - i + n] && !udg[u + i])
            {
                col[i] = dg[u - i + n] = udg[u + i] = true;

                dfs(u + 1, n);

                col[i] = dg[u - i + n] = udg[u + i] = false;
            }
        }
    }
};
53. 最大子数组和(动态规划)

从动态规划的角度去理解:(更推荐)

cpp 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res = INT_MIN;
        for (int i = 0, last = 0; i < nums.size(); i++)
        {
            last = nums[i] + max(last, 0);
            res = max(res, last);
        }

        return res;
    }
};

// 动态规划:
// f[i] 表示所有以nums[i]结尾的连续子数组的最大和
// f[i] = max(nums[i], f[i - 1] + nums[i]) = nums[i] + max(f[i - 1], 0);
// 由于只用到了两个状态,因此可以用一个变量保存f[i - 1],这样空间复杂度可以优化至O(1)

从贪心的角度去理解:

cpp 复制代码
class Solution {
public:
    int maxSubArray(vector<int>& nums) {
        int res = -1e5, sum = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            sum += nums[i];
            if (sum < nums[i]) sum = nums[i];
            res = max(res, sum);
   
        }

        return res;
    }
};
// 贪心:
// 从左到右扫描,
54. 螺旋矩阵

方法一:(迭代)利用方向数组(推荐)

cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
        int m = matrix.size(), n = matrix[0].size();
        vector<vector<bool>> st(m, vector<bool>(n));
        for (int i = 0, x = 0, y = 0, d = 0; i < m * n; i++)
        {
            // 先判断上一步有没有走对,如果没有走对,就纠正上一步并让其走对
            if (x < 0 || x >= m || y < 0 || y >= n || st[x][y])
            {
                x -= dx[d], y -= dy[d];
                d = (d + 1) % 4;
                x += dx[d], y += dy[d];
            }
            
            // 如果走对的话就记录
            res.push_back(matrix[x][y]);
            st[x][y] = true;
            x += dx[d], y += dy[d]; // 并继续往前走
        }

        return res;
    }
};

方法二:(DFS)模拟

cpp 复制代码
class Solution {
public:
    vector<int> res;
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int m = matrix.size(), n = matrix[0].size();
        dfs(matrix, 0, 0, m - 1, n - 1); // 传入的是左上角和右下角的坐标(x1, y1)和(x2, y2)

        return res;
    }

    void dfs(vector<vector<int>>& matrix, int x1, int y1, int x2, int y2)
    {
        if (x1 > x2 || y1 > y2) return;

        int i = x1, j = y1;
        while (j <= y2)
        {
            res.push_back(matrix[i][j]);
            j++;
        }

        j--,i++;
        while (i <= x2)
        {
            res.push_back(matrix[i][j]);
            i++;
        }

        i--, j--;
        while (i > x1 && j >= y1) // i > x1的目的是防止出现回退,如第2个测试用例
        {
            res.push_back(matrix[i][j]);
            j--;
        }

        j++, i--;
        while (j < y2 && i > x1) // j < y2的目的也是防止出现回退,例如[[7], [9], [6]]
        {
            res.push_back(matrix[i][j]);
            i--;
        }

        dfs(matrix, x1 + 1, y1 + 1, x2 - 1, y2 - 1);
    }
};
55.跳跃游戏:
cpp 复制代码
class Solution {
public:
    bool canJump(vector<int>& nums) {
        for (int i = 0, j = 0; i < nums.size(); i++)
        {
            if (j < i) return false;
            j = max(j, i + nums[i]);
        }

        return true;
    }
};
56. 区间合并(模板:区间合并)
cpp 复制代码
class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        vector<vector<int>> res;
        sort(intervals.begin(), intervals.end());
        int st = -1, ed = -1;
        for (auto seg : intervals)
        {
            if (ed < seg[0])
            {
                if (st != -1) res.push_back({st, ed});
                st = seg[0], ed = seg[1];
            }
            else ed = max(ed, seg[1]);
        }

        if (st != -1) res.push_back({st, ed});

        return res;
    }
};
57. 插入区间

方法一:先用二分查找找到插入位置,将新区间插入,再调用标准的区间合并算法

cpp 复制代码
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        // 先二分找到插入位置
        int l = 0, r = intervals.size();
        while (l < r)
        {
            int mid = l + r >> 1;
            if (intervals[mid][0] >= newInterval[0]) r = mid;
            else l = mid + 1;
        }

        intervals.insert(intervals.begin() + l, newInterval);

        // 调用区间合并模板
        int st = -1, ed = -1;
        vector<vector<int>> res;
        for (int i = 0; i < intervals.size(); i++)
        {
            if (ed < intervals[i][0])
            {
                if (st != -1) res.push_back({st, ed});
                st = intervals[i][0], ed = intervals[i][1];
            }
            else ed = max(ed, intervals[i][1]);
        }
        if (st != -1) res.push_back({st, ed});

        return res;
    }
};

方法二:(Y总做法)模拟

cpp 复制代码
class Solution {
public:
    vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
        vector<vector<int>> res;

        // 首先寻找左侧没有交叠的区间
        int k = 0;
        while (k < intervals.size() && intervals[k][1] < newInterval[0])
        {
            res.push_back(intervals[k]);
            k++;
        }

        // 对中间有交叠的区间进行合并
        if (k < intervals.size()) // 这一条件可以处理intervals为空的情况
        {
            newInterval[0] = min(intervals[k][0], newInterval[0]);
            while (k < intervals.size() && intervals[k][0] <= newInterval[1])
            {
                newInterval[1] = max(intervals[k][1], newInterval[1]);
                k++;
            }
        }
        res.push_back(newInterval);


        // 右侧没有交叠的区间也照抄
        while (k < intervals.size())
        {
            res.push_back(intervals[k]);
            k++;
        }

        return res;
    }
};
58. 最后一个单词的长度(模拟题)
cpp 复制代码
class Solution {
public:
    int lengthOfLastWord(string s) {
        int k = s.size() - 1;
        while (s[k] == ' ') k--;
        int j = k;
        while (j >= 0 && s[j] != ' ') j--;

        return k - j;
    }
};
59. 螺旋矩阵 II(方向数组的简单应用)
cpp 复制代码
class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> res(n, vector<int>(n));
        int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};
        for (int i = 1, x = 0, y = 0, d = 0; i <= n * n; i++)
        {
            if (x < 0 || x >= n || y < 0 || y >= n || res[x][y] != 0)
            {
                x -= dx[d], y -= dy[d];
                d = (d + 1) % 4;
                x += dx[d], y += dy[d];
            }

            res[x][y] = i;
            x += dx[d], y += dy[d];
        }

        return res;
    }
};
相关推荐
XH华3 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生3 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_4 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子4 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
菜鸡中的奋斗鸡→挣扎鸡4 小时前
滑动窗口 + 算法复习
数据结构·算法
Lenyiin4 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码4 小时前
Cmd命令大全(万字详细版)
python·算法·小程序
scan7244 小时前
LILAC采样算法
人工智能·算法·机器学习
菌菌的快乐生活5 小时前
理解支持向量机
算法·机器学习·支持向量机
大山同学5 小时前
第三章线性判别函数(二)
线性代数·算法·机器学习