Posts Tagged ‘array’
- In: leetcode
- 3 Comments
这个题要用反证法来理解。算法:
- 从i开始,j是当前station的指针,sum += gas[j] – cost[j] (从j站加了油,再算上从i开始走到j剩的油,走到j+1站还能剩下多少油)
- 如果sum < 0,说明从i开始是不行的。那能不能从i..j中间的某个位置开始呢?假设能从k (i <=k<=j)走,那么i..j < 0,若k..j >=0,说明i..k – 1更是<0,那从k处就早该断开了,根本轮不到j。
- 所以一旦sum<0,i就赋成j + 1,sum归零。
- 最后total表示能不能走一圈。
- 这个题算法简单,写起来真是够呛。对数组一快一慢双指针的理解还是不行。注意千万不能出现while (i < 0) { i++, A[i]}这种先++然后取值的情况,必须越界。
public int canCompleteCircuit(int[] gas, int[] cost) { int i = 0, j = 0; int sum = 0; int total = 0; while (j < gas.length) { int diff = gas[j] - cost[j]; if (sum + diff < 0) { i = j + 1; sum = 0; } else { sum += diff; } j++; total += diff; } return total >= 0 ? i : -1; }
[leetcode] Longest Substring Without Repeating Characters | 最长的unique char组成的substring
Posted November 2, 2013
on:这题完全没印象,后来发现是之前做用了string.indexOf这个函数cheat成功了。这也是two pointers一快一慢的练习题,挺难做的,需要重做。
- i:当前unique substring的头
- j:当前unique substring的尾
- 注意最后可能忘记算最后一段的长度了(因为dup那个条件已经不存在)
public int lengthOfLongestSubstring(String s) { int i = 0, len = 0, n = s.length(); char A[] = s.toCharArray(); Set<Character> set = new HashSet<Character>(); for (int j = 0; j < n; j++) { if (set.contains(A[j])) { //found dup len = Math.max(len, j - i); while (i < n && A[i] != A[j]) { set.remove(A[i]); i++; } i++; } else { set.add(A[j]); } } return Math.max(len, n - i); }
- In: leetcode
- 6 Comments
括号题也不是只用stack才能做,这题是算长度,所以stack那种一match就俩一起pop了的方式就不适合了。求极值,一维dp最好使。
- d[i]: 以i开头的最长valid parentheses有多长。
- d[i] =
- if (str[i] == ‘)’),以右括号开头必定invalid,d[i] = 0
- if (str[i] == ‘(‘),以左括号开头
- 我们想看对应的有木有右括号。因为d[i + 1]代表的sequence肯定是左括号开头右括号结尾,所以我们想catch((…))这种情况。j = i + 1 + d[i + 1],正好就是str[i]后面越过完整sequence的下一个,若是右括号,d[i] = 2 + d[i + 1]
- 除此之外,还有包起来后因为把后面的右括号用了而导致跟再往后也连起来了的情况,如((..))()()()。所以d[i]还要再加上j后面那一段的d[j + 1]
这个定义和最长公共字串那题的定义类似,都是“以某个固定位置开始/结束”。看两头的方式又像palindrome。从后往前的一维dp也不常见。挺好玩的,一下复习好多东西。
public int longestValidParentheses(String s) { if (s.length() == 0) return 0; int maxLen = 0; int[] d = new int[s.length()]; // d[i] means substring starts with i has max valid lenth of d[i] d[s.length() - 1] = 0; for (int i = s.length() - 2; i >= 0; i--) { if (s.charAt(i) == ')') d[i] = 0; else { int j = (i + 1) + d[i + 1]; if (j < s.length() && s.charAt(j) == ')') { d[i] = d[i + 1] + 2; //(()())的外包情况 if (j + 1 < s.length()) d[i] += d[j + 1];//()()的后面还有的情况 } } maxLen = Math.max(maxLen, d[i]); } return maxLen; }
—————————-用stack的做法———————————–
- stack里面装的一直是“还没配好对的那些可怜的括号的index”
- 是'(‘的时候push
- 是’)’的时候,说明可能配对了;看stack top是不是左括号,不是的话,push当前右括号
- 是的话,pop那个配对的左括号,然后update res:i和top的(最后一个配不成对的)index相减,就是i属于的这一段的当前最长。如果一pop就整个栈空了,说明前面全配好对了,那res就是最大=i+1
public int longestValidParentheses(String s) { int res = 0; Stack<Integer> stack = new Stack<Integer>(); char[] arr = s.toCharArray(); for (int i = 0; i < arr.length; i++) { if (arr[i] == ')' && !stack.isEmpty() && arr[stack.peek()] == '(') { stack.pop(); if (stack.isEmpty()) res = i + 1; else res = Math.max(res, i - stack.peek()); } else { stack.push(i); } } return res; }
这个题是非常常见的面试题,自己试着implement了两种方法,一个O(logn),一个O(n),但是写起来都不是很简洁。
O(n)的方法:binary search by start,找到一个要insert的位置(start1<start<=start2),取那个大的start2(因为它肯定会被start给replace掉,start1则不一定)。插入这个新interval,然后从头到尾检查overlap(两个参数无论谁前谁后都能用这个helper查出来),有就merge,不断吞噬overlap的。代码写起来有点长,不过思路非常清晰,不用考虑任何边界条件(什么a.start <= b.end…那些都烦死人了)。interview的时候可以写这种,因为binary search那段可能都不让你写。
还有一个trick是用iterator可以直接修改正在iterate的list,不会报modification exception,第一次用,很方便。
public ArrayList<Interval> insert(ArrayList<Interval> intervals, Interval newInterval) { int insertPosition = insertByStart(intervals, newInterval.start, 0, intervals.size() - 1); intervals.add(insertPosition, newInterval); Iterator<Interval> it = intervals.iterator(); Interval prev = it.next(); while (it.hasNext()) { Interval curr = it.next(); if (overlap(prev, curr)) { prev.start = Math.min(prev.start, curr.start); prev.end = Math.max(prev.end, curr.end); it.remove(); } else { prev = curr; } } return intervals; } private int insertByStart(ArrayList<Interval> intervals, int x, int p, int q) { if (p > q) return p; int mid = (p + q) / 2; if (intervals.get(mid).start < x) { return insertByStart(intervals, x, mid + 1, q); } else { return insertByStart(intervals, x, p, mid - 1); } } private boolean overlap(Interval a, Interval b) { return within(a.start, b) || within(a.end, b) || within(b.start, a) || within(b.end, a); } private boolean within(int v, Interval b) { return v >= b.start && v <= b.end; }
另一种方法是binary search,找出要start属于的位置,end属于的位置。但是很不好写,需要判断那些this.start >=that.end之类的,就更长了。回头看看能不能写简洁点。
[leetcode] 4Sum | 四个数加和等于x,不许重复
Posted October 18, 2013
on:和3sum一样,多加一维就多n的复杂度,这个是O(n^3),没看到别人有什么好的做法。注意防止duplicate的地方都标红了,算是比较简洁的two pointers防止dup的方式,以后可以都这么些。
public ArrayList<ArrayList> fourSum(int[] num, int target) { ArrayList<ArrayList> result = new ArrayList<ArrayList>(); Arrays.sort(num); for (int i = 0; i < num.length; i++) { if (i > 0 && num[i] == num[i - 1]) continue; for (int j = i + 1; j < num.length; j++) { if (j > i + 1 && num[j] == num[j - 1] ) //一开始写的j > 0,就把1111 x=4的组合给漏掉了 continue; int p = j + 1, q = num.length - 1; while (p < q) { int sum = num[i] + num[j] + num[p] + num[q]; if (sum == target) { ArrayList quad = new ArrayList(); quad.add(num[i]); quad.add(num[j]); quad.add(num[p]); quad.add(num[q]); result.add(quad); do {p++;} while (p < q && num[p] == num[p - 1]); do {q--;} while (p < q && num[q] == num[q + 1]); } else if (sum < target) { p++; } else { q--; } } } } return result; }
本来以为是弱智题,但是要求不能duplicate之后,发现代码还真不好写了。难怪通过率只有16%这么低。
- 如何处理“和上一个一样就不要做了”这种duplicate情况?(形成自己的代码习惯)
- if (i > 0 或类似condition && num[i] == num[i – 1]) continue;
- 这样可以保证永远笼罩在while (some condition)的保证之下,用continue就不会出现越界等情况。
- 如果是递归,外层没有while,则用while (boundary && num[i] == num[i – 1[) {i++;}
public ArrayList<ArrayList> threeSum(int[] num) { ArrayList<ArrayList> result = new ArrayList<ArrayList>(); Arrays.sort(num); for (int i = 0; i < num.length; i++) { if (i > 0 && num[i] == num[i - 1]) continue; int x = 0 - num[i]; int p = i + 1, q = num.length - 1; while (p < q) { if (p > i + 1 && num[p] == num[p - 1]) { p++; continue; } if (q < num.length - 1 && num[q] == num[q + 1]) { q--; continue; } if (num[p] + num[q] == x) { ArrayList triple = new ArrayList(); triple.add(num[i]); triple.add(num[p]); triple.add(num[q]); result.add(triple); } else if (num[p] + num[q] < x) { p++; } else { q--; } } } return result; }
[leetcode] Remove Duplicates From Sorted Array | 排好序的数组inplace的remove dup,不用额外空间
Posted October 11, 2013
on:这题八月轻松过,现在十月看看就彻底不会了。感觉跟array有关的自己想算法的题就能把我吓个好歹。多做多总结!
Given input array A = [1,1,2]
,
Your function should return length = 2
, and A is now [1,2,1]
.(length之后的都不管了)。
算法:
- i从1开始,遇见和上一个不一样的,就放在j指的位置里,j++
- 要是arr[i] == arr[i – 1],i继续往后走
public int removeDuplicates(int[] A) { if (A.length == 0) return 0; int j = 1; for (int i = 1; i < A.length; i++) { if (A[i] != A[i - 1]) { A[j] = A[i]; j++; } } return j; }
bit好久没看真是全尼玛忘了。那些最基本的:
- 显现a的第i位: a & 1 << i
- 把a的第i位置零:a &= ~(1 << i)
- 把result给populate回来:result |= 1 << i
创建一个长度为32的数组a,a[i]表示所有数字在i位出现的次数。假如a[i]是3的整数倍,则忽略;否则就把该位取出来组成答案。空间复杂度O(1).
public int singleNumber(int[] A) { int[] bv = new int[32]; for (int i = 0; i < A.length; i++) { for (int j = 0; j < 32; j++) { bv[j] += (A[i] & (1 << j)) == 0 ? 0 : 1; } } int res = 0; for (int i = 0; i < 32; i++) { if (bv[i] % 3 != 0) { res |= 1 << i; } } return res; }
DP是O(n2)的解法。新开一个数列B,B[i]表示以A[i]结尾的LIS的最大长度。每次在i处往前看,若j<i,则以i结尾的就只少>=B[j] + 1。
public int getLIS(int[] A) { int[] B = new int[A.length]; int max = 1; B[0] = 1; for (int i = 1; i < A.length; i++) { B[i] = 1; for (int j = i - 1; j >= 0; j--) { if (A[i] > A[j]) { B[i] = Math.max(B[i], B[j] + 1); max = Math.max(B[i], max); } } } return max; }