diff --git a/problems/0017.电话号码的字母组合.md b/problems/0017.电话号码的字母组合.md new file mode 100644 index 00000000..bcf53662 --- /dev/null +++ b/problems/0017.电话号码的字母组合.md @@ -0,0 +1,56 @@ + + +## 题目地址 + +## 思路 + + +1. 数字和字母如何映射 +2. 两个字母我就两个for循环,三个字符我就三个for循环,以此类推,然后发现代码根本写不出来 +3. 如何排列组合呢 +4. 1 * # 等等情况 + +树形结构啊 + +## C++代码 + +``` +class Solution { +private: + const string letterMap[10] = { + "", // 0 + "", // 1 + "abc", // 2 + "def", // 3 + "ghi", // 4 + "jkl", // 5 + "mno", // 6 + "pqrs", // 7 + "tuv", // 8 + "wxyz", // 9 + }; +public: + vector result; + void getCombinations(const string& digits, int index, const string& s) { + if (index == digits.size()) { + result.push_back(s); + return; + } + int digit = digits[index] - '0'; + string letters = letterMap[digit]; + for (int i = 0; i < letters.size(); i++) { + getCombinations(digits, index + 1, s + letters[i]); + } + } + vector letterCombinations(string digits) { + if (digits.size() == 0) { + return result; + } + getCombinations(digits, 0, ""); + return result; + + } +}; +``` + +> 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。 diff --git a/problems/0110.平衡二叉树.md b/problems/0110.平衡二叉树.md index 6170cf6a..bb6b0d0d 100644 --- a/problems/0110.平衡二叉树.md +++ b/problems/0110.平衡二叉树.md @@ -3,32 +3,85 @@ https://leetcode-cn.com/problems/balanced-binary-tree/ ## 思路 +递归三步曲分析: + +1. 明确递归函数的参数和返回值 + +参数的话为传入的节点指针,就没有其他参数需要传递了,**返回值要注意,我们的返回值是要求传入节点为根节点树的深度**,否则如何标记左右子树是否差值大于1呢。 + +这里还要注意一点,如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,返回高度还有意义么? 此时可以返回-1 来标记已经不符合平衡树的规则了。 + +代码如下: + +``` +int depth(TreeNode* node) +``` + +2. 明确终止条件 + +递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的书高度为0 + +代码如下: + +``` +if (node == NULL) { + return 0; +} +``` + +3. 明确单层递归的逻辑 + +如何判断当前传入节点为根节点的二叉树是否是平衡二叉树呢,当然是左子树高度和右子树高度相差。 + +分别求出左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则则返回-1,表示已经不是二叉树了。 + +代码如下: + +``` +int leftDepth = depth(node->left); +if (leftDepth == -1) return -1; +int rightDepth = depth(node->right); +if (rightDepth == -1) return -1; +return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth); +``` + +此时递归的函数就已经写出来了,这个递归的函数传入节点指针,返回以该节点为根节点的二叉树的高度,如果不是二叉平衡树了则返回-1。 + +代码如下: + +``` +int depth(TreeNode* node) { + if (node == NULL) { + return 0; + } + int leftDepth = depth(node->left); + if (leftDepth == -1) return -1; + int rightDepth = depth(node->right); + if (rightDepth == -1) return -1; + return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth); +} +``` + +整体代码如下: ## C++代码 ``` class Solution { public: - // 一开始的想法是 遍历这棵树,然后针对每个节点判断其左右子树的深度 - - // 求node为头节点二叉树的深度,并且比较起左右节点的高度差 - // 平衡树的条件: 左子树是平衡树,右子树是平衡树,左右子树高度相差不超过1。 - // 递归三部曲 - // 1. 明确终止条件:如果node为null - // 2. 明确返回信息: 判断左子树,判断右子树 - // 3. 一次递归要处理的逻辑 + // 返回以该节点为根节点的二叉树的高度,如果不是二叉搜索树了则返回-1 int depth(TreeNode* node) { if (node == NULL) { return 0; } - int left = depth(node->left); - if (left == -1) return -1; - int right = depth(node->right); - if (right == -1) return -1; - return abs(left - right) > 1 ? -1 : 1 + max(left, right); + int leftDepth = depth(node->left); + if (leftDepth == -1) return -1; // 说明左子树已经不是二叉平衡树 + int rightDepth = depth(node->right); + if (rightDepth == -1) return -1; // 说明右子树已经不是二叉平衡树 + return abs(leftDepth - rightDepth) > 1 ? -1 : 1 + max(leftDepth, rightDepth); } bool isBalanced(TreeNode* root) { - return depth(root) == -1 ? false : true; + return depth(root) == -1 ? false : true; } }; ``` diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index 42a275d8..3e67e840 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -4,13 +4,30 @@ https://leetcode-cn.com/problems/implement-stack-using-queues/ ## 思路 -有的同学可能疑惑这种题目有什么实际工程意义,其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样! +有的同学可能疑惑这种题目有什么实际工程意义,**其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样!** -用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质 +相信做过[0232.用栈实现队列](https://github.com/youngyangyang04/leetcode/blob/master/problems/0232.用栈实现队列.md)这道题目的部分同学会依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,其实不是这样的。 -建议大家题目编号: 225 和 232 一起做来做 +队列是先进先出的规则,你把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并有变成先进后出的顺序。 -详细如代码注释所示 +所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。 + +如下面动画所示,**用两个队列que1和que2实现队列的功能,que2其实完全就是一个备份的作用**,把que1最后面的元素以外的元素都备份到que2,然后弹出最后面的元素,再把其他元素从que2导回que1。 + +模拟的队列执行语句如下: +queue.push(1); +queue.push(2); +queue.pop(); +queue.push(3); +queue.push(4); +queue.pop(); +queue.pop(); +queue.pop(); +queue.empty(); + + + +详细如代码注释所示: ## C++代码 diff --git a/problems/0232.用栈实现队列.md b/problems/0232.用栈实现队列.md index b505a600..80594525 100644 --- a/problems/0232.用栈实现队列.md +++ b/problems/0232.用栈实现队列.md @@ -3,7 +3,28 @@ https://leetcode-cn.com/problems/implement-queue-using-stacks/ ## 思路 -使用两个栈 一个输入栈,一个输出栈, 这里要注意输入栈和输出栈的关系,每当pop的时候且输出栈为空,就应该把输入站全部导入输出栈中 +使用**两个栈 一个输入栈,一个输出栈**,这里要注意输入栈和输出栈的关系。 + +下面动画模拟一下队列的执行过程如下: + +执行语句: +queue.push(1); +queue.push(2); +queue.pop(); **注意此时的输出栈的操作** +queue.push(3); +queue.push(4); +queue.pop(); +queue.pop();**注意此时的输出栈的操作** +queue.pop(); +queue.empty(); + + + +在push数据的时候,只要数据放进输入栈就好,**但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入)**,再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。 + +最后如何判断队列为空呢?**如果进栈和出栈都为空的话,说明模拟的队列为空了。** + +代码如下: ## C++代码 diff --git a/problems/0242.有效的字母异位词.md b/problems/0242.有效的字母异位词.md index e903ee44..59555a4a 100644 --- a/problems/0242.有效的字母异位词.md +++ b/problems/0242.有效的字母异位词.md @@ -2,13 +2,24 @@ https://leetcode-cn.com/problems/valid-anagram/ -## 思路 +> 数组就是简单的哈希表,但是数组的大小可不是无限开辟的 -先说一个特殊示例,输入:s = “car”, t= “car”,输出应该是什么呢? 后台判题逻辑返回的依然是true,其实这道题目真正题意是字符串s是否可以排练组合为字符串t。 +# 第242题.有效的字母异位词 -那么来看一下应该怎么做, 首先是暴力的解法,两层for循环,同时还要记录 字符是否重复出现,很明显时间复杂度是 O(n^2),暴力的方法这里就不做介绍了,直接看一下有没有更优的方式。 +给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。 -数组其实就是一个简单哈希表,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。 +![](https://img-blog.csdnimg.cn/202008171902298.png) + +**说明:** +你可以假设字符串只包含小写字母。 + +# 思路 + +先看暴力的解法,两层for循环,同时还要记录字符是否重复出现,很明显时间复杂度是 O(n^2)。 + +暴力的方法这里就不做介绍了,直接看一下有没有更优的方式。 + +**数组其实就是一个简单哈希表**,而且这道题目中字符串只有小写字符,那么就可以定义一个数组,来记录字符串s里字符出现的次数。 需要定义一个多大的数组呢,定一个数组叫做record,大小为26 就可以了,初始化为0,因为字符a到字符z的ASCII也是26个连续的数值。 @@ -16,24 +27,23 @@ https://leetcode-cn.com/problems/valid-anagram/ 操作动画如下: - + 定义一个数组叫做record用来上记录字符串s里字符出现的次数。 -如何记录呢,需要把字符映射到数组也就是哈希表的索引下表上,那么**因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下表0,相应的字符z映射为下表25。** +需要把字符映射到数组也就是哈希表的索引下表上,**因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下表0,相应的字符z映射为下表25。** 再遍历 字符串s的时候,**只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。** 这样就将字符串s中字符出现的次数,统计出来了。 +那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。 -那看一下如何检查 字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。 - -那么最后检查一下,**record数组如果有的元素不为零0,说明字符串s和t 一定是谁多了字符或者谁少了字符,return false。** +那么最后检查一下,**record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。** 最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。 时间复杂度为O(n),空间上因为定义是的一个常量大小的辅助数组,所以空间复杂度为O(1)。 -## C++ 代码 +# C++ 代码 ``` class Solution { public: diff --git a/problems/0617.合并二叉树.md b/problems/0617.合并二叉树.md index f7620b81..43dcf67b 100644 --- a/problems/0617.合并二叉树.md +++ b/problems/0617.合并二叉树.md @@ -90,10 +90,8 @@ public: while(!que.empty()) { TreeNode* node1 = que.front(); que.pop(); TreeNode* node2 = que.front(); que.pop(); - // 两个节点不为空,val相加 - if (node1 != NULL && node2 != NULL) { - node1->val += node2->val; - } + // 此时两个节点一定不为空,val相加 + node1->val += node2->val; // 如果左节点都不为空,加入队列 if (node1->left != NULL && node2->left != NULL) { que.push(node1->left); diff --git a/video/225.用队列实现栈.mp4 b/video/225.用队列实现栈.mp4 new file mode 100644 index 00000000..9aa76c34 Binary files /dev/null and b/video/225.用队列实现栈.mp4 differ diff --git a/video/232.用栈实现队列.mp4 b/video/232.用栈实现队列.mp4 new file mode 100644 index 00000000..1ba21a36 Binary files /dev/null and b/video/232.用栈实现队列.mp4 differ diff --git a/video/232.用栈实现队列版本2.mp4 b/video/232.用栈实现队列版本2.mp4 new file mode 100644 index 00000000..32db272e Binary files /dev/null and b/video/232.用栈实现队列版本2.mp4 differ