20 Valid Parentheses
问题:没有意识到字符串中只包含字符:’(‘, ‘)’, ‘{‘, ‘}’, ‘[’ and ‘]’
代码:git代码
682 Baseball Game
问题:错误在+操作:top1 先弹出,top2 再弹出,还原到stack里面的时候,要先放top2,再放top1,最后放score = top1+top2;从优秀代码中学习到的优点:谁说栈就只能用stack了?linkedList也可以;每种操作都优先考虑放入栈中,再考虑加入到sum中。这是我没有注意的,我的思考顺序就很随便了。
public int calPointsV2(String[] ops) {int sum = 0;LinkedList<Integer> list = new LinkedList<>();for (String op : ops) {if (op.equals("C")) {sum -= list.removeLast();}else if (op.equals("D")) {list.add(list.peekLast() * 2);sum += list.peekLast();}else if (op.equals("+")) {list.add(list.peekLast() + list.get(list.size() - 2));sum += list.peekLast();}else {list.add(Integer.parseInt(op));sum += list.peekLast();}}
return sum;}
代码:git代码
496 Next Greater Element I
问题:这道题目,自己首先是理解题意有问题。对于nums1中的每个元素,先查找num2中自己(这个元素)所在位置,从右侧开始查找,最近的一个比当前元素大的元素。我理解成对应的下标了。从优先代码学习到观察之后能发现在一个递减序列中,遇到一个大的元素,那这个元素就是这个序列中每个元素的下一个更大元素。
代码:git代码
735 Asteroid Collision
问题:我的问题是在写代码的时候没有把if 情况合并起来,写出来的代码不够简洁。
代码:git代码
103 Binary Tree Zigzag Level Order Traversal
问题:遍历节点和添加值到结果中这两个步骤要分清楚
代码:git代码
144 Binary Tree Preorder Traversal
问题:if条件的位置影响程序的执行时间
代码:git代码
402 Remove K Digits
问题:这道题目最后卡在了超时问题上。参考了最佳答案。原始思路是:每次删除一位数字,得到新的最小num,再删除,直到删除k个数字。需要求每次删除一位数字,如果得到最小的num:从高位开始删除,遇到比当前最小值大的删除操作就停止。例如1432219删除第一位的步骤是:删除1,得到:432219;删除4,得到:132219;删除3,得到142219。因为142219>132219,所以停止本次循环。接着使用132219,再删除一位,步骤是:删除1,得到32219;删除3,得到12219,;删除2得到:13219;13219 > 12219 ,所以停止本次循环。依次处理k次。时间复杂度是O(nk)。
参考代码:看了discussion,思路是这样的。往栈里面push元素。如果当前元素比栈顶元素小,则pop。每一次pop,k需要减1;一直到当前元素大于栈中的元素。前提条件是k>0。
public String removeKdigitsV3(String num, int k) {int digits = num.length() - k;// 最后会留下digits长度的字符char[] stk = new char[num.length()];int top = 0;// 栈的顶端for (int i = 0; i < num.length(); i++) {char c = num.charAt(i);while (top > 0 && stk[top - 1] > c && k > 0) {top -= 1;k -= 1;}stk[top++] = c;}int idx = 0;while (idx < digits && stk[idx] == '0')idx++;return idx == digits ? "0" : new String(stk, idx, digits - idx);}
代码:代码位置
394 Decode String
问题:看了题目之后首先想到先计算内层的[],再计算外层的[]。始终用一个变量来记录最后的结果。分析遇到数字应该做什么,遇到[、]、字母分别应该做什么,就能得到代码。即使思路想明白了,在代码实现上一点小的区别和改动,就能得到流畅的代码和不优雅的代码。看decodeStringV2和decodeStringV3的区别
代码
385 Mini Parser
问题:本题和上一题类似,也有嵌套。区别是本题有效的全是数字。我学习到的是递归思路。上题分析了,找到遇到不同类型的字符时候怎么处理。还有一种是递归。对于:[123,[456,[789]]] ,我们可以先处理123,[456,[789]],进而可以处理:123 和 [456,[789]];对于123,直接处理转为int;对于[456,[789]],我们可以先处理456,[789];进而分别处理456和[789]。对于[789],我们处理789。不断递归,不断缩小问题。
递归思想:先处理退出情况。处理不断递归对结果的影响。
public NestedInteger deserializeV3(String s) {if (s.length() == 0)return new NestedInteger();if (s.charAt(0) != '[')return new NestedInteger(Integer.parseInt(s));if (s.length() <= 2)return new NestedInteger();NestedInteger res = new NestedInteger();int start = 1, cnt = 0;for (int i = 1; i < s.length(); ++i) {if (cnt == 0 && (s.charAt(i) == ',' || i == s.length() - 1)) {res.add(deserializeV3(s.substring(start, i)));start = i + 1;} else if (s.charAt(i) == '[')++cnt;else if (s.charAt(i) == ']')--cnt;}return res; }
341. Flatten Nested List Iterator
问题:把嵌套的int展开。这里也是一个递归思想。
代码
331 Verify Preorder Serialization of a Binary Tree
问题:题目中说明给定的字符串是一个二叉树先序遍历的结果。判断是不是一颗有效的二叉树。这里注意两点:1 树的叶子节点一定是空节点;2 可能会有多余的节点。
我的思路:第一感觉是用stack。我按着操作顺序走一遍,觉得自己应该能写出代码;发现写不出来;后来我就分开访问节点与判断节点是否有效,写出了第一版的代码。写完后发现,因为是一颗二叉树,所以最终就是判断preorder[0]是不是一个有效节点就可以了。所以合并了访问节点与判断节点是否有效的操作。写出来第一版的递归代码。不满意的地方是index使用了全局变量,处理得不好。
别人的代码:所有的叶子节点是空节点,说明这颗二叉树是一颗满树。二叉树满树有一个特性:叶子节点数量=非叶子节点数量+1。利用二叉树的性质。聪明。当然还有些解决方法用了入度、出度的概念,我就没有继续学习了。思维扩散开来就好了。
代码
456 132 Pattern
问题:输入int数组,判断数组是否包含下标:i<j<k,数值a[i]<a[k]<a[j]这样格式的组合。这道题目按照直觉三层循环解决非常简单,但是会遇到超时的问题。
学习:思路一:固定j;当 i确定了之后,nums[k]就一定在 (nums[i],nums[j])之间。既然nums[j]已经确定了,那就应该找一个尽可能小的nums[i]在[0,j)之间。
学习:思路二:使用了栈,参考了别人的文章。a[i]<a[k]<a[j],a[j]是最大值,用栈存这个值。还需要找到一个尽可能大的a[k],但是又<a[j],记为third。因为k>j,所以从数组的末尾开始遍历。当a[k]和a[j]确定之后,只要判断a[i]<third就返回true。至于如何能想到这么解决,我想应该是题目做多了,自然就有感觉了吧。
代码
/*** i<j<k* a[i]<a[k]<a[j]* @param nums* @return*/public boolean find132patternV3(int[] nums) {int third = Integer.MIN_VALUE;//a[k]Stack<Integer> stack = new Stack<Integer>();for(int i=nums.length - 1;i>=0;i--){//nums[i] = a[i]if(nums[i] < third){return true;}else{while(!stack.isEmpty() && nums[i] > stack.peek()){third = stack.pop();}stack.push(nums[i]);//a[j]}}return false;}
739 Daily Temperatures
问题:输入: [73, 74, 75, 71, 69, 72, 76, 73],输出[1, 1, 4, 2, 1, 1, 0, 0]。直观的解决方法很简单。两层循环,当i确认的时候,j比i大,找到第一个nums[i]<nums[j]的j,r[i] = j-i。时间复杂度是n2。
学习:使用栈,几乎是O(n)的复杂度。自己也想过放栈里面,但是想着想着就不知道怎么做了,看着别人这么做,觉得好简单。遇到下标i,放入栈中,接着向后遍历(因为符合条件的元素肯定在后面)。当遇到nums[i]>nums[栈顶元素]的情况下,就是找到答案了。当然可能多个元素,符合条件的下标都是i,所以需要循环栈。需要处理的元素放入栈,每遍历一个元素,看这个元素是否符合栈内元素的要求。和456题有点类似。
public int[] dailyTemperaturesV2(int[] temperatures) {int[] result = new int[temperatures.length];Stack<Integer> stack = new Stack<Integer>();for(int i=0;i<temperatures.length;i++){while(!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]){int idx = stack.pop();result[idx] = i - idx;}stack.push(i);}return result;}
503 Next Greater Element II
问题:这是一个可循环的数组,找到每个元素下标最接近的下一个比较大的元素。这依然是一道找到较大值的题目。和上一个题目类似,用stack类解决。我的思路是先找一遍,再找一遍。循环两次,因为毕竟是个循环数组。
public int[] nextGreaterElements(int[] nums) {int[] result = new int[nums.length];Stack<Integer> stack = new Stack<Integer>();for (int i = 0; i < nums.length; i++) {result[i] = -1;while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {result[stack.pop()] = nums[i];}stack.push(i);}for (int i = 0; i < nums.length - 1; i++) {while (!stack.isEmpty() && nums[i] > nums[stack.peek()]) {result[stack.pop()] = nums[i];}}return result;}
学习:有人总结了循环数组问题的解决思路。方法一:将原始数组扩大两倍,当做普通问题处理。方法二:借助栈思想。就是上述代码,只是不太优雅。参考代码中的nextGreaterElementsV3。
代码
71 Simplify Path
问题:这是关于文件夹路径简化的问题。
总结的规律是:大小写区分;”/字母”这是一级目录; “/.”可以忽略 ;”/..”回退一级目录。题目思考的思路是按照一个字符一个字符去思考。思考遇到不同类型的字符,应该做什么操作。代码经过不断合并if条件,得到最后的代码。
public String simplifyPath(String path) {Stack<String> dirStack = new Stack<String>();int n = path.length();for (int i = 0; i < n; i++) {char ch = path.charAt(i);if (ch!='/') {int end = i + 1;while (end < n && path.charAt(end) !='/') {end++;}String str = path.substring(i,end);if(str.equals("..")){if(!dirStack.isEmpty()){dirStack.pop();}}else if(!str.equals(".")){dirStack.push(str);}i = (end > i + 1 ? end - 1 : i);}}String r = "";for(String dir : dirStack){r = r + "/"+dir;}return r==""?"/":r;}
学习:有人思考的角度是把字符串按/分隔后,考虑字符的有效性和无效性。代码简洁了很多。只是执行速度慢了。
public String simplifyPathV2(String path) {Deque<String> stack = new LinkedList<String>();for(String str : path.split("/")){if(str.equals("..") && !stack.isEmpty()){stack.pop();}else if(!str.equals("..") && !str.equals(".") && !str.equals("")){stack.push(str);}}String r = "";for(String dir : stack){r = "/"+dir+r;}return r==""?"/":r;}
代码
173 Binary Search Tree Iterator
问题:这是关于二分查找树的一个遍历器,要求实现hasNext方法和next方法,时间要求平均是O(1),空间要求是O(h),h是树的高度。根据二分查找树的定义,我们知道所有左子树节点的值小于根节点;所有右子树节点的值大于根节点。当next方法要求依次返回最小值的时候,我们知道肯定是先返回左子树的值,再返回节点值,最后返回右子树上的值。只是在实现的时候不能在空间要求下实现。
学习:思路一:用stack存储从根节点开始的所有左节点。当next被调用的时候,直接从stack pop节点tmpNode,返回tmpNode的值。在返回之前,将tmpNode.right作为根节点再次处理存入stack。我的问题是总想弄一个curNode,想怎么在三种状态之间切换(左子节点、当前节点值、右子节点)
public class BSTIteratorV2 {private Stack<TreeNode> stack = new Stack<TreeNode>();public BSTIteratorV2(TreeNode root) {fillStack(root);}private void fillStack(TreeNode node) {for(;node!=null;node = node.left){stack.push(node);}}/** @return whether we have a next smallest number */public boolean hasNext() {return !stack.isEmpty();}/** @return the next smallest number */public int next() {TreeNode tmpNode = stack.pop();fillStack(tmpNode.right);return tmpNode.val;}
}
学习:思路二:看到一个有当前节点版本的。当前节点与要返回值的节点不是一个节点。这是重点
public class BSTIteratorV3 {private Stack<TreeNode> stack;private TreeNode cur = null;public BSTIteratorV3(TreeNode root){cur = root;stack = new Stack<TreeNode>();}public boolean hasNext() {return !stack.isEmpty() || cur !=null;}public int next(){while(cur!=null){stack.push(cur);cur = cur.left;}TreeNode node = stack.pop();//这一部是没有想到的cur = node.right;return node.val;}
}
94 Binary Tree Inorder Traversal
问题:中序遍历二叉树。整个解题思路与上一题目完全相同。可以实现一下递归版本与迭代版本。
代码