diff --git a/README.md b/README.md index 3327a40d..e50dc6c3 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,7 @@ vector> levelOrder(TreeNode* root) { * [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) * [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md) +* [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md) * [0104.二叉树的最大深度 (迭代法)](https://github.com/youngyangyang04/leetcode/blob/master/problems/0104.二叉树的最大深度.md) * [0111.二叉树的最小深度(迭代法)]((https://github.com/youngyangyang04/leetcode/blob/master/problems/0111.二叉树的最小深度.md)) @@ -463,6 +464,7 @@ backtracking() { |[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) |树 |简单|**递归** **迭代**| +|[0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md) |树 |简单|**广度优先搜索/队列**| |[0654.最大二叉树](https://github.com/youngyangyang04/leetcode/blob/master/problems/0654.最大二叉树.md) |树 |中等|**递归**| |[0700.二叉搜索树中的搜索](https://github.com/youngyangyang04/leetcode/blob/master/problems/0700.二叉搜索树中的搜索.md) |树 |简单|**递归** **迭代**| |[0701.二叉搜索树中的插入操作](https://github.com/youngyangyang04/leetcode/blob/master/problems/0701.二叉搜索树中的插入操作.md) |树 |简单|**递归** **迭代**| diff --git a/problems/0020.有效的括号.md b/problems/0020.有效的括号.md index 95796af3..80a3cb49 100644 --- a/problems/0020.有效的括号.md +++ b/problems/0020.有效的括号.md @@ -3,22 +3,74 @@ https://leetcode-cn.com/problems/valid-parentheses/ -## 思路 +> 数据结构与算法应用往往隐藏在我们看不到的地方 -### 题外话 -括号匹配是使用栈解决的经典问题。 +# 20. 有效的括号 -题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,响应的位置必须要有右括号,如果还记得编译原理的话,编译器在 词法分析的过程中处理 括号,花括号等这个符号的逻辑,也是使用了栈这种数据结构。 +给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。 -所以栈在计算机领域中应用是非常广泛的。 有的同学可以经常会想学的这些数据结构有什么用,也开发不了什么软件,大多数同学说的软件应该都是可视化的软件例如APP之类的,那都是非常上层的应用了,底层很多功能的实现都是基础的数据结构和算法。 这里我就不过多展开了,我们先来看题。 +有效字符串需满足: +* 左括号必须用相同类型的右括号闭合。 +* 左括号必须以正确的顺序闭合。 +* 注意空字符串可被认为是有效字符串。 -### 进入正题 +示例 1: +输入: "()" +输出: true -由于栈结构的特殊性,非常适合做对称匹配类的题目。首先我们要弄清楚,字符串里的括号不匹配有几种情况。 +示例 2: +输入: "()[]{}" +输出: true -一些同学,在面试中看到这种题目上来就开始写代码,建议要写代码之前要分析好有哪几种不匹配的情况,如果不动手之前分析好,写出的代码也会有很多问题。 会给面试官留下不好的印象。 +示例 3: +输入: "(]" +输出: false -我们先来分析一下 这里有三种不匹配的情况, +示例 4: +输入: "([)]" +输出: false + +示例 5: +输入: "{[]}" +输出: true + +# 思路 + +## 题外话 + +**括号匹配是使用栈解决的经典问题。** + +题意其实就像我们在写代码的过程中,要求括号的顺序是一样的,有左括号,相应的位置必须要有右括号。 + +如果还记得编译原理的话,编译器在 词法分析的过程中处理括号、花括号等这个符号的逻辑,也是使用了栈这种数据结构。 + +再举个例子,linux系统中,cd这个进入目录的命令我们应该再熟悉不过了。 + +``` +cd a/b/c/../../ +``` + +这个命令最后进入a目录,系统是如何知道进入了a目录呢 ,这就是栈的应用(其实可以出一道相应的面试题了) + +所以栈在计算机领域中应用是非常广泛的。 + +有的同学经常会想学的这些数据结构有什么用,也开发不了什么软件,大多数同学说的软件应该都是可视化的软件例如APP、网站之类的,那都是非常上层的应用了,底层很多功能的实现都是基础的数据结构和算法。 + +**所以数据结构与算法的应用往往隐藏在我们看不到的地方!** + +这里我就不过多展开了,先来看题。 + +## 进入正题 + +由于栈结构的特殊性,非常适合做对称匹配类的题目。 + +首先要弄清楚,字符串里的括号不匹配有几种情况。 + +**一些同学,在面试中看到这种题目上来就开始写代码,然后就越写越远。** + +建议要写代码之前要分析好有哪几种不匹配的情况,如果不动手之前分析好,写出的代码也会有很多问题。 + +先来分析一下 这里有三种不匹配的情况, 1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。 ![括号匹配1](https://img-blog.csdnimg.cn/2020080915505387.png) @@ -27,7 +79,7 @@ https://leetcode-cn.com/problems/valid-parentheses/ 3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。 ![括号匹配3](https://img-blog.csdnimg.cn/20200809155115779.png) -我们的代码只要覆盖了这三种不匹配的情况,基本就不会出问题,可以看出 动手之前分析好题目的重要性。 +我们的代码只要覆盖了这三种不匹配的情况,就不会出问题,可以看出 动手之前分析好题目的重要性。 动画如下: @@ -40,9 +92,9 @@ https://leetcode-cn.com/problems/valid-parentheses/ 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false -那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈也是空的,就说明全都匹配了。 +那么什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。 -接下来我们来看一下代码。 +分析完之后,代码其实就比较好些了,如下: ## C++代码 @@ -56,12 +108,12 @@ public: if (s[i] == '(') st.push(')'); else if (s[i] == '{') st.push('}'); else if (s[i] == '[') st.push(']'); - // 第三种情况 是遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false - // 第二种情况 遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false + // 第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号 return false + // 第二种情况:遍历字符串匹配的过程中,发现栈里没有我们要匹配的字符。所以return false else if (st.empty() || st.top() != s[i]) return false; - else st.pop(); // st.top() == s[i] + else st.pop(); // st.top() 与 s[i]相等,栈弹出元素 } - // 第一种情况 此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true + // 第一种情况:此时我们已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false,否则就return true return st.empty(); } }; diff --git a/problems/0102.二叉树的层序遍历.md b/problems/0102.二叉树的层序遍历.md index 61153e09..b5998a45 100644 --- a/problems/0102.二叉树的层序遍历.md +++ b/problems/0102.二叉树的层序遍历.md @@ -5,6 +5,11 @@ https://leetcode-cn.com/problems/binary-tree-level-order-traversal/ 我们之前讲过了,二叉树的深度优先遍历:[一文学通二叉树前中后序递归法与迭代法](https://github.com/youngyangyang04/leetcode/blob/master/problems/0144.二叉树的前序遍历.md)里面有前中后序遍历的方式,前中后序分辨可以使用递归和迭代的方法来实现,接下来我们再来介绍二叉树的另一种遍历方式,也就是层序遍历。 +相似题目: +* [0102.二叉树的层序遍历](https://github.com/youngyangyang04/leetcode/blob/master/problems/0102.二叉树的层序遍历.md) +* [0199.二叉树的右视图](https://github.com/youngyangyang04/leetcode/blob/master/problems/0199.二叉树的右视图.md) +* [0637.二叉树的层平均值](https://github.com/youngyangyang04/leetcode/blob/master/problems/0637.二叉树的层平均值.md) + 层序遍历一个二叉树。就是从左到右一层一层的去遍历二叉树。这种遍历的方式和我们之前讲过的都不太一样。 需要借用一个辅助数据结构队列来实现,**队列先进先出,符合一层一层遍历的逻辑,而是用栈先进后出适合模拟深度优先遍历也就是递归的逻辑。** diff --git a/problems/0225.用队列实现栈.md b/problems/0225.用队列实现栈.md index a359d6e8..00aae08f 100644 --- a/problems/0225.用队列实现栈.md +++ b/problems/0225.用队列实现栈.md @@ -22,10 +22,14 @@ https://leetcode-cn.com/problems/implement-stack-using-queues/ # 思路 +(这里要强调是单向队列) + 有的同学可能疑惑这种题目有什么实际工程意义,**其实很多算法题目主要是对知识点的考察和教学意义远大于其工程实践的意义,所以面试题也是这样!** 刚刚做过[栈与队列:我用栈来实现队列怎么样?](https://mp.weixin.qq.com/s/P6tupDwRFi6Ay-L7DT4NVg)的同学可能依然想着用一个输入队列,一个输出队列,就可以模拟栈的功能,仔细想一下还真不行! +**队列模拟栈,其实一个队列就够了**,那么我们先说一说两个队列来实现栈的思路。 + **队列是先进先出的规则,把一个队列中的数据导入另一个队列中,数据的顺序并没有变,并有变成先进后出的顺序。** 所以用栈实现队列, 和用队列实现栈的思路还是不一样的,这取决于这两个数据结构的性质。 @@ -95,5 +99,52 @@ public: } }; ``` + +# 优化 + +其实这道题目就是用一个队里就够了。 + +**一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。** + +代码如下: + +# C++优化代码 + +``` +class MyStack { +public: + queue que; + /** Initialize your data structure here. */ + MyStack() { + + } + /** Push element x onto stack. */ + void push(int x) { + que.push(x); + } + /** Removes the element on top of the stack and returns that element. */ + int pop() { + int size = que.size(); + size--; + while (size--) { // 将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部 + que.push(que.front()); + que.pop(); + } + int result = que.front(); // 此时弹出的元素顺序就是栈的顺序了 + que.pop(); + return result; + } + + /** Get the top element. */ + int top() { + return que.back(); + } + + /** Returns whether the stack is empty. */ + bool empty() { + return que.empty(); + } +}; +``` > 更多算法干货文章持续更新,可以微信搜索「代码随想录」第一时间围观,关注后,回复「Java」「C++」 「python」「简历模板」「数据结构与算法」等等,就可以获得我多年整理的学习资料。