diff --git a/README.md b/README.md index efb9572b..b93b9bb1 100644 --- a/README.md +++ b/README.md @@ -349,10 +349,14 @@ 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) |数组 |简单| **暴力** **二分**| +|[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) |回溯|中等| **回溯**| |[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) |回溯 |中等|**回溯**| +|[0078.子集](https://github.com/youngyangyang04/leetcode/blob/master/problems/0078.子集.md) |回溯/数组 |中等|**回溯**| |[0083.删除排序链表中的重复元素](https://github.com/youngyangyang04/leetcode/blob/master/problems/0083.删除排序链表中的重复元素.md) |链表 |简单|**模拟**| |[0093.复原IP地址](https://github.com/youngyangyang04/leetcode/blob/master/problems/0093.复原IP地址) |回溯 |中等|**回溯**| |[0094.二叉树的中序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0094.二叉树的中序遍历.md) |树 |中等|**递归** **迭代/栈**| @@ -375,6 +379,7 @@ int countNodes(TreeNode* root) { |[0205.同构字符串](https://github.com/youngyangyang04/leetcode/blob/master/problems/0205.同构字符串.md) |哈希表 |简单| **哈希**| |[0206.翻转链表](https://github.com/youngyangyang04/leetcode/blob/master/problems/0206.翻转链表.md) |链表 |简单| **双指针法** **递归**| |[0209.长度最小的子数组](https://github.com/youngyangyang04/leetcode/blob/master/problems/0209.长度最小的子数组.md) |数组 |中等| **暴力** **滑动窗口**| +|[0216.组合总和III](https://github.com/youngyangyang04/leetcode/blob/master/problems/0216.组合总和III.md) |数组/回溯 |中等| **回溯**| |[0219.存在重复元素II](https://github.com/youngyangyang04/leetcode/blob/master/problems/0219.存在重复元素II.md) | 哈希表 |简单| **哈希** | |[0222.完全二叉树的节点个数](https://github.com/youngyangyang04/leetcode/blob/master/problems/0222.完全二叉树的节点个数.md) | 树 |简单| **递归** | |[0225.用队列实现栈](https://github.com/youngyangyang04/leetcode/blob/master/problems/0225.用队列实现栈.md) | 队列 |简单| **队列** | diff --git a/pics/90_子集 II.png b/pics/90_子集 II.png new file mode 100644 index 00000000..8c773a17 Binary files /dev/null and b/pics/90_子集 II.png differ diff --git a/problems/0001.两数之和.md b/problems/0001.两数之和.md index e62fd846..624cd6bc 100644 --- a/problems/0001.两数之和.md +++ b/problems/0001.两数之和.md @@ -1,17 +1,67 @@ -## 题目地址 +# 题目地址 https://leetcode-cn.com/problems/two-sum/ -## 思路 +> 只用数组和set还是不够的! + +# 第1题. 两数之和 + +给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 + +你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。 + +**示例:** + +给定 nums = [2, 7, 11, 15], target = 9 + +因为 nums[0] + nums[1] = 2 + 7 = 9 + +所以返回 [0, 1] + + +# 思路 很明显暴力的解法是两层for循环查找,时间复杂度是O(n^2)。 -我们来看一下使用数组和set来做哈希法的局限。 + +使用哈希法最为合适,之前已经介绍过,数组和set在哈希法中的应用,那么来看一下使用数组和set来做哈希法的局限。 * 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。 -* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为我们要返回x 和 y的下表。所以set 也不能用。 +* set是一个集合,里面放的元素只能是一个key,而两数之和这道题目,不仅要判断y是否存在而且还要记录y的下表位置,因为要返回x 和 y的下表。所以set 也不能用。 -此时我们就要选择一种数据结构 map ,map是一种key value的存储结构,我们可以用key保存数值,用value在保存数值所在的下表。 -这道题目是map在哈希法中的经典应用 +此时就要选择另一种数据结构:map ,map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下表。 +C++中map,有三种类型: + +|映射 |底层实现 | 是否有序 |数值是否可以重复 | 能否更改数值|查询效率 |增删效率| +|---|---| --- |---| --- | --- | ---| +|std::map |红黑树 |key有序 |key不可重复 |key不可修改 | O(logn)|O(logn) | +|std::multimap | 红黑树|key有序 | key可重复 | key不可修改|O(logn) |O(logn) | +|std::unordered_map |哈希表 | key无序 |key不可重复 |key不可修改 |O(1) | O(1)| + +std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。 + +同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。 更多哈希表的理论知识请看[关于哈希表,你该了解这些!](https://mp.weixin.qq.com/s/g8N6WmoQmsCUw3_BaWxHZA)。 + +**这道题目中并不需要key有序,选择std::unordered_map 效率更高!** + +# C++代码 + +``` +class Solution { +public: + vector twoSum(vector& nums, int target) { + std::unordered_map map; + for(int i = 0; i < nums.size(); i++) { + auto iter = map.find(target - nums[i]); + if(iter != map.end()) { + return {iter->second, i}; + break; + } + map.insert(nums[i], i); + } + return {}; + } +}; +``` ## 一般解法 diff --git a/problems/0039.组合总和.md b/problems/0039.组合总和.md new file mode 100644 index 00000000..ec89c132 --- /dev/null +++ b/problems/0039.组合总和.md @@ -0,0 +1,66 @@ +# 第39题. 组合总和 + +给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 + +candidates 中的数字可以无限制重复被选取。 + +**说明:** + +所有数字(包括 target)都是正整数。 +解集不能包含重复的组合。  + +示例 1: + +输入:candidates = [2,3,6,7], target = 7, +所求解集为: +[ + [7], + [2,2,3] +] + +示例 2: + +输入:candidates = [2,3,5], target = 8, +所求解集为: +[ +  [2,2,2,2], +  [2,3,3], +  [3,5] +] + +# 思路 + +# C++代码 + +``` +// 无限制重复被选取。 吓得我赶紧想想 0 可咋办 +class Solution { +private: + vector> result; + void backtracking(vector& candidates, int target, vector& vec, int sum, int startIndex) { + if (sum > target) { + return; + } + if (sum == target) { + result.push_back(vec); + return; + } + + // 因为可重复,所以我们从0开始, 这道题目感觉像是47.全排列II,其实不是 + for (int i = startIndex; i < candidates.size(); i++) { + sum += candidates[i]; + vec.push_back(candidates[i]); + backtracking(candidates, target, vec, sum, i); // 关键点在这里,不用i+1了 + sum -= candidates[i]; + vec.pop_back(); + + } + } +public: + vector> combinationSum(vector& candidates, int target) { + vector vec; + backtracking(candidates, target, vec, 0, 0); + return result; + } +}; +``` diff --git a/problems/0040.组合总和II.md b/problems/0040.组合总和II.md new file mode 100644 index 00000000..dfd513b7 --- /dev/null +++ b/problems/0040.组合总和II.md @@ -0,0 +1,74 @@ +# 第40题. 组合总和 +给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 + +candidates 中的每个数字在每个组合中只能使用一次。 + +说明: + +所有数字(包括目标数)都是正整数。 +解集不能包含重复的组合。  + +示例 1: + +输入: candidates = [10,1,2,7,6,1,5], target = 8, +所求解集为: +[ + [1, 7], + [1, 2, 5], + [2, 6], + [1, 1, 6] +] + +示例 2: + +输入: candidates = [2,5,2,1,2], target = 5, +所求解集为: +[ +  [1,2,2], +  [5] +] + +# 思想 + +# C++代码 + +``` +class Solution { +private: + vector> result; + void backtracking(vector& candidates, int target, vector& vec, int sum, int startIndex, vector& used) { + if (sum > target) { + return; + } + if (sum == target) { + result.push_back(vec); + return; + } + +// 每个组合中只能使用一次 所以用 startindex +// 给定一个数组 candidates 默认有重复项,解集不能包含重复的组合。 所以使用if这一套 + for (int i = startIndex; i < candidates.size(); i++) { + if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) { + continue; + } + sum += candidates[i]; + vec.push_back(candidates[i]); + used[i] = true; + backtracking(candidates, target, vec, sum, i + 1, used); // 关键点在这里,不用i+1了 + used[i] = false; + sum -= candidates[i]; + vec.pop_back(); + } + } + +public: + vector> combinationSum2(vector& candidates, int target) { + vector vec; + vector used(candidates.size(), false); + sort(candidates.begin(), candidates.end()); + backtracking(candidates, target, vec, 0, 0, used); + return result; + + } +}; +``` diff --git a/problems/0077.组合.md b/problems/0077.组合.md new file mode 100644 index 00000000..09367483 --- /dev/null +++ b/problems/0077.组合.md @@ -0,0 +1,46 @@ +# 第77题. 组合 +给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。 +示例: + +输入: n = 4, k = 2 +输出: +[ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], +] + +# 思路 + + +# C++ 代码 + +``` +class Solution { +private: + vector> result; + void backtracking(int n, int k, vector& vec, int startIndex) { + if (vec.size() == k) { + result.push_back(vec); + return; + } + // 这个for循环有讲究,组合的时候 要用startIndex,排列的时候就要从0开始 + // 这个过程好难理解,需要画图 + for (int i = startIndex; i <= n; i++) { + vec.push_back(i); + backtracking(n, k, vec, i + 1); + vec.pop_back(); + } + } +public: + + vector> combine(int n, int k) { + vector vec; + backtracking(n, k, vec, 1); + return result; + } +}; +``` diff --git a/problems/0078.子集.md b/problems/0078.子集.md new file mode 100644 index 00000000..ee84f70d --- /dev/null +++ b/problems/0078.子集.md @@ -0,0 +1,49 @@ +# 题目地址 + +# 第78题. 子集 +给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。 + +说明:解集不能包含重复的子集。 + +示例: + +输入: nums = [1,2,3] +输出: +[ + [3], +  [1], +  [2], +  [1,2,3], +  [1,3], +  [2,3], +  [1,2], +  [] +] + + +# 思路 + + +# C++代码 + +``` + +class Solution { +private: + void backtracking(vector& nums, vector>& result, vector& vec, int startIndex) { + result.push_back(vec); + for (int i = startIndex; i < nums.size(); i++) { + vec.push_back(nums[i]); + backtracking(nums, result, vec, i + 1); + vec.pop_back(); + } + } +public: + vector> subsets(vector& nums) { + vector> result; + vector vec; + backtracking(nums, result, vec, 0); + return result; + } +}; +``` diff --git a/problems/0090.子集II.md b/problems/0090.子集II.md new file mode 100644 index 00000000..6e516536 --- /dev/null +++ b/problems/0090.子集II.md @@ -0,0 +1,38 @@ +# 题目地址 +https://leetcode-cn.com/problems/subsets-ii/ +# 第90题. 子集II + + +# 思路 + + +# C++代码 + +``` +class Solution { +private: + void backtracking(vector& nums, vector>& result, vector& vec, int startIndex, vector& used) { + result.push_back(vec); + for (int i = startIndex; i < nums.size(); i++) { + if (i > 0 && nums[i] == nums[i - 1] && used[i-1] == false) { // 果然去重都是一个逻辑啊 + continue; + } + vec.push_back(nums[i]); + used[i] = true; + backtracking(nums, result, vec, i + 1, used); + used[i] = false; + vec.pop_back(); + } + } + +public: + vector> subsetsWithDup(vector& nums) { + vector used(nums.size(), false); + vector> result; + vector vec; + sort(nums.begin(), nums.end()); + backtracking(nums, result, vec, 0, used); + return result; + } +}; +``` diff --git a/problems/0216.组合总和III.md b/problems/0216.组合总和III.md new file mode 100644 index 00000000..2cdf5dfe --- /dev/null +++ b/problems/0216.组合总和III.md @@ -0,0 +1,56 @@ +# 第216题. 组合总和 III +找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。 + +说明: + +所有数字都是正整数。 +解集不能包含重复的组合。  + +示例 1: + +输入: k = 3, n = 7 +输出: [[1,2,4]] +示例 2: + +输入: k = 3, n = 9 +输出: [[1,2,6], [1,3,5], [2,3,4]] + + +# 思路 + + +# C++代码 + +``` +class Solution { +private: + vector> result; + void backtracking(int target, int k, vector& vec, int num, int sum, int startIndex) { + if (sum > target || num > k) { + return; + } + if (num == k && sum == target) { + result.push_back(vec); + return; + } + + for (int i = startIndex; i <= 9; i++) { + sum += i; + vec.push_back(i); + num++; + backtracking(target, k, vec, num, sum, i + 1); + num--; + sum -= i; + vec.pop_back(); + } + } + +public: + vector> combinationSum3(int k, int n) { + vector vec; + backtracking(n, k, vec, 0, 0, 1); + return result; + + } +}; +```