diff --git a/README.md b/README.md index 05888b3b..55d52266 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ * [哈希表:map等候多时了](https://mp.weixin.qq.com/s/uVAtjOHSeqymV8FeQbliJQ) * [哈希表:其实需要哈希的地方都能找到map的身影](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA) * [哈希表:这道题目我做过?](https://mp.weixin.qq.com/s/sYZIR4dFBrw_lr3eJJnteQ) +* [哈希表:解决了两数之和,那么能解决三数之和么?](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A) * 精选链表相关的面试题 * 精选字符串相关的面试题 * 精选栈与队列相关的面试题 @@ -354,11 +355,13 @@ int countNodes(TreeNode* root) { |[0027.移除元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0027.移除元素.md) |数组 |简单| **暴力** **双指针/快慢指针/双指针**| |[0028.实现strStr()](https://github.com/youngyangyang04/leetcode/blob/master/problems/0028.实现strStr().md) |字符串 |简单| **KMP** | |[0035.搜索插入位置](https://github.com/youngyangyang04/leetcode/blob/master/problems/0035.搜索插入位置.md) |数组 |简单| **暴力** **二分**| +|[0037.解数独](https://github.com/youngyangyang04/leetcode/blob/master/problems/0037.解数独.md) |回溯 |困难| **回溯**| |[0039.组合总和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0039.组合总和.md) |数组/回溯 |中等| **回溯**| |[0040.组合总和II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0040.组合总和II.md) |数组/回溯 |中等| **回溯**| |[0046.全排列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0046.全排列.md) |回溯|中等| **回溯**| |[0047.全排列II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0047.全排列II.md) |回溯|中等| **回溯**| -|[0051.N皇后](https://github.com/youngyangyang04/leetcode/blob/master/problems/0051.N皇后.md) |回溯|中等| **回溯**| +|[0051.N皇后](https://github.com/youngyangyang04/leetcode/blob/master/problems/0051.N皇后.md) |回溯|困难| **回溯**| +|[0052.N皇后II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0052.N皇后II.md) |回溯|困难| **回溯**| |[0053.最大子序和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0053.最大子序和.md) |数组 |简单|**暴力** **贪心** 动态规划 分治| |[0059.螺旋矩阵II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0059.螺旋矩阵II.md) |数组 |中等|**模拟**| |[0077.组合](https://github.com/youngyangyang04/leetcode/blob/master/problems/0077.组合.md) |回溯 |中等|**回溯**| @@ -400,10 +403,11 @@ int countNodes(TreeNode* root) { |[0349.两个数组的交集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0349.两个数组的交集.md) |哈希表 |简单|**哈希**| |[0350.两个数组的交集II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0350.两个数组的交集II.md) |哈希表 |简单|**哈希**| |[0383.赎金信](https://github.com/youngyangyang04/leetcode/blob/master/problems/0383.赎金信.md) |数组 |简单|**暴力** **字典计数** **哈希**| -|[0450.删除二叉搜索树中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0450.删除二叉搜索树中的节点.md) |树 |中等|**递归**| |[0434.字符串中的单词数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0434.字符串中的单词数.md) |字符串 |简单|**模拟**| +|[0450.删除二叉搜索树中的节点](https://github.com/youngyangyang04/leetcode/blob/master/problems/0450.删除二叉搜索树中的节点.md) |树 |中等|**递归**| |[0454.四数相加II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0454.四数相加II.md) |哈希表 |中等| **哈希**| |[0459.重复的子字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0459.重复的子字符串.md) |字符创 |简单| **KMP**| +|[0491.递增子序列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0491.递增子序列.md) |深度优先搜索 |中等|**深度优先搜索/回溯**| |[0541.反转字符串II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0541.反转字符串II.md) |字符串 |简单| **模拟**| |[0575.分糖果](https://github.com/youngyangyang04/leetcode/blob/master/problems/0575.分糖果.md) |哈希表 |简单|**哈希**| |[0617.合并二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0617.合并二叉树.md) |树 |简单|**递归** **迭代**| diff --git a/pics/459.重复的子字符串_1.png b/pics/459.重复的子字符串_1.png new file mode 100644 index 00000000..2feddd6b Binary files /dev/null and b/pics/459.重复的子字符串_1.png differ diff --git a/pics/491. 递增子序列1.jpg b/pics/491. 递增子序列1.jpg new file mode 100644 index 00000000..df8d33fa Binary files /dev/null and b/pics/491. 递增子序列1.jpg differ diff --git a/pics/面试题02.07.链表相交_1.png b/pics/面试题02.07.链表相交_1.png new file mode 100644 index 00000000..678311d1 Binary files /dev/null and b/pics/面试题02.07.链表相交_1.png differ diff --git a/pics/面试题02.07.链表相交_2.png b/pics/面试题02.07.链表相交_2.png new file mode 100644 index 00000000..97881bec Binary files /dev/null and b/pics/面试题02.07.链表相交_2.png differ diff --git a/problems/0018.四数之和.md b/problems/0018.四数之和.md index 2384ece1..ac1f699e 100644 --- a/problems/0018.四数之和.md +++ b/problems/0018.四数之和.md @@ -1,19 +1,48 @@ - -## 题目地址 +# 题目地址 https://leetcode-cn.com/problems/4sum/ -## 思路 +> 一样的道理,能解决四数之和 +> 那么五数之和、六数之和、N数之和呢? -四数之和,和[三数之和](https://github.com/youngyangyang04/leetcode/blob/master/problems/0015.三数之和.md)是一个思路,都是使用双指针法,但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。 +# 第18题. 四数之和 -三数之和 我们是一层for循环,然后循环内有left和right下表作为指针,四数之和,就可以是两层for循环,依然是循环内有left和right下表作为指针,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。 +题意:给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。 -动画如下: - +**注意:** +答案中不可以包含重复的四元组。 -## C++代码 +示例: +给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。 +满足要求的四元组集合为: +[ + [-1, 0, 0, 1], + [-2, -1, 1, 2], + [-2, 0, 0, 2] +] + +# 思路 + +四数之和,和[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)是一个思路,都是使用双指针法, 基本解法就是在[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A) 的基础上再套一层for循环。 + +但是有一些细节需要注意,例如: 不要判断`nums[k] > target` 就返回了,三数之和 可以通过 `nums[i] > 0` 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。(大家亲自写代码就能感受出来) + +[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)的双指针解法是一层for循环num[i]为确定值,然后循环内有left和right下表作为双指针,找到nums[i] + nums[left] + nums[right] == 0。 + +四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下表作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况,三数之和的时间复杂度是O(n^2),四数之和的时间复杂度是O(n^3) 。 + +那么一样的道理,五数之和、六数之和等等都采用这种解法。 + +对于[三数之和](https://mp.weixin.qq.com/s/r5cgZFu0tv4grBAexdcd8A)双指针法就是将原本暴力O(n^3)的解法,降为O(n^2)的解法,四数之和的双指针解法就是将原本暴力O(n^4)的解法,降为O(n^3)的解法。 + +之前我们讲过哈希表的经典题目:[四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA),相对于本题简单很多,因为本题是要求在一个集合中找出四个数相加等于target,同时四元组不能重复。 + +而[四数相加II](https://mp.weixin.qq.com/s/Ue8pKKU5hw_m-jPgwlHcbA)是四个独立的数组,只要找到A[i] + B[j] + C[k] + D[l] = 0就可以,不用考虑有重复的四个元素相加等于0的情况,所以相对于本题还是简单了不少! + +大家解决一下这两道题目就能感受出来难度的差异。 + +# C++代码 ``` class Solution { public: @@ -21,7 +50,7 @@ public: vector> result; sort(nums.begin(), nums.end()); for (int k = 0; k < nums.size(); k++) { - // 这中剪枝是错误的,这道题目target 是任意值 + // 这种剪枝是错误的,这道题目target 是任意值 // if (nums[k] > target) { // return result; // } diff --git a/problems/0037.解数独.md b/problems/0037.解数独.md new file mode 100644 index 00000000..d7694445 --- /dev/null +++ b/problems/0037.解数独.md @@ -0,0 +1,60 @@ +# 题目地址 + +https://leetcode-cn.com/problems/sudoku-solver/ + +# 思路 + +这道题目和之前递归的方式都不一样,这里相当于两层递归,之前的都是一层递归 + +# C++代码 + +``` +class Solution { +private: +bool backtracking(vector>& board) { + for (int i = 0; i < board.size(); i++) { + for (int j = 0; j < board[0].size(); j++) { + if (board[i][j] != '.') continue; + for (char k = '1'; k <= '9'; k++) { + if (isValid(i, j, k, board)) { + board[i][j] = k; + if (backtracking(board)) return true; + board[i][j] = '.'; + } + } + return false; + } + } + return true; + +} +bool isValid(int row, int col, char val, vector>& board) { + + for (int i = 0; i < 9; i++) { + if (board[row][i] == val) { + return false; + } + } + for (int j = 0; j < 9; j++) { + if (board[j][col] == val) { + return false; + } + + } + int startRow = (row / 3) * 3; + int startCol = (col / 3) * 3; + for (int i = startRow; i < startRow + 3; i++) { + for (int j = startCol; j < startCol + 3; j++) { + if (board[i][j] == val ) { + return false; + } + } + } + return true; +} +public: + void solveSudoku(vector>& board) { + backtracking(board); + } +}; +``` diff --git a/problems/0052.N皇后II.md b/problems/0052.N皇后II.md new file mode 100644 index 00000000..8d15611b --- /dev/null +++ b/problems/0052.N皇后II.md @@ -0,0 +1,86 @@ +# 题目链接 +https://leetcode-cn.com/problems/n-queens-ii/ + +# 第51题. N皇后 + +n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。 + +上图为 8 皇后问题的一种解法。 +![51n皇后](https://img-blog.csdnimg.cn/20200821152118456.png) + +给定一个整数 n,返回 n 皇后不同的解决方案的数量。 + +示例: + +输入: 4 +输出: 2 +解释: 4 皇后问题存在如下两个不同的解法。 +[ + [".Q..",  // 解法 1 +  "...Q", +  "Q...", +  "..Q."], + + ["..Q.",  // 解法 2 +  "Q...", +  "...Q", +  ".Q.."] +] +# 思路 + +这道题目和 51.N皇后 基本没有区别 + +# C++代码 + +``` +class Solution { +private: +int count = 0; +void backtracking(int n, int row, vector& chessboard, vector>& result) { + if (row == n) { + count++; + return; + } + for (int col = 0; col < n; col++) { + if (isValid(row, col, chessboard, n)) { + chessboard[row][col] = 'Q'; + backtracking(n, row + 1, chessboard, result); + chessboard[row][col] = '.'; + } + } +} +bool isValid(int row, int col, vector& chessboard, int n) { + int count = 0; + // 检查列 + for (int i = 0; i < row; i++) { // 这是一个剪枝 + if (chessboard[i][col] == 'Q') { + return false; + } + } + // 检查 45度角是否有皇后 + for (int i = row - 1, j = col - 1; i >=0 && j >= 0; i--, j--) { + if (chessboard[i][j] == 'Q') { + return false; + } + } + // 检查 135度角是否有皇后 + for(int i = row - 1, j = col + 1; i >= 0 && j < n; i--, j++) { + if (chessboard[i][j] == 'Q') { + return false; + } + } + return true; +} + +public: + int totalNQueens(int n) { + std::vector chessboard(n, std::string(n, '.')); + vector> result; + backtracking(n, 0, chessboard, result); + return count; + + } +}; +``` + +> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 diff --git a/problems/0053.最大子序和.md b/problems/0053.最大子序和.md index 33f10c2a..582b6d68 100644 --- a/problems/0053.最大子序和.md +++ b/problems/0053.最大子序和.md @@ -53,5 +53,5 @@ public: } }; ``` -> 更过算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 +> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 diff --git a/problems/0459.重复的子字符串.md b/problems/0459.重复的子字符串.md index 2f482611..4c9be6e8 100644 --- a/problems/0459.重复的子字符串.md +++ b/problems/0459.重复的子字符串.md @@ -4,6 +4,22 @@ https://leetcode-cn.com/problems/repeated-substring-pattern/ ## 思路 +这是一道标准的KMP的题目。 + +使用KMP算法,next 数组记录的就是最长公共前后缀, 最后如果 next[len - 1] != -1,说明此时有最长公共前后缀(就是字符串里的前缀子串和后缀子串相同的最长长度),同时如果len % (len - (next[len - 1] + 1)) == 0 ,则说明 (数组长度-最长公共前后缀的长度) 正好可以被 数组的长度整除,说明有重复的子字符串。 + +**强烈建议大家把next数组打印出来,看看next数组里的规律,有助于理解KMP算法** + +如图: + + + +此时next[len - 1] = 7,next[len - 1] + 1 = 8,8就是此时 字符串asdfasdfasdf的最长公共前后缀的长度。 + + +(len - (next[len - 1] + 1)) 也就是: 12(字符串的长度) - 8(最长公共前后缀的长度) = 4, 4正好可以被 12(字符串的长度) 整除,所以说明有重复的子字符串。 + +代码如下: ## C++代码 diff --git a/problems/0491.递增子序列.md b/problems/0491.递增子序列.md new file mode 100644 index 00000000..1aa8efc8 --- /dev/null +++ b/problems/0491.递增子序列.md @@ -0,0 +1,42 @@ +## 题目地址 + +## 思路 + +这道题可以说是深度优先搜索,也可以说是回溯法,其实我更倾向于说它用回溯法,因为本题和[90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/)非常像,差别就是[90. 子集 II](https://leetcode-cn.com/problems/subsets-ii/)可以通过排序,在加一个标记数组来达到去重的目的。 + +去重复的逻辑,关键在于子序列的末尾,如果子序列的末尾重复出现一个元素,那么该序列就是重复的了,如图所示: + + + +在递归的过程中 `if ((subseq.empty() || nums[i] >= subseq.back()) && uset.find(nums[i]) == uset.end())` 这个判断条件一定要想清楚, 如果子序列为空或者nums[i]>=子序列尾部数值,**同时** 这个nums[i] 不能出现过, 因为一旦出现过就 是一个重复的递增子序列了。 + +## C++代码 + +``` +class Solution { +private: +void backtracking(vector& nums, vector>& result, vector& subseq, int startIndex) { + if (subseq.size() > 1) { + result.push_back(subseq); + // 注意这里不要加return,因为要取所有的可能 + } + unordered_set uset; // 使用set来对尾部元素进行去重 + for (int i = startIndex; i < nums.size(); i++) { + if ((subseq.empty() || nums[i] >= subseq.back()) + && uset.find(nums[i]) == uset.end()) { + subseq.push_back(nums[i]); + backtracking(nums, result, subseq, i + 1); + subseq.pop_back(); + uset.insert(nums[i]);//在回溯的时候,记录这个元素用过了,后面不能再用了 + } + } +} +public: + vector> findSubsequences(vector& nums) { + vector> result; + vector subseq; + backtracking(nums, result, subseq, 0); + return result; + } +}; +``` diff --git a/problems/面试题02.07.链表相交.md b/problems/面试题02.07.链表相交.md index dfdb4e1a..3c30c0dc 100644 --- a/problems/面试题02.07.链表相交.md +++ b/problems/面试题02.07.链表相交.md @@ -7,6 +7,20 @@ https://leetcode-cn.com/problems/intersection-of-two-linked-lists-lcci/ 简单来说,就是求两个链表交点节点的**指针**。 这里同学们要注意,交点不是数值相等,而是指针相等。 +为了方便举例,假设节点元素数值相等,则节点指针相等。 + +看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点: + + + +我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图: + + + +此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到焦点。 + +否则循环退出返回空指针。 + ## C++代码 ```