Animal Shelter
宠物收养所
思路:要使各项操作都为O(1),应该使用linkedHashMap(插入查找删除都是O(1),还记录了顺序关系)及两个队列。
public class AnimalShelter { ListNode head; ListNode tail; Queue<Integer> cat_indexs; Queue<Integer> dog_indexs; Map<Integer, ListNode> map; int count; public AnimalShelter() { // do initialize if necessary head = new ListNode(-1, ""); tail = head; cat_indexs = new LinkedList<Integer>(); dog_indexs = new LinkedList<Integer>(); map = new HashMap<Integer, ListNode>(); } /** * @param name a string * @param type an integer, 1 if Animal is dog or 0 * @return void */ void enqueue(String name, int type) { // Write your code here ListNode cur = new ListNode(count, name); cur.pre = tail; tail.next = cur; tail = cur; map.put(count, cur); if (type == 1) { dog_indexs.offer(count); } else { cat_indexs.offer(count); } count++; } public String dequeueAny() { // Write your code here ListNode del = head.next; head.next = del.next; if (del != tail) { del.next.pre = del.pre; } else { tail = del.pre; } map.remove(del.key); if ((!cat_indexs.isEmpty()) && cat_indexs.peek() == del.key) { cat_indexs.poll(); } else { dog_indexs.poll(); } return del.name; } public String dequeueDog() { // Write your code here int dog_key = dog_indexs.poll(); ListNode dog = map.get(dog_key); map.remove(dog_key); dog.pre.next = dog.next; if (dog != tail) { dog.next.pre = dog.pre; } else { tail = dog.pre; } return dog.name; } public String dequeueCat() { // Write your code here int cat_key = cat_indexs.poll(); ListNode cat = map.get(cat_key); map.remove(cat_key); cat.pre.next = cat.next; if (cat != tail) { cat.next.pre = cat.pre; } else { tail = cat.pre; } return cat.name; } } class ListNode { ListNode pre; ListNode next; int key; String name; ListNode(int key, String name) { this.key = key; this.name = name; } }
Basic Calculator II
实现计算器II
思路:这道题没用什么数据结构,放在这为了跟I对比。这种题目就是想清楚碰到每种符号该做什么事:
碰到' ',直接pass;
碰到'+',如果之前有乘除没做,先做乘除,得到当前数,然后把当前数乘以当前符号,加到res上,符号更新为1;
碰到 '-',与碰到+一样,符号更新为-1;
碰到' *', 如果之前有乘除没做,先做乘除,得到当前数,然后把pn记为当前数,po记为'*';
碰到' /',与'*'一样,po记为'/'。
1 public class Solution { 2 public int calculate(String s) { 3 int num = 0; 4 int pn = 0; 5 char po = '0'; 6 int sign = 1; 7 int res = 0; 8 9 for (int i = 0; i < s.length(); i++) { 10 char c = s.charAt(i); 11 if (c == ' ') { 12 continue; 13 } else if (c >= '0' && c <= '9') { 14 num = num * 10 + c - '0'; 15 } else if (c == '+' || c == '-') { 16 num = doMultOrDiv(num, pn, po); 17 po = '0'; 18 res += sign * num; 19 num = 0; 20 sign = c == '+' ? 1 : -1; 21 } else if (c == '*' || c == '/') { 22 num = doMultOrDiv(num, pn, po); 23 pn = num; 24 num = 0; 25 po = c == '*' ? '*' : '/'; 26 } 27 } 28 num = doMultOrDiv(num, pn, po); 29 res += sign * num; 30 return res; 31 } 32 33 public int doMultOrDiv(int num, int pn, char po) { 34 if (po == '*') { 35 return pn * num; 36 } else if (po == '/') { 37 return pn / num; 38 } 39 return num; 40 } 41 }
Contains Duplicate III
检测重复数III
思路:这道题要求找到每个数最接近的数,因此想到使用treemap。treemap大小为k,存储当前结点前面的k个数,找最接近的数就是找小于当前数的最大值和大于当前数的最小值。
1 public class Solution { 2 public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { 3 if (nums == null || nums.length == 0 || t < 0) { 4 return false; 5 } 6 7 TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); 8 for (int i = 0; i <= Math.min(k, nums.length - 1); i++) { 9 if (map.containsKey(nums[i])) { 10 return true; 11 } 12 map.put(nums[i], i); 13 } 14 for (int i = 0; i < nums.length; i++) { 15 Integer lower = map.lowerKey(nums[i]); 16 Integer higher = map.higherKey(nums[i]); 17 18 if (lower != null && (long)nums[i] - (long)lower <= t) { 19 return true; 20 } 21 if (higher != null && (long)higher - (long)nums[i] <= t) { 22 return true; 23 } 24 map.remove(nums[i]); 25 if (i + k + 1 < nums.length) { 26 if (map.containsKey(nums[i + 1 + k])) { 27 return true; 28 } 29 map.put(nums[i + 1 + k], i + k + 1); 30 } 31 } 32 return false; 33 } 34 }
Design Twitter
设计推特
思路: 用hashmap来存储用户与用户之间,用户与推特之间的关系;用一个count来表示时间;获取最近十条twitter有点类似merge k sorted array,需要新定义一个类型存数组号和当前索引,然后用heap。
1 public class Twitter { 2 Map<Integer, Set<Integer>> user2users; 3 Map<Integer, List<Integer>> user2tweets; 4 Map<Integer, Integer> tweet2time; 5 int count; 6 7 class Pair implements Comparable<Pair> { 8 int userId; 9 int curIndex; 10 public Pair(int userId, int curIndex) { 11 this.userId = userId; 12 this.curIndex = curIndex; 13 } 14 public int compareTo(Pair o) { 15 int tweetIdc = user2tweets.get(userId).get(curIndex); 16 int tweetIdo = user2tweets.get(o.userId).get(o.curIndex); 17 int timec = tweet2time.get(tweetIdc); 18 int timeo = tweet2time.get(tweetIdo); 19 return timeo - timec; 20 } 21 } 22 23 /** Initialize your data structure here. */ 24 public Twitter() { 25 user2users = new HashMap<Integer, Set<Integer>>(); 26 user2tweets = new HashMap<Integer, List<Integer>>(); 27 tweet2time = new HashMap<Integer, Integer>(); 28 } 29 30 /** Compose a new tweet. */ 31 public void postTweet(int userId, int tweetId) { 32 if (!user2users.containsKey(userId)) { 33 user2users.put(userId, new HashSet<Integer>()); 34 user2users.get(userId).add(userId); 35 } 36 if (!user2tweets.containsKey(userId)) { 37 user2tweets.put(userId, new ArrayList<Integer>()); 38 } 39 user2tweets.get(userId).add(tweetId); 40 tweet2time.put(tweetId, count++); 41 } 42 43 /** Retrieve the 10 most recent tweet ids in the user's news feed. Each item in the news feed must be posted by users who the user followed or by the user herself. Tweets must be ordered from most recent to least recent. */ 44 public List<Integer> getNewsFeed(int userId) { 45 if (!user2users.containsKey(userId)) { 46 return new ArrayList<Integer>(); 47 } 48 49 PriorityQueue<Pair> q = new PriorityQueue<Pair>(); 50 List<Integer> res = new ArrayList<Integer>(); 51 for (int id : user2users.get(userId)) { 52 List<Integer> tweetList = user2tweets.get(id); 53 if (tweetList != null && tweetList.size() > 0) { 54 q.offer(new Pair(id, tweetList.size() - 1)); 55 } 56 } 57 for (int i = 1; i <= 10; i++) { 58 if (!q.isEmpty()) { 59 Pair cur = q.poll(); 60 res.add(user2tweets.get(cur.userId).get(cur.curIndex)); 61 if (cur.curIndex != 0) { 62 q.offer(new Pair(cur.userId, cur.curIndex - 1)); 63 } 64 } 65 } 66 return res; 67 } 68 69 /** Follower follows a followee. If the operation is invalid, it should be a no-op. */ 70 public void follow(int followerId, int followeeId) { 71 if (!user2users.containsKey(followerId)) { 72 user2users.put(followerId, new HashSet<Integer>()); 73 user2users.get(followerId).add(followerId); 74 } 75 user2users.get(followerId).add(followeeId); 76 } 77 78 /** Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ 79 public void unfollow(int followerId, int followeeId) { 80 if (user2users.containsKey(followerId) && followerId != followeeId) { 81 user2users.get(followerId).remove(followeeId); 82 } 83 } 84 }
Evaluate Reverse Polish Notation
计算后缀表达式(逆波兰表达式)
思路:用栈,以前就见过这题。
1 public class Solution { 2 public int evalRPN(String[] tokens) { 3 Stack<Integer> s = new Stack<Integer>(); 4 for (String item : tokens) { 5 if (item.equals("+")) { 6 s.push(s.pop() + s.pop()); 7 } else if (item.equals("-")) { 8 int a = s.pop(); 9 s.push(s.pop() - a); 10 } else if (item.equals("*")) { 11 s.push(s.pop() * s.pop()); 12 } else if (item.equals("/")) { 13 int a = s.pop(); 14 s.push(s.pop() / a); 15 } else { 16 s.push(Integer.parseInt(item)); 17 } 18 } 19 return s.pop(); 20 } 21 }
Find Right Interval
找到右边的区间
思路:使用treemap。区间的start为key,value是区间的索引。找一个区间的右边第一个区间时,就是在treemap中找大于等于end的第一个区间。
1 /** 2 * Definition for an interval. 3 * public class Interval { 4 * int start; 5 * int end; 6 * Interval() { start = 0; end = 0; } 7 * Interval(int s, int e) { start = s; end = e; } 8 * } 9 */ 10 public class Solution { 11 public int[] findRightInterval(Interval[] intervals) { 12 TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>(); 13 for (int i = 0; i < intervals.length; i++) { 14 map.put(intervals[i].start, i); 15 } 16 int[] res = new int[intervals.length]; 17 for (int i = 0; i < res.length; i++) { 18 int key = intervals[i].end; 19 Integer next = map.containsKey(key) ? key : map.higherKey(key); 20 res[i] = next != null ? map.get(next) : -1; 21 } 22 return res; 23 } 24 25 26 }
Fraction to Recurring Decimal
计算循环小数
思路:本题是用程序模拟人做除法。出现循环小数的情况是:当前的余数前面出现过。因此用一个hashmap来记住余数与其产生的小数位数的映射。为了考虑负数并避免溢出,要使用long类型。
1 public class Solution { 2 public String fractionToDecimal(int numerator, int denominator) { 3 boolean pos = (numerator >= 0 && denominator >= 0) || (numerator <= 0 && denominator <= 0); 4 long num = Math.abs((long)numerator); 5 long den = Math.abs((long)denominator); 6 7 StringBuilder sb = new StringBuilder(); 8 long intPart = num / den; 9 if (!pos) { 10 sb.append("-"); 11 } 12 sb.append(String.valueOf(intPart)); 13 14 long remainder = num % den; 15 if (remainder == 0) { 16 return sb.toString(); 17 } 18 19 sb.append("."); 20 Map<Long, Integer> remainder2index = new HashMap<Long, Integer>(); 21 22 while (remainder != 0) { 23 if (remainder2index.containsKey(remainder)) { 24 sb.insert(remainder2index.get(remainder), "("); 25 sb.append(")"); 26 break; 27 } 28 long nextNumerator = remainder * 10; 29 long decimalPart = nextNumerator / den; 30 remainder2index.put(remainder, sb.length()); 31 remainder = nextNumerator % den; 32 sb.append(String.valueOf(decimalPart)); 33 } 34 return sb.toString(); 35 36 } 37 }
4Sum II
4sum II
思路:遍历A和B每一对,建一个hash表,表示和为key的A[i],B[j]有value对。然后再遍历C和D的每一对,看hash表里面存不存在-(C[i]+D[j])。时间复杂度O(n^2)。
1 public class Solution { 2 public int fourSumCount(int[] A, int[] B, int[] C, int[] D) { 3 int res = 0; 4 int N = A.length; 5 Map<Integer, Integer> ABmap = new HashMap<Integer, Integer>(); 6 for (int i = 0; i < N; i++) { 7 for (int j = 0; j < N; j++) { 8 int ABsum = A[i] + B[j]; 9 if (!ABmap.containsKey(ABsum)) { 10 ABmap.put(ABsum, 0); 11 } 12 ABmap.put(ABsum, ABmap.get(ABsum) + 1); 13 } 14 } 15 16 for (int i = 0; i < N; i++) { 17 for (int j = 0; j < N; j++) { 18 int CDsum = C[i] + D[j]; 19 if (ABmap.containsKey(-CDsum)) { 20 res += ABmap.get(-CDsum); 21 } 22 } 23 } 24 return res; 25 } 26 }
Hash Function
哈希函数
思路:由于hashcode("abcd") = (ascii(a) * 33^3+ascii(b)*33^2+ascii(c)*33+ascii(d)*1) % hashsize。当字符串达到一定长度时,直接计算一定会溢出。这时需要用到以下几个模运算的法则:
(a+b)%c = (a%c+b%c)%c
(a*b)%c = (a%c * b%c)%c
class Solution { /** * @param key: A String you should hash * @param HASH_SIZE: An integer * @return an integer */ public int hashCode(char[] key,int HASH_SIZE) { // write your code here int factor = 33 % HASH_SIZE; long cur33nModSzie = 1; long mod_sum = 0; for (int i = key.length - 1; i >= 0; i--) { if (i != key.length - 1) { cur33nModSzie = (factor * cur33nModSzie) % HASH_SIZE; } long tmp = (key[i] % HASH_SIZE) * cur33nModSzie; mod_sum += tmp % HASH_SIZE; } return (int)(mod_sum % HASH_SIZE); } }
Heapify
堆化
思路:把根节点的左右子堆分别堆化出来,然后再把根节点作siftdown就把整个堆堆化出来了。
public class Solution { /** * @param A: Given an integer array * @return: void */ public void heapify(int[] A) { // write your code here heapify_helper(A, 0); } public void heapify_helper(int[] A, int root) { if (root * 2 + 1 <= A.length - 1) { heapify_helper(A, root * 2 + 1); } if (root * 2 + 2 <= A.length - 1) { heapify_helper(A, root * 2 + 2); } int hole = root; int left = hole * 2 + 1; int right = hole * 2 + 2; while (right <= A.length - 1 && (A[hole] > A[left] || A[hole] > A[right])) { int tmp = A[hole]; int next_hole = A[left] < A[right] ? left : right; A[hole] = A[next_hole]; A[next_hole] = tmp; hole = next_hole; left = hole * 2 + 1; right = hole * 2 + 2; } if (left <= A.length - 1 && A[hole] > A[left]) { int tmp = A[hole]; A[hole] = A[left]; A[left] = tmp; } } }
Isomorphic Strings
同构字符串
思路:用hash map和set。
1 public class Solution { 2 public boolean isIsomorphic(String s, String t) { 3 Set<Character> set = new HashSet<Character>(); 4 Map<Character, Character> map = new HashMap<Character, Character>(); 5 6 for (int i = 0; i < s.length(); i++) { 7 char char_s = s.charAt(i); 8 char char_t = t.charAt(i); 9 if (map.containsKey(char_s)) { 10 if (map.get(char_s) != char_t) { 11 return false; 12 } 13 } else { 14 if (set.contains(char_t)) { 15 return false; 16 } 17 map.put(char_s, char_t); 18 set.add(char_t); 19 } 20 } 21 return true; 22 } 23 }
Implement Queue by Two Stacks
队列实现栈
思路:入队时压入stack1,出队时检查stack2,若stack2为空则将stack1中所有元素压入stack2,若不为空则弹出元素。
public class Queue { private Stack<Integer> stack1; private Stack<Integer> stack2; public Queue() { // do initialization if necessary stack1 = new Stack<Integer>(); stack2 = new Stack<Integer>(); } public void push(int element) { // write your code here stack1.push(element); } public int pop() { // write your code here if (stack2.isEmpty()) { moveStack1toStack2(stack1, stack2); } return stack2.pop(); } public int top() { // write your code here if (stack2.isEmpty()) { moveStack1toStack2(stack1, stack2); } return stack2.peek(); } public void moveStack1toStack2(Stack<Integer> s1, Stack<Integer> s2) { while(!s1.isEmpty()) { s2.push(s1.pop()); } } }
LRU Cache
LRU缓存
思路:使用linkedHashMap,每次操作就把当前结点移到链表尾部,删除元素时删除链表头部的元素。
public class Solution { Map<Integer, ListNode> map; ListNode head; ListNode tail; int capacity; int size; // @param capacity, an integer public Solution(int capacity) { // write your code here map = new HashMap<Integer, ListNode>(); head = new ListNode(0, -1); tail = new ListNode(0, -1); this.capacity = capacity; } // @return an integer public int get(int key) { // write your code here ListNode cur = map.get(key); if (cur == null) { return -1; } move2End(cur); return cur.val; } // @param key, an integer // @param value, an integer // @return nothing public void set(int key, int value) { // write your code here if (map.containsKey(key)) { ListNode cur = map.get(key);; cur.val = value; move2End(cur); } else { if (size < capacity) { size++; } else { //delete dummy's next ListNode del = head.next; head.next = del.next; del.next.pre = head; map.remove(del.key); } //put in the end ListNode newData = new ListNode(value, key); ListNode preTail = (tail.pre == null) ? head : tail.pre; preTail.next = newData; tail.pre = newData; newData.pre = preTail; newData.next = tail; map.put(key, newData); } } private void move2End(ListNode cur) { cur.pre.next = cur.next; cur.next.pre = cur.pre; ListNode preTail = tail.pre; preTail.next = cur; tail.pre = cur; cur.pre = preTail; cur.next = tail; } } class ListNode { int val; int key; ListNode next; ListNode pre; ListNode(int val, int key) { this.val = val; this.key = key; } }
LFU Cache
LFU缓存
思路:使用hash表和平衡树结合,这里用平衡树是因为它 找最小值 以及 删除任意节点 性能都是O(lgn),这是堆和哈希表达不到的。
public class LFUCache { Map<Integer, TreeSetElement> map; TreeSet<TreeSetElement> set; long TIME_STAMP; int capacity; // @param capacity, an integer public LFUCache(int capacity) { // Write your code here map = new HashMap<Integer, TreeSetElement>(); set = new TreeSet<TreeSetElement>(); this.capacity = capacity; } // @param key, an integer // @param value, an integer // @return nothing public void set(int key, int value) { // Write your code here if (map.containsKey(key)) { TreeSetElement cur = map.get(key); TreeSetElement copy = new TreeSetElement(TIME_STAMP++, cur.count + 1, key, value); map.put(key, copy); set.remove(cur); set.add(copy); } else { if (map.size() < capacity) { TreeSetElement cur = new TreeSetElement(TIME_STAMP++, 1, key, value); map.put(key, cur); set.add(cur); } else { TreeSetElement min = set.first(); set.remove(min); map.remove(min.hashMapKey); TreeSetElement cur = new TreeSetElement(TIME_STAMP++, 1, key, value); map.put(key, cur); set.add(cur); } } } public int get(int key) { // Write your code here if (map.containsKey(key)) { TreeSetElement cur = map.get(key); TreeSetElement copy = new TreeSetElement(TIME_STAMP++, cur.count + 1, key, cur.value); map.put(key, copy); set.remove(cur); set.add(copy); return cur.value; } else { return -1; } } } class TreeSetElement implements Comparable<TreeSetElement>{ long time_stamp; int count; int hashMapKey; int value; TreeSetElement(long time_stamp, int count, int hashMapKey, int value) { this.time_stamp = time_stamp; this.count = count; this.hashMapKey = hashMapKey; this.value = value; } public int compareTo(TreeSetElement e) { if (count < e.count) { return -1; } else if (count > e.count) { return 1; } else { if (time_stamp > e.time_stamp) { return 1; } else if (time_stamp < e.time_stamp) { return -1; } else { return 0; } } } }
Longest Consecutive Sequence
最长连续序列
思路:还没做出来。。
Largest Rectangle in Histogram
直方图最大矩形覆盖
思路: 使用单调栈。每个柱形弹出单调栈时可以知道左边第一个低于它的柱形以及右边第一个低于它的柱形,可算出最大面积。
public class Solution { /** * @param height: A list of integer * @return: The area of largest rectangle in the histogram */ public int largestRectangleArea(int[] height) { // write your code here int res = 0; Stack<Integer> s = new Stack<Integer>(); for (int i = 0; i <= height.length; i++) { int cur = i == height.length ? -1 : height[i]; while (!s.isEmpty() && height[s.peek()] >= cur) { int h = height[s.pop()]; int w = s.isEmpty() ? i : i - s.peek() - 1; res = Math.max(res, w * h); } s.push(i); } return res; } }
Maximal Rectangle
最大矩形
思路:这个问题可以转化为对每一行使用“直方图最大矩形覆盖”算法来求最大矩形。时间复杂度为O(m * n)。
1 public class Solution { 2 public int maximalRectangle(char[][] matrix) { 3 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 4 return 0; 5 } 6 7 int m = matrix.length; 8 int n = matrix[0].length; 9 int[][] HistogramMat = new int[m][n]; 10 for (int j = 0; j < n; j++) { 11 HistogramMat[0][j] = matrix[0][j] - '0'; 12 } 13 for (int i = 1; i < m; i++) { 14 for (int j = 0; j < n; j++) { 15 HistogramMat[i][j] = matrix[i][j] == '1' ? HistogramMat[i - 1][j] + 1 : 0; 16 } 17 } 18 19 int res = Integer.MIN_VALUE; 20 Stack<Integer> s = new Stack<Integer>(); 21 for (int i = 0; i < m; i++) { 22 s.clear(); 23 for (int j = 0; j <= n; j++) { 24 int num = j == n ? -1 : HistogramMat[i][j]; 25 while (!s.isEmpty() && HistogramMat[i][s.peek()] >= num) { 26 int h = HistogramMat[i][s.pop()]; 27 int w = s.isEmpty() ? j : j - s.peek() - 1; 28 res = Math.max(res, h * w); 29 } 30 s.push(j); 31 } 32 } 33 return res; 34 } 35 }
Max Tree
最大树
思路:(1)对于任意一个数组元素,它的左子树的范围一直到它左边第一个比它大的树,右子树的范围一直到它右边第一个比它大的树;
(2)在结论(1)的基础上,对于情形[......b......a......c......],其中,b是a左边第一个比a大的数,c是a右边第一个比a大的数:
若 b < c , 则a一定是b的右儿子;若b>c,则a一定是c的左儿子;
(3)在涉及到求数组中每个数的左(右)两边第一个比它大(小)的数时,联想到单调栈。单调栈的性质是,每个栈元素a出栈时,下一个栈顶元素是a的左边界,使a出栈的元素b是a的右边界。
(4)基于上,每个元素出栈时,都可以找到它的父亲节点并作连接,最后用压入一个无穷大,使得所有元素出栈。最终返回这个无穷大节点的左子树即为max tree。
/** * Definition of TreeNode: * public class TreeNode { * public int val; * public TreeNode left, right; * public TreeNode(int val) { * this.val = val; * this.left = this.right = null; * } * } */ public class Solution { /** * @param A: Given an integer array with no duplicates. * @return: The root of max tree. */ public TreeNode maxTree(int[] A) { // write your code here Stack<TreeNode> s = new Stack<TreeNode>(); for (int i = 0; i <= A.length; i++) { int cur = (i == A.length) ? Integer.MAX_VALUE : A[i]; TreeNode right = new TreeNode(cur); while (!s.isEmpty() && s.peek().val < cur) { TreeNode nowNode = s.pop(); if (!s.isEmpty()) { TreeNode left = s.peek(); if (left.val < right.val) { left.right = nowNode; } else { right.left = nowNode; } } else { right.left = nowNode; } } s.push(right); } return s.peek().left; } }
Merge k Sorted Arrays
合并k个数组
思路:用最小堆。注意堆中存储的元素还要记录该元素所在的行和列,便于知道删除它后下一个进来的是谁。
public class Solution { /** * @param arrays k sorted integer arrays * @return a sorted array */ public List<Integer> mergekSortedArrays(int[][] arrays) { // Write your code here ArrayList<Integer> results = new ArrayList<Integer>(); if (arrays == null || arrays.length == 0) { return results; } int m = arrays.length; int n = arrays[0].length; PriorityQueue<Element> minHeap = new PriorityQueue<Element>(); for (int i = 0; i < m; i++) { if (arrays[i].length == 0) { continue; } minHeap.offer(new Element(arrays[i][0], i, 0)); } while (!minHeap.isEmpty()) { Element top = minHeap.poll(); results.add(top.val); if (top.col + 1 < arrays[top.row].length) { minHeap.add(new Element(arrays[top.row][top.col + 1], top.row, top.col + 1)); } } return results; } } class Element implements Comparable<Element> { int val; int row; int col; public Element(int val, int row, int col) { this.val = val; this.row = row; this.col = col; } public int compareTo(Element eleB) { return val - eleB.val; } }
Number of Boomerangs
Boomerangs的数目
思路:两重循环。把每个点作为Boomerangs中的第一个点,建一个hash表,表示到该点距离为Key的点有value个。时间复杂度O(n^2)。
1 public class Solution { 2 public int numberOfBoomerangs(int[][] points) { 3 int res = 0; 4 for (int i = 0; i < points.length; i++) { 5 Map<Long, Integer> map = new HashMap<Long, Integer>(); 6 for (int j = 0; j < points.length; j++) { 7 if (i == j) { 8 continue; 9 } 10 long diff1 = points[i][0] - points[j][0]; 11 long diff2 = points[i][1] - points[j][1]; 12 13 long distance = diff1 * diff1 + diff2 * diff2; 14 if (!map.containsKey(distance)) { 15 map.put(distance, 0); 16 } 17 map.put(distance, map.get(distance) + 1); 18 } 19 for (long key : map.keySet()) { 20 int val = map.get(key); 21 res += val * (val - 1); 22 } 23 } 24 return res; 25 } 26 }
Super Ugly Number
超级丑数
思路1:与丑数II这道题一样。如果用heap时间复杂度就是O(knlgn),过不了leetcode最后两个大的test case。
1 public class Solution { 2 public int nthSuperUglyNumber(int n, int[] primes) { 3 PriorityQueue<Long> q = new PriorityQueue<Long>(); 4 Set<Long> set = new HashSet<Long>(); 5 q.offer(1L); 6 int count = 0; 7 while(!q.isEmpty()) { 8 long cur = q.poll(); 9 if (++count == n) { 10 return (int)cur; 11 } 12 for (int prime : primes) { 13 if (!set.contains(cur * prime)) { 14 q.offer(cur * prime); 15 set.add(cur * prime); 16 } 17 } 18 } 19 return 0; 20 } 21 }
思路2:与丑数II的思路2一样的方法,只不过p1,p2,p3用一个索引数组indexs[]来代替。时间复杂度O(nk)。
1 public class Solution { 2 public int nthSuperUglyNumber(int n, int[] primes) { 3 int[] uglys = new int[n]; 4 uglys[0] = 1; 5 int[] indexs = new int[primes.length]; 6 int last = 1; 7 8 for (int i = 1; i < n; i++) { 9 for (int j = 0; j < indexs.length; j++) { 10 while (primes[j] * uglys[indexs[j]] <= last) { 11 indexs[j]++; 12 } 13 } 14 last = Integer.MAX_VALUE; 15 for (int j = 0; j < indexs.length; j++) { 16 last = Math.min(last, primes[j] * uglys[indexs[j]]); 17 } 18 uglys[i] = last; 19 } 20 return uglys[n - 1]; 21 } 22 }
Stack Sorting
栈排序
思路:一个栈作为单调栈,另一个栈作为辅助栈。不断把辅助栈中的元素压入单调栈,单调栈弹出的元素存回辅助栈。直到所有元素都压入单调栈为止。
public class Solution { /** * @param stack an integer stack * @return void */ public void stackSorting(Stack<Integer> stack) { // Write your code here Stack<Integer> stack2 = new Stack<Integer>(); while (!stack.isEmpty()) { int cur = stack.pop(); while (!stack2.isEmpty() && stack2.peek() < cur) { stack.push(stack2.pop()); } stack2.push(cur); } while (!stack2.isEmpty()) { stack.push(stack2.pop()); } } }
Top k Largest Numbers
最大的k个数
思路1:用最大堆,时间复杂度为
思路2:用最小堆,时间复杂度为
思路3:快速选择算法,时间复杂度为
Top k Largest Numbers II
最大的k个数II
思路:流式数据版的topk,用一个最小堆存当前的topk。
public class Solution { int k; PriorityQueue<Integer> minHeap; public Solution(int k) { // initialize your data structure here. this.k = k; minHeap = new PriorityQueue<Integer>(); } public void add(int num) { // Write your code here if (minHeap.size() < k) { minHeap.add(num); } else { if (minHeap.peek() < num) { minHeap.poll(); minHeap.add(num); } else { return; } } } public List<Integer> topk() { // Write your code here List<Integer> res = new ArrayList<Integer>(); for (Integer num : minHeap) { res.add(num); } Collections.sort(res, Collections.reverseOrder()); return res; } }
Top K Frequent Words
最高频k个单词
思路:类似于TOP K ii(流式数据topk)这道题,即维护一个最小堆,这个最小堆里保存着当前的TOPk。
public class Solution { /** * @param words an array of string * @param k an integer * @return an array of string */ public String[] topKFrequentWords(String[] words, int k) { // Write your code here String[] res = new String[k]; if (k == 0) { return res; } Map<String, Integer> map = new HashMap<String, Integer>(); for (String word : words) { int value = map.get(word) == null ? 1 : map.get(word) + 1; map.put(word, value); } PriorityQueue<Element> q = new PriorityQueue<Element>(); for (Map.Entry<String, Integer> entry : map.entrySet()) { Element e = new Element(entry.getKey(), entry.getValue()); if (q.size() < k) { q.offer(e); } else { if (q.peek().compareTo(e) < 0) { q.poll(); q.offer(e); } } } for (int i = k-1; i >= 0; i--) { res[i] = q.poll().key; } return res; } } class Element implements Comparable<Element> { String key; int value; Element(String key, int value) { this.key = key; this.value = value; } public int compareTo(Element e) { if (value > e.value) { return 1; } else if (value < e.value) { return -1; } else { if (dictOrderCompare(key, e.key) > 0) { return 1; } else { return -1; } } } private int dictOrderCompare(String s1, String s2) { int p1 = 0; int p2 = 0; while (p1 <= s1.length() - 1 && p2 <= s2.length() - 1) { if (s1.charAt(p1) < s2.charAt(p2)) { return 1; } else if (s1.charAt(p1) > s2.charAt(p2)) { return -1; } else { p1++; p2++; } } if (p1 <= s1.length() - 1) { return -1; } if (p2 <= s2.length() - 1) { return 1; } return 0; } }
Ugly Number II
丑数II
思路1:使用heap。时间复杂度O(nlgn)。
1 class Solution { 2 /** 3 * @param n an integer 4 * @return the nth prime number as description. 5 */ 6 public int nthUglyNumber(int n) { 7 // Write your code here 8 PriorityQueue<Long> minHeap = new PriorityQueue<Long>(); 9 Set<Long> set = new HashSet<Long>(); 10 minHeap.offer(1L); 11 for (int i = 1; i <= n; i++){ 12 long cur = minHeap.poll(); 13 if (i == n) { 14 return (int)cur; 15 } 16 if (!set.contains(cur * 2)) { 17 minHeap.offer(cur * 2); 18 set.add(cur * 2); 19 } 20 if (!set.contains(cur * 3)) { 21 minHeap.offer(cur * 3); 22 set.add(cur * 3); 23 } 24 if (!set.contains(cur * 5)) { 25 minHeap.offer(cur * 5); 26 set.add(cur * 5); 27 } 28 } 29 return -1; 30 } 31 };
思路2:使用一个list记录丑数,三个指针p1,p2,p3对应于已经输出的丑数list(p1),list(p2),list(p3),分别表示2,3,5的因子。三个乘积list(p1)*2,list(p2)*3,list(p3)*5应该大于上一个丑数,这三个乘积的最小值就是下一个丑数。时间复杂度O(n)。
1 public class Solution { 2 public int nthUglyNumber(int n) { 3 List<Integer> uglys = new ArrayList<Integer>(); 4 uglys.add(1); 5 int p1 = 0; 6 int p2 = 0; 7 int p3 = 0; 8 9 int last = 1; 10 11 for (int i = 1; i < n; i++) { 12 while (uglys.get(p1) * 2 <= last) { 13 p1++; 14 } 15 while (uglys.get(p2) * 3 <= last) { 16 p2++; 17 } 18 while (uglys.get(p3) * 5 <= last) { 19 p3++; 20 } 21 last = Math.min(Math.min(uglys.get(p1) * 2, uglys.get(p2) * 3), uglys.get(p3) * 5); 22 uglys.add(last); 23 } 24 return uglys.get(n - 1); 25 } 26 }
Valid Anagram
有效的Anagram
思路1:排序然后一一比较。时间复杂度O(nlgn),空间复杂度O(1)。
思路2:hash。这里用数组,因为只有26个字母。时间复杂度O(n),空间复杂度O(1)。
1 public class Solution { 2 public boolean isAnagram(String s, String t) { 3 if (s == null || t == null || s.length() != t.length()) { 4 return false; 5 } 6 int[] count = new int[26]; 7 for (int i = 0; i < s.length(); i++) { 8 char c = s.charAt(i); 9 count[c - 'a']++; 10 } 11 for (int i = 0; i < t.length(); i++) { 12 char c = t.charAt(i); 13 if (count[c - 'a'] == 0) { 14 return false; 15 } 16 count[c - 'a']--; 17 } 18 return true; 19 20 } 21 }