数据结构:可以认为是一个集合,并且提供集合上的若干操作。
队列 Queue
支持操作:O(1) Push / O(1) Pop / O(1) Top BFS的主要数据结构,多联系BFS的相关题目即可
栈 Stack
支持操作:O(1) Push / O(1) Pop / O(1) Top 非递归实现DFS的主要数据结构
1.expression-expand(表达式展开)
给定一个表达式包括数字,字母和括号。括号内的数字表示重复次数(可以是字符串或另一个表达式)。请将表达式扩展为字符串。例:
s = abc3[a]
return abcaaa
s = 3[abc]
return abcabcabc
s = 4[ac]dy
, return acacacacdy
s = 3[2[ad]3[pf]]xyz
, return adadpfpfpfadadpfpfpfadadpfpfpfxyz
使用栈的解法:
public class Solution { /** * @param s an expression includes numbers, letters and brackets * @return a string */ public String expressionExpand(String s) { // Write your code here Stack<Object> stack = new Stack<>(); int number = 0; for (char c : s.toCharArray()) { if (Character.isDigit(c)) { number = number * 10 + c - '0'; } else if (c == '[') { stack.push(Integer.valueOf(number)); number = 0; } else if (c == ']') { String newStr = popStack(stack); Integer count = (Integer) stack.pop(); for (int i = 0; i < count; i++) { stack.push(newStr); } } else { stack.push(String.valueOf(c)); } } return popStack(stack); } private String popStack(Stack<Object> stack) { Stack<String> buffer = new Stack<>(); while (!stack.isEmpty() && (stack.peek() instanceof String)) { buffer.push((String) stack.pop()); } StringBuilder sb = new StringBuilder(); while (!buffer.isEmpty()) { sb.append(buffer.pop()); } return sb.toString(); } }
注意:从左往右扫描表达式中的每个元素,遇到数字就求和记为number(初始化为0),遇到[就把当前number放入栈中然后number归0,遇到字母就以String类型入栈(String.valueOf(c)),遇到]就调用popStack()函数找到栈顶部分非数字子字符串然后展开。最后返回popStack(stack)即可。
问:如何反转栈里的元素?在popStack()函数里使用新栈buffer辅助
递归解法:(更合适,但是可能嵌套很多会产生stackoverflow[当应用程序递归太深而发生堆栈溢出])
public class Solution { /** * @param s an expression includes numbers, letters and brackets * @return a string */ public String expressionExpand(String s) { // Write your code here int number = 0; int paren = 0; String subString = ""; StringBuilder sb = new StringBuilder(); for (char c : s.toCharArray()) { if (c == '[') { if (paren > 0) { subString = subString + c; } paren++; } else if (c == ']') { paren--; if (paren == 0) { // push number * substring to sb String expandedString = expressionExpand(subString); for (int i = 0; i < number; i++) { sb.append(expandedString); } number = 0; subString = ""; } else { subString = subString + c; } } else if (c >= '0' && c <= '9') { if (paren == 0) { number = number * 10 + c - '0'; } else { subString = subString + c; } } else { if (paren == 0) { sb.append(String.valueOf(c)); } else { subString = subString + c; } } } return sb.toString(); } }
2.implement-queue-by-two-stacks(用两个栈实现队列)
正如标题所述,你需要使用两个栈来实现队列的一些操作。队列应支持push(element),pop() 和 top(),其中pop是弹出队列中的第一个(最前面的)元素。pop和top方法都应该返回第一个元素的值,但top()不弹出。
public class MyQueue { private Stack<Integer> stack1; private Stack<Integer> stack2; public MyQueue() { // do initialization if necessary stack1 = new Stack<Integer>(); stack2 = new Stack<Integer>(); } private void stack2ToStack1() { while (!stack2.isEmpty()) { stack1.push(stack2.pop()); } } public void push(int element) { // write your code here stack2.push(element); } public int pop() { // write your code here if (stack1.isEmpty()) { this.stack2ToStack1(); } return stack1.pop(); } public int top() { // write your code here if (stack1.isEmpty()) { this.stack2ToStack1(); } return stack1.peek(); } }
注意:队列先进先出,而栈是先进后出,所以需要两个栈(其中一个用于辅助反转栈中的元素,实现先进先出)。push操作直接存入stack2;pop操作如果stack1为空,就将stack2中的元素pop后存入stack1(实现先进先出),然后pop出stack1的栈顶元素;top操作如果stack1为空,就将stack2中的元素pop后存入stack1(实现先进先出),然后peek出stack1的栈顶元素。
3.implement-stack-by-two-queues(用两个队列实现栈)
正如标题所述,你需要使用两个队列来实现栈的一些操作。栈应支持push(element),pop() ,top()和isEmpty()。
class Stack { Queue<Integer> queue1; Queue<Integer> queue2; // Push a new item into the stack public Stack() { queue1 = new LinkedList<Integer>(); queue2 = new LinkedList<Integer>(); } private void moveItems() { while (queue1.size() != 1) { queue2.offer(queue1.poll()); } } private void swapQueue() { Queue<Integer> temp = queue1; queue1 = queue2; queue2 = temp; } public void push(int x) { // Write your code here queue1.offer(x); } // Pop the top of the stack public void pop() { // Write your code here moveItems(); queue1.poll(); swapQueue(); } // Return the top of the stack public int top() { // Write your code here moveItems(); int item = queue1.poll(); swapQueue(); queue1.offer(item); return item; } // Check the stack is empty or not. public boolean isEmpty() { // Write your code here return queue1.isEmpty(); } }
注意:栈先进后出,而队列是先进先出,所以需要两个队列(其中一个用于辅助记录原队列中最后一个元素之前的所有元素,实现后进先出)。push操作直接存入queue1;pop操作使用moveItems()函数将queue1队列中除最后一个元素之外的所有其他元素都放入queue2,然后弹出queue1中最后一个元素实现后进先出,最后使用swapQueue()函数交换queue1和queue2;top操作使用moveItems()函数将queue1队列中除最后一个元素之外的所有其他元素都放入queue2,然后弹出queue1中最后一个元素做记录(item)之后,交换queue1和queue2并将queue1复原,返回item;isEmpty操作返回queue1是否为空即可。
4.min-stack(带最小值操作的栈)
实现一个带有取最小值min方法的栈,min方法将返回当前栈中的最小值。你实现的栈将支持push,pop 和 min 操作,所有操作要求都在O(1)时间内完成。如果堆栈中没有数字则不能进行min方法的调用。
public class MinStack { Stack<Integer> stack; Stack<Integer> minStack; public MinStack() { // do initialize if necessary stack = new Stack<Integer>(); minStack = new Stack<Integer>(); } public void push(int number) { // write your code here stack.push(number); if (minStack.isEmpty()) { minStack.push(number); } else { minStack.push(Math.min(number, minStack.peek())); } } public int pop() { // write your code here minStack.pop(); return stack.pop(); } public int min() { // write your code here return minStack.peek(); } }
注意:使用minStack只存储stack中的较小值。min操作返回minStack的栈顶元素;push操作将元素直接放入stack中,若minStack为空则直接放入minStack中,若不为空则放入当前元素和minStack栈顶元素的较小值;pop操作弹出minStack的栈顶元素,返回stack的栈顶元素。
iterator迭代器是一种设计模式,它是一个对象,可以遍历并选择序列中的对象,而开发人员不需要了解该序列的底层结构。
(1)iterator()方式是Java.lang.Iterator接口,被Collection实现,使用方法iterator()要求容器返回一个iterator。
(2)第一次调用Iterator的next()方法时,它返回序列的第一个元素,之后都会获得[取出]序列中的下一个元素。
(3)使用hasNext()检查序列中是否还有元素[不取出]。
(4)使用remove()将迭代器新返回的元素删除。
5.flatten-nested-list-iterator(摊平嵌套的列表)
给你一个嵌套的列表,实现一个迭代器将其摊平。一个列表的每个元素可能是整数或者一个列表。你不需要实现remove方法。
样例:给出列表 [[1,1],2,[1,1]]
,经过迭代器之后返回 [1,1,2,1,1]
。
/** * // This is the interface that allows for creating nested lists. * // You should not implement it, or speculate about its implementation * public interface NestedInteger { * * // @return true if this NestedInteger holds a single integer, * // rather than a nested list. * public boolean isInteger(); * * // @return the single integer that this NestedInteger holds, * // if it holds a single integer * // Return null if this NestedInteger holds a nested list * public Integer getInteger(); * * // @return the nested list that this NestedInteger holds, * // if it holds a nested list * // Return null if this NestedInteger holds a single integer * public List<NestedInteger> getList(); * } */ import java.util.Iterator; public class NestedIterator implements Iterator<Integer> { private Stack<NestedInteger> stack; private void pushListToStack(List<NestedInteger> nestedList) { Stack<NestedInteger> temp = new Stack<>(); for (NestedInteger nested : nestedList) { temp.push(nested); } while (!temp.isEmpty()) { stack.push(temp.pop()); } } public NestedIterator(List<NestedInteger> nestedList) { // Initialize your data structure here. stack = new Stack<>(); pushListToStack(nestedList); } // @return {int} the next element in the iteration @Override public Integer next() { // Write your code here if (!hasNext()) { return null; } return stack.pop().getInteger(); } // @return {boolean} true if the iteration has more element or false @Override public boolean hasNext() { // Write your code here while (!stack.isEmpty() && !stack.peek().isInteger()) { pushListToStack(stack.pop().getList()); } return !stack.isEmpty(); } @Override public void remove() {} } /** * Your NestedIterator object will be instantiated and called as such: * NestedIterator i = new NestedIterator(nestedList); * while (i.hasNext()) v.add(i.next()); */
注意:pushListToStack(List<NestedInteger> nestedList)在辅助栈temp[为了实现嵌套列表中元素先进先出]的帮助下将嵌套列表中的元素(整数或一个列表)压入栈stack中。NestedIterator(List<NestedInteger> nestedList)初始化stack并执行pushListToStack()函数。next() 返回栈顶元素。hasNext()返回stack是否为空,注意在判断之前当stack不为空且栈顶元素是列表时要执行pushListToStack()函数。
6.flatten-2d-vector(扁平化2D向量)
实现一个迭代器来扁平化一个2d向量。
样例:给定2d矢量=[[1,2],[3],[4,5,6]]通过重复调用next()直到hasNext()返回false,next()返回的元素的顺序应为:[1,2,3,4,5,6]。
public class Vector2D implements Iterator<Integer> { Stack<List<Integer>> stack = new Stack<>(); Stack<Integer> stackj; void pushListListToStack(List<List<Integer>> vec2d) { Stack<List<Integer>> temp = new Stack<>(); for (List<Integer> nested : vec2d) { temp.push(nested); } while (!temp.isEmpty()) { stack.push(temp.pop()); } } void pushListToStack(List<Integer> vec) { Stack<Integer> temp = new Stack<>(); for (Integer nested : vec) { temp.push(nested); } while (!temp.isEmpty()) { stackj.push(temp.pop()); } } public Vector2D(List<List<Integer>> vec2d) { pushListListToStack(vec2d); // Initialize your data structure here stackj = new Stack<>(); } public Integer next() { // Write your code here if (!hasNext()) { return null; } return stackj.pop(); } public boolean hasNext() { // Write your code here while (stackj.isEmpty() && !stack.isEmpty()) { pushListToStack(stack.pop()); } return !stackj.isEmpty(); } public void remove() {} } /** * Your Vector2D object will be instantiated and called as such: * Vector2D i = new Vector2D(vec2d); * while (i.hasNext()) v[f()] = i.next(); */
注意:栈stack中存放的是列表,栈stackj中存放的是元素。pushListListToStack(List<List<Integer>> vec2d) 在辅助栈temp[为了实现2d向量中列表先进先出]的帮助下将2d向量中的列表压入栈stack中。pushListToStack(List<integer> vec)在辅助栈temp[为了实现列表中元素先进先出]的帮助下将一个列表中的元素(整数)压入栈stackj中。Vector2D(List<List<Integer>> vec2d) 初始化stack,stackj并执行pushListListToStack()函数。next() 返回stackj栈顶元素。hasNext()返回stackj是否为空,注意在判断之前当stack不为空但stackj为空时要执行pushListToStack()函数。
7.zigzag-iterator(左旋右旋迭代器)
给你两个一维向量,实现一个迭代器,交替返回两个向量的元素。
public class ZigzagIterator { /** * @param v1 v2 two 1d vectors */ public Iterator<Integer> it1; public Iterator<Integer> it2; public int count; public ZigzagIterator(List<Integer> v1, List<Integer> v2) { // initialize your data structure here. this.it1 = v1.iterator(); this.it2 = v2.iterator(); count = 0; } public int next() { // Write your code here count++; if ((count % 2 == 1 && it1.hasNext()) || (!it2.hasNext())) { return it1.next(); } else if ((count % 2 == 0 && it2.hasNext()) || (!it1.hasNext())) { return it2.next(); } return -1; } public boolean hasNext() { // Write your code here return it1.hasNext() || it2.hasNext(); } }
注意:使用it1和it2两个迭代器分别操作两个一维,count%2是否等于1来判断取哪个向量中的元素。
8.zigzag-iterator-ii(左旋右旋迭代器II)
k 个一维向量,循环地返回向量中的元素。
样例:k = 3
[1,2,3]
[4,5,6,7]
[8,9]
返回 [1,4,8,2,5,9,3,6,7].
public class ZigzagIterator2 { /** * @param vecs a list of 1d vectors */ public List<Iterator<Integer>> its; public int count; public ZigzagIterator2(ArrayList<ArrayList<Integer>> vecs) { // initialize your data structure here. this.its = new ArrayList<Iterator<Integer>>(); for (List<Integer> vec : vecs) { if (vec.size() > 0) { its.add(vec.iterator()); } } count = 0; } public int next() { // Write your code here int item = its.get(count).next(); if (its.get(count).hasNext()) { count = (count + 1) % its.size(); } else { //删除第count个已经完全被返回的向量 its.remove(count); if (its.size() > 0) { count = count % its.size(); } } return item; } public boolean hasNext() { // Write your code here return its.size() > 0; } } /** * Your ZigzagIterator2 object will be instantiated and called as such: * ZigzagIterator2 solution = new ZigzagIterator2(vecs); * while (solution.hasNext()) result.add(solution.next()); * Output result */
注意:每个一维向量产生一个迭代器,k个迭代器形成一个List。ZigzagIterator2(ArrayList<ArrayList<Integer>> vecs)初始化迭代器的List,并将每个向量的迭代器放入List。next()函数依次取第count(初始化为0)个向量的第(1,2,...)个元素,如果第count个向量hasNext(),count = (count + 1) % its.size();否则就删除第count个向量,
if (its.size() > 0) { count = count % its.size();}。hasNext()函数返回its.size() 是否大于0。
单调栈:栈内元素单调递增或单调递减,单调栈只能在栈顶操作。
9.largest-rectangle-in-histogram(直方图中的最大矩形)【hard】
给定表示每个条的宽度为1的直方图条高的n个非负整数,找到直方图中最大矩形的面积。左图是每个条的宽度为1的直方图,给定height = [2,1,5,6,2,3]。右图中最大矩形显示在阴影区域,面积=10单位。
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 if (height == null || height.length == 0) { return 0; } Stack<Integer> stack = new Stack<Integer>(); int length = height.length; int max = 0; for (int i = 0; i <= length; i++) { int curt = (i == length) ? -1 : height[i]; while (!stack.isEmpty() && curt <= height[stack.peek()]) { int h = height[stack.pop()]; int w = stack.isEmpty() ? i : i - stack.peek() - 1; max = Math.max(max, h * w); } stack.push(i); } return max; } }
注意:使用单调递增栈来处理,栈里存的是index,计算面积时的宽度使用index的差值。假设在整个直方的最后存在一个高度为0的直方,一直计算到index=height.length结束。
详细步骤见笔记。
10.maximal-rectangle(最大矩形)【hard】
给你一个二维矩阵,权值为False
和True
,找到一个最大的矩形,使得里面的值全部为True
,输出它的面积。
样例:给你一个矩阵如下[
[1, 1, 0, 0, 1],
[0, 1, 0, 0, 1],
[0, 0, 1, 1, 1],
[0, 0, 1, 1, 1],
[0, 0, 0, 0, 1]
],
输出6。
样例中的height矩阵如下[
[1, 1, 0, 0, 1],
[0, 2, 0, 0, 2],
[0, 0, 1, 1, 3],
[0, 0, 2, 2, 4],
[0, 0, 0, 0, 5]
]
public class Solution { /** * @param matrix a boolean 2D matrix * @return an integer */ public int maximalRectangle(boolean[][] matrix) { // Write your code here int n = matrix.length; if (n < 1) { return 0; } int m = matrix[0].length; if (m == 0) { return 0; } int[][] height = new int[n][m]; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (i == 0) { height[i][j] = (matrix[i][j]) ? 1 : 0; } else { height[i][j] = (matrix[i][j]) ? height[i - 1][j] + 1 : 0; } } } int answer = 0; for (int i = 0; i < n; i++) { Stack<Integer> stack = new Stack<Integer>(); for (int j = 0; j < m; j++) { while (!stack.isEmpty() && height[i][j] < height[i][stack.peek()]) { int pos = stack.pop(); answer = Math.max(answer, height[i][pos] * (stack.isEmpty() ? j : j - stack.peek() - 1)); } stack.push(j); } //j=m,一直计算到栈空为止,求出到当前行的最大矩形值 while (!stack.isEmpty()) { int pos = stack.pop(); answer = Math.max(answer, height[i][pos] * (stack.isEmpty() ? m : m - stack.peek() - 1)); } } return answer; } }
注意:基于第9题的解法。将矩阵中的每一行看做直方图,立柱的高度(使用height[][]矩阵保存)就是行中元素往上数包含的连续1的个数。每一行都可以计算当前的最大矩形,最后求出各行结果的最大值。height矩阵第一行最大矩形2,第二行最大矩形2,第三行最大矩形3,第四行最大矩形6,第五行最大矩阵5,最后结果为6。
n<1和m==0的情况要分别返回0值。
哈希表 Hash:Hash Table / Hash Map / Hash Set
支持操作:O(1) Insert / O(1) Find / O(1) Delete
Hash Function
使命:对于任意的key,得到一个 固定 且 无规律 的介于0~capacity(容量)-1的整数
11.rehashing(重哈希)
哈希表容量的大小在一开始是不确定的。如果哈希表存储的元素太多(如超过容量的十分之一),我们应该将哈希表容量扩大一倍,并将所有的哈希值重新安排。假设你有如下一哈希表:size=3
, capacity=4
[null, 21, 14, null] ↓ ↓ 9 null ↓ null
哈希函数为:
int hashcode(int key, int capacity) {
return key % capacity;
}
这里有三个数字9,14,21,其中21和9共享同一个位置因为它们有相同的哈希值1(21 % 4 = 9 % 4 = 1)。我们将它们存储在同一个链表中。重建哈希表,将容量扩大一倍,我们将会得到:size=3
, capacity=8
index: 0 1 2 3 4 5 6 7
hash : [null, 9, null, null, null, 21, 14, null]
给定一个哈希表,返回重哈希后的哈希表。哈希表中负整数的下标位置可以通过下列方式计算:C++/Java:如果你直接计算-4 % 3,你会得到-1,你可以应用函数:a % b = (a % b + b) % b得到一个非负整数。Python:你可以直接用-1 % 3,你可以自动得到2。
/** * Definition for ListNode * public class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { /** * @param hashTable: A list of The first node of linked list * @return: A list of The first node of linked list which have twice size */ public ListNode[] rehashing(ListNode[] hashTable) { // write your code here if (hashTable.length <= 0) { return hashTable; } int newcapacity = 2 * hashTable.length; ListNode[] newTable = new ListNode[newcapacity]; //链表型数组中的每一个链表 for (int i = 0; i < hashTable.length; i++) { while (hashTable[i] != null) { int newindex = (hashTable[i].val % newcapacity + newcapacity) % newcapacity; if (newTable[newindex] == null) { newTable[newindex] = new ListNode(hashTable[i].val); } else { ListNode dummy = newTable[newindex]; while (dummy.next != null) { dummy = dummy.next; } dummy.next = new ListNode(hashTable[i].val); } //链表中的下一个元素 hashTable[i] = hashTable[i].next; } } return newTable; } };
注意:先将新ListNode型数组的容量扩大一倍。对于原链表数组中的每一个链表:
for (int i = 0; i < hashTable.length; i++) {
while (hashTable[i] != null) {
的每一个元素:hashTable[i] = hashTable[i].next;,计算其在新链表数组中的位置,如果该位置为空直接放入;如果该位置不为空就连接在该位置最后一个元素的后面。
12.lru-cache(LRU缓存策略)【hard】【高频++】
为最近最少使用(LRU)缓存策略设计一个数据结构,它应该支持以下操作:获取数据(get)和写入数据(set)。
获取数据get(key):如果缓存中存在key,则获取其数据值(通常是正数),否则返回-1。
写入数据set(key, value):如果key还没有在缓存中,则写入其数据值。当缓存达到上限,它应该在写入新数据之前删除最近最少使用的数据用来腾出空闲位置。
public class Solution { private class Node { Node prev; Node next; int key; int value; public Node(int key, int value) { this.key = key; this.value = value; this.prev = null; this.next = null; } } private int capacity; private HashMap<Integer, Node> map = new HashMap<Integer, Node>(); private Node head = new Node(-1, -1); private Node tail = new Node(-1, -1); // @param capacity, an integer public Solution(int capacity) { // write your code here this.capacity = capacity; head.next = tail; tail.prev = head; } // @return an integer public int get(int key) { // write your code here if (!map.containsKey(key)) { return -1; } Node current = map.get(key); //把要获取节点的前后相连 current.next.prev = current.prev; current.prev.next = current.next; //要获取的节点移到链表尾部 move_to_tail(current); return map.get(key).value; } // @param key, an integer // @param value, an integer // @return nothing public void set(int key, int value) { // write your code here //如果key在缓存中且有数据,就更新成新的value返回即可 if (get(key) != -1) { map.get(key).value = value; return; } //如果缓存达到上限,就移除最近最少使用的数据(head之后的数据) if (map.size() == capacity) { map.remove(head.next.key); head.next = head.next.next; head.next.prev = head; } //然后插入新数据,并移到链表尾部 Node insert = new Node(key, value); map.put(key, insert); move_to_tail(insert); } private void move_to_tail(Node current) { current.prev = tail.prev; tail.prev = current; current.prev.next = current; current.next = tail; } }
注意:用一个双向链表来存储节点,节点中包含键值对,HashMap<Integer, Node> map中存储key和节点,通过在map中查找key来定位节点。所有最近被访问的节点放在链表尾部,因此get(key)中需要在查找到节点之后将节点取出(把要获取节点的前后相连)放尾部(move_to_tail()函数)。set(key, value)在写入数据之前,如果key在缓存中且有数据,就更新成新的value返回(return;)即可,如果缓存达到上限,就移除最近最少使用的数据(head之后的数据),然后插入新数据,并移到链表尾部。
13.anagrams(乱序字符串)
给出一个字符串数组S,找到其中所有的乱序字符串(Anagram)。如果一个字符串是乱序字符串,那么他存在一个字母集合相同,但顺序不同的字符串也在S中。所有的字符串都只包含小写字母。
样例:对于字符串数组 ["lint","intl","inlt","code"] 返回 ["lint","inlt","intl"]
public class Solution { /** * @param strs: A list of strings * @return: A list of strings */ private int getHash(int[] count) { int hash = 0; int a = 378551; int b = 63689; for (int num : count) { hash = hash * a + num; a = a * b; } return hash; } public List<String> anagrams(String[] strs) { // write your code here ArrayList<String> result = new ArrayList<String>(); HashMap<Integer, ArrayList<String>> map = new HashMap<Integer, ArrayList<String>>(); for (String str : strs) { int[] count = new int[26]; for (int i = 0; i < str.length(); i++) { count[str.charAt(i) - 'a']++; } int hash = getHash(count); if (!map.containsKey(hash)) { map.put(hash, new ArrayList<String>()); } map.get(hash).add(str); } for (ArrayList<String> tmp : map.values()) { if (tmp.size() > 1) { result.addAll(tmp); } } return result; } }
注意:使用HashMap<Integer, ArrayList<String>> map 存储<一个hash值,hash值相同的所有字符串>。对于字符串数组中的每一个字符串,计算其hash值,值相同的字符串存入map中的相同位置。判断map.values()里所有List的size(),大于1即符合条件。使用addAll()将其全部加入result。
//求字符串的hash值 private int getHash(int[] count) { int hash = 0; int a = 378551; int b = 63689; for (int num : count) { hash = hash * a + num; a = a * b; } return hash; } for (String str : strs) { int[] count = new int[26]; //计算字符串中包含哪些字母及其个数 for (int i = 0; i < str.length(); i++) { count[str.charAt(i) - 'a']++; } int hash = getHash(count); ... }
14.longest-consecutive-sequence(最长连续序列)
给定一个未排序的整数数组,找出最长连续序列的长度。要求你的算法复杂度为O(n)。
样例:给出数组[100, 4, 200, 1, 3, 2],这个最长的连续序列是 [1, 2, 3, 4],返回所求长度 4。
public class Solution { /** * @param nums: A list of integers * @return an integer */ public int longestConsecutive(int[] num) { // write you code here HashSet<Integer> set = new HashSet<Integer>(); for (int i = 0; i < num.length; i++) { set.add(num[i]); } int longest = 0; for (int i = 0; i < num.length; i++) { int down = num[i] - 1; while (set.contains(down)) { set.remove(down); down--; } int up = num[i] + 1; while (set.contains(up)) { set.remove(up); up++; } longest = Math.max(longest, up - down - 1); } return longest; } }
注意:使用HashSet,将数组中的所有元素存入set。对每一个整数num[i],up=num[i]+1,当(while)set中存在up值,就移除这个up值,up++;对每一个整数num[i],
down=num[i]-1,当(while)set中存在down值,就移除这个down值,down--。移除元素是为了避免相同序列的重复计算。最后up-down-1与当前的最长连续序列长度做比较。
堆 Heap
支持操作:O(log N) Add / O(log N) Remove / O(1) Min or Max Max Heap vs Min Heap
push 添加新的元素进入堆 pop 删除堆中的最小/最大元素 top 返回堆中最小/最大元素
vs 优先级队列 PriorityQueue
用Heap实现,没有实现Heap的delete功能,默认是minHeap(堆顶最小)
15.heapify(堆化)
给出一个整数数组,堆化操作就是把它变成一个最小堆数组。对于堆数组A,A[0]是堆的根,并对于每个A[i],A [i * 2 + 1]是A[i]的左儿子并且A[i * 2 + 2]是A[i]的右儿子。
说明
什么是堆?堆是一种数据结构,它通常有三种方法:push,pop 和 top。其中,“push”添加新的元素进入堆,“pop”删除堆中最小/最大元素,“top”返回堆中最小/最大元素。
什么是堆化?把一个无序整数数组变成一个堆数组。如果是最小堆,每个元素A[i],我们将得到A[i * 2 + 1] >= A[i]和A[i * 2 + 2] >= A[i]
如果有很多种堆化的结果?返回其中任何一个。
解法一:时间复杂度o(n)
public class Solution { /** * @param A: Given an integer array * @return: void */ private void siftdown(int[] A, int k) { while (k < A.length) { int smallest = k; if (k * 2 + 1 < A.length && A[k * 2 + 1] < A[smallest]) { smallest = k * 2 + 1; } if (k * 2 + 2 < A.length && A[k * 2 + 2] < A[smallest]) { smallest = k * 2 + 2; } if (smallest == k) { break; } int temp = A[smallest]; A[smallest] = A[k]; A[k] = temp; k = smallest; } } public void heapify(int[] A) { // write your code here for (int i = A.length / 2; i >= 0; i--) { siftdown(A, i); } } }
注意:从数组的中位数开始逐个向前执行siftdown()函数。在siftdown()函数中找到该元素k的左右儿子节点(k*2+1,k*2+2),如果儿子节点值都大于该元素值就直接break,如果某个儿子节点值小于该元素值就交换,当前k赋值为最小儿子节点的位置。
解法二:时间复杂度o(nlogn)
public class Solution { /** * @param A: Given an integer array * @return: void */ private void siftup(int[] A, int k) { while (k != 0) { int father = (k - 1) / 2; if (A[k] > A[father]) { break; } int temp = A[k]; A[k] = A[father]; A[father] = temp; k = father; } } public void heapify(int[] A) { // write your code here for (int i = 0; i < A.length; i++) { siftup(A, i); } } }
注意:对数组中的每个元素执行siftup()函数。在siftup()函数中,找到每个元素k的父亲节点(father=k-1/2),因为是最小堆,如果元素大于父亲节点就直接break,否则就交换该元素和父亲节点值,k赋值为father,继续向上比较,直至k=0为止。
16.ugly-number-ii(丑数II)
设计一个算法,找出只含素因子2
,3
,5
的第 n 大的数。符合条件的数如:1, 2, 3, 4, 5, 6, 8, 9, 10, 12...
时间复杂度o(n)的scan解法[最好]:
class Solution { /** * @param n an integer * @return the nth prime number as description. */ public int nthUglyNumber(int n) { // Write your code here List<Integer> uglys = new ArrayList<Integer>(); uglys.add(1); int p2 = 0; int p3 = 0; int p5 = 0; for (int i = 1; i < n; i++) { int lastNumber = uglys.get(i - 1); while (uglys.get(p2) * 2 <= lastNumber) { p2++; } while (uglys.get(p3) * 3 <= lastNumber) { p3++; } while (uglys.get(p5) * 5 <= lastNumber) { p5++; } uglys.add(Math.min( Math.min(uglys.get(p2) * 2, uglys.get(p3) * 3), uglys.get(p5) * 5 )); } return uglys.get(n - 1); } };
注意:使用List<Integer> uglys记录需要找到的前n个数,初始化时放入1。使用p2,p3,p5(初始化都为0)记录乘以2,3,5后大于当前uglys中最后一个数的位置,每次放入
uglys.get(p2) * 2, uglys.get(p3) * 3,uglys.get(p5) * 5中的最小值。最后返回uglys.get(n-1)即可。
时间复杂度o(nlogn)的HashMap+Heap解法[希望能想到]:
class Solution { /** * @param n an integer * @return the nth prime number as description. */ public int nthUglyNumber(int n) { // Write your code here Queue<Long> Q = new PriorityQueue<Long>(); HashSet<Long> inQ = new HashSet<Long>(); Long[] primes = new Long[3]; primes[0] = Long.valueOf(2); primes[1] = Long.valueOf(3); primes[2] = Long.valueOf(5); for (int i = 0; i < 3; i++) { Q.add(primes[i]); inQ.add(primes[i]); } Long number = Long.valueOf(1); for (int i = 1; i < n; i++) { number = Q.poll(); for (int j = 0; j < 3; j++) { if (!inQ.contains(primes[j] * number)) { Q.add(number * primes[j]); inQ.add(number * primes[j]); } } } return number.intValue(); } };
17.merge-k-sorted-lists(合并k个排序链表)
合并k个排序链表,并且返回合并后的排序链表。尝试分析和描述其复杂度。
分治解法一:
/** * Definition for ListNode. * public class ListNode { * int val; * ListNode next; * ListNode(int val) { * this.val = val; * this.next = null; * } * } */ public class Solution { /** * @param lists: a list of ListNode * @return: The head of one sorted list. */ public ListNode mergeKLists(List<ListNode> lists) { // write your code here if (lists.size() == 0) { return null; } return mergeHelper(lists, 0, lists.size() - 1); } private ListNode mergeHelper(List<ListNode> lists, int start, int end) { if (start == end) { return lists.get(start); } int mid = start + (end - start) / 2; ListNode left = mergeHelper(lists, start, mid); ListNode right = mergeHelper(lists, mid + 1, end); return mergeTwoLists(left, right); } private ListNode mergeTwoLists(ListNode list1, ListNode list2) { ListNode dummy = new ListNode(0); ListNode tail = dummy; while (list1 != null && list2 != null) { if (list1.val < list2.val) { tail.next = list1; tail = list1; list1 = list1.next; } else { tail.next = list2; tail = list2; list2 = list2.next; } } if (list1 != null) { tail.next = list1; } if (list2 != null) { tail.next = list2; } return dummy.next; } }
注意:基本思路是对链表进行两两排序。mergeTwoLists()函数对两个链表进行排序,mergeHelper()函数找到每次要排序的起始链表和结束链表。
Heap(使用PriorityQueue)解法二:
/** * Definition for ListNode. * public class ListNode { * int val; * ListNode next; * ListNode(int val) { * this.val = val; * this.next = null; * } * } */ public class Solution { /** * @param lists: a list of ListNode * @return: The head of one sorted list. */ private Comparator<ListNode> ListNodeComparator = new Comparator<ListNode>() { public int compare(ListNode left, ListNode right) { return left.val - right.val; } }; public ListNode mergeKLists(List<ListNode> lists) { // write your code here if (lists.size() == 0 || lists == null) { return null; } //声明一个优先级队列 Queue<ListNode> heap = new PriorityQueue<ListNode>(lists.size(), ListNodeComparator); //将链表放入优先级队列 for (int i = 0; i < lists.size(); i++) { if (lists.get(i) != null) { heap.add(lists.get(i)); } } ListNode dummy = new ListNode(0); ListNode tail = dummy; while (!heap.isEmpty()) { ListNode head = heap.poll();//取出一个链表 tail.next = head; tail = head; if (head.next != null) { //将该链表中的元素依次放入队列中,优先级队列会自动排序 heap.add(head.next); } } return dummy.next; } }
注意:PriorityQueue的声明方法:Queue<ListNode> heap = new PriorityQueue<ListNode>(lists.size(), ListNodeComparator);
private Comparator<ListNode> ListNodeComparator = new Comparator<ListNode>() {
public int compare(ListNode left, ListNode right) {
return left.val - right.val;
}
};
18.merge-k-sorted-arrays(合并k个排序数组)
将 k 个排序数组合并为一个大的排序数组。
class Element { public int row; public int col; public int val; Element(int row, int col, int val) { this.row = row; this.col = col; this.val = val; } } public class Solution { /** * @param arrays k sorted integer arrays * @return a sorted array */ private Comparator<Element> ElementComparator = new Comparator<Element>() { public int compare(Element left, Element right) { return left.val - right.val; } }; public List<Integer> mergekSortedArrays(int[][] arrays) { // Write your code here if (arrays == null) { return new int[0]; } int total_size = 0; Queue<Element> Q = new PriorityQueue<Element>( arrays.length, ElementComparator); for (int i = 0; i < arrays.length; i++) { if (arrays[i].length > 0) { Element elem = new Element(i, 0, arrays[i][0]); Q.add(elem); total_size += arrays[i].length; } } int[] result = new int[total_size]; int index = 0; while (!Q.isEmpty()) { Element elem = Q.poll(); result[index++] = elem.val; if (elem.col + 1 < arrays[elem.row].length) { elem.col += 1; elem.val = arrays[elem.row][elem.col]; Q.add(elem); } } return result; } }
19.top-k-largest-numbers(前k大数)
在一个数组中找到前K大(从大到小)的数。
解法一:
class Solution { /* * @param nums an integer array * @param k an integer * @return the top k largest numbers in array */ private Comparator<Integer> newComparator = new Comparator<Integer>() { public int compare(Integer a, Integer b) { if (a < b) { return 1; } else if (a > b) { return -1; } else { return 0; } } }; public int[] topk(int[] nums, int k) { // Write your code here PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(k, newComparator); for (int i = 0; i < nums.length; i++) { minHeap.add(nums[i]); } int[] result = new int[k]; for (int i = 0; i < k; i++) { result[i] = minHeap.poll(); } return result; } };
注意:使用PriorityQueue从大到小排序:
public int compare(Integer a, Integer b) {
if (a < b) { return 1;
} else if (a > b) { return -1;
} else { return 0; }
}最后返回队列的前K个数即可。
解法二:
class Solution { /* * @param nums an integer array * @param k an integer * @return the top k largest numbers in array */ private Comparator<Integer> newComparator = new Comparator<Integer>() { public int compare(Integer a, Integer b) { return a - b; } }; public int[] topk(int[] nums, int k) { // Write your code here PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>(k, newComparator); for (int i = 0; i < nums.length; i++) { minHeap.add(nums[i]); if (minHeap.size() > k) { minHeap.poll(); } } int[] result = new int[k]; for (int i = k - 1; i >= 0; i--) { result[i] = minHeap.poll(); } return result; } };
注意:使用PriorityQueue从小到大排序,遍历整个数组,只存储K个数,当队列size大于K时就poll出队列头,最后队列中剩余的就是从小到大排序的数组中的前K大数。
20.top-k-largest-numbers-ii(前k大数II)
实现一个数据结构,提供下面两个接口 1.add(number)
添加一个元素 2.topk()
返回前K大的数。
public class Solution { private int size; private Queue<Integer> minHeap; public Solution(int k) { // initialize your data structure here. minHeap = new PriorityQueue<>(); size = k; } public void add(int num) { // Write your code here if (minHeap.size() < size) { minHeap.offer(num); return; } if (num > minHeap.peek()) { minHeap.poll(); minHeap.offer(num); } } public List<Integer> topk() { // Write your code here Iterator it = minHeap.iterator(); List<Integer> result = new ArrayList<Integer>(); while (it.hasNext()) { result.add((Integer) it.next()); } Collections.sort(result, Collections.reverseOrder()); return result; } };
注意:使用优先级队列minHeap从小到大存储前k大的数,topk()函数输出结果时使用Collections.sort(result, Collections.reverseOrder());翻转即可。
add(number)函数操作时,如果minHeap的size小于k,要特别注意在添加完元素后return[返回操作权给调用者,不执行后面的代码]。
21.high-five(优秀成绩)
每个学生有两个属性 id
和 scores
。找到每个学生最高的5个分数的平均值。
/** * Definition for a Record * class Record { * public int id, score; * public Record(int id, int score){ * this.id = id; * this.score = score; * } * } */ public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ public Map<Integer, Double> highFive(Record[] results) { // Write your code here Map<Integer, Double> result = new HashMap<Integer, Double>(); Map<Integer, List<Integer>> hash = new HashMap<Integer, List<Integer>>(); for (Record r : results) { if (!hash.containsKey(r.id)) { hash.put(r.id, new ArrayList<Integer>()); } if (hash.get(r.id).size() < 5) { hash.get(r.id).add(r.score); } else { int index = 0; for (int i = 1; i < 5; i++) { if (hash.get(r.id).get(i) < hash.get(r.id).get(index)) { index = i; } } if (hash.get(r.id).get(index) < r.score) { hash.get(r.id).set(index, r.score); } } } for (Map.Entry<Integer, List<Integer>> entry : hash.entrySet()) { int id = entry.getKey(); List<Integer> scores = entry.getValue(); double average = 0; for (int i = 0; i < 5; i++) { average += scores.get(i); } average /= 5.0; result.put(id, average); } return result; } }
注意:使用HashMap。Map<Integer, Double> result存储学生编号和对应的平均分数值;Map<Integer, List<Integer>> hash存储学生编号和前5个最高分数。对每个学生编号,如果hash中的分数少于5个就直接添加,否则就替换掉最小的那个。然后计算Hash中分数的平均值存入result即可。
for (Map.Entry<Integer, List<Integer>> entry : hash.entrySet()) {
int id = entry.getKey();
List<Integer> scores = entry.getValue();
.......
}
22.k-closest-points(k个最近的点)
给定一些points
和一个origin,
从points
中找到k
个离origin
最近的点。按照距离由小到大返回。如果两个点有相同距离,则按照x值来排序;若x值也相同,就再按照y值排序。
/** * Definition for a point. * class Point { * int x; * int y; * Point() { x = 0; y = 0; } * Point(int a, int b) { x = a; y = b; } * } */ public class Solution { /** * @param points a list of points * @param origin a point * @param k an integer * @return the k closest points */ private Point global_point = null; public Point[] kClosest(Point[] points, Point origin, int k) { // Write your code here global_point = origin; PriorityQueue<Point> pq = new PriorityQueue<Point>(k, new Comparator<Point>() { public int compare(Point a, Point b) { int diff = getDistance(b, global_point) - getDistance(a, global_point); if (diff == 0) { diff = b.x - a.x; } if (diff == 0) { diff = b.y - a.y; } return diff; } }); for (int i = 0; i < points.length; i++) { pq.offer(points[i]); if (pq.size() > k) { pq.poll(); } } k = pq.size(); Point[] result = new Point[k]; while (!pq.isEmpty()) { result[--k] = pq.poll(); } return result; } private int getDistance(Point a, Point b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); } }
注意:使用PriorityQueue从大到小排序,当PriorityQueue里存够k个点后,每次都把最大的踢出,这样遍历完成后队列里剩余的即为k个从大到小排序距离origin最近的点,输出时记得翻转顺序即可。
说明:Java中的内部类访问该方法的局部变量时,该变量要被final,不能随意修改。
private Point global_point = null;
public Point[] kClosest(Point[] points, Point origin, int k) {
global_point = origin;
PriorityQueue<Point> pq = new PriorityQueue<Point>(k, new Comparator<Point>() {
public int compare(Point a, Point b) {
int diff = getDistance(b, global_point) - getDistance(a, global_point);
......
}
});
}
23.data-stream-median(数据流中位数)【hard】
数字是不断进入数组的,在每次添加一个新的数进入数组的同时返回当前新数组的中位数。说明 中位数的定义:中位数是排序后数组的中间值,如果有数组中有n个数,则中位数为A[(n-1)/2]。比如:数组A=[1,2,3]的中位数是2,数组A=[1,19]的中位数是1。样例 持续进入数组的数的列表为:[4, 5, 1, 3, 2, 6, 0],则返回 [4, 4, 4, 3, 3, 3, 3]
public class Solution { /** * @param nums: A list of integers. * @return: the median of numbers */ private PriorityQueue<Integer> maxHeap; private PriorityQueue<Integer> minHeap; private int numOfElements = 0; public int[] medianII(int[] nums) { // write your code here Comparator<Integer> revCmp = new Comparator<Integer>() { @Override public int compare(Integer left, Integer right) { return right.compareTo(left); } }; int cnt = nums.length; maxHeap = new PriorityQueue<Integer>(cnt, revCmp); minHeap = new PriorityQueue<Integer>(cnt); int[] ans = new int[cnt]; for (int i = 0; i < cnt; ++i) { addNumber(nums[i]); ans[i] = getMedian(); } return ans; } void addNumber(int value) { maxHeap.add(value); if (numOfElements % 2 == 0) { if (minHeap.isEmpty()) { numOfElements++; return; } else if (maxHeap.peek() > minHeap.peek()) { Integer maxHeapRoot = maxHeap.poll(); Integer minHeapRoot = minHeap.poll(); maxHeap.add(minHeapRoot); minHeap.add(maxHeapRoot); } } else { minHeap.add(maxHeap.poll()); } numOfElements++; } int getMedian() { return maxHeap.peek(); } }
24.kth-smallest-number-in-sorted-matrix(排序矩阵中的从小到大第k个数)
在一个排序矩阵中找从小到大的第 k 个整数。排序矩阵的定义为:每一行递增,每一列也递增。
class Pair{ public int x; public int y; public int val; public Pair(int x, int y, int val) { this.x = x; this.y = y; this.val = val; } } public class Solution { /** * @param matrix: a matrix of integers * @param k: an integer * @return: the kth smallest number in the matrix */ public int kthSmallest(int[][] matrix, int k) { // write your code here int[] dx = new int[]{0, 1}; int[] dy = new int[]{1, 0}; int n = matrix.length; int m = matrix[0].length; boolean[][] hash = new boolean[n][m]; PriorityQueue<Pair> minHeap = new PriorityQueue<Pair>(k, new Comparator<Pair>() { public int compare(Pair a, Pair b) { return a.val - b.val; } }); minHeap.add(new Pair(0, 0, matrix[0][0])); for (int i = 0; i < k - 1; i++) { Pair cur = minHeap.poll(); for (int j = 0; j < 2; j++) { int next_x = cur.x + dx[j]; int next_y = cur.y + dy[j]; Pair next_Pair = new Pair(next_x, next_y, 0); if (next_x < n && next_y < m && !hash[next_x][next_y]){ hash[next_x][next_y] = true; next_Pair.val = matrix[next_x][next_y]; minHeap.add(next_Pair); } } } return minHeap.poll().val; } }
注意:class Pair{int x; int y; int val;...}记录每个整数及其在矩阵中的坐标。每个当前整数有两个移动方向:int[] dx = new int[]{0,1};int[] dy = new int[]{1, 0}。
使用boolean[][]矩阵来记录该整数是否已加入队列,避免重复操作。使用PriorityQueue从小到大排序,在每次for循环中,poll()出队列头,将队列头元素接下来可能到达的两个位置元素加入队列。执行k-1次for循环移除前k-1个元素,最后poll()出的即为从小到大第k个数。
25.kth-largest-element-ii(第k大元素II)
查找数组中从大到小第K个最大的元素。并且N远大于k。你可以交换数组中的元素。
注意:使用PriorityQueue从小到大存储数组中最大的K个元素,最后返回优先级队列的堆头即为第K大元素。
TreeMap(要了解红黑树的实现)
又想知道最小值,又想支持修改和删除
26.top-k-frequent-words(最高频的K个单词)
给一个单词列表,求出这个列表中出现频次最高(从高到低)的K个单词。你需要按照单词的词频排序后输出,越高频的词排在越前面。如果两个单词出现的次数相同,则词典序小的排在前面。
class Pair { String key; int value; Pair(String key, int value) { this.key = key; this.value = value; } } public class Solution { /** * @param words an array of string * @param k an integer * @return an array of string */ private Comparator<Pair> pairComparator = new Comparator<Pair>() { public int compare(Pair left, Pair right) { if (left.value != right.value) { return left.value - right.value; } return right.key.compareTo(left.key); } }; public String[] topKFrequentWords(String[] words, int k) { // Write your code here if (k == 0) { return new String[0]; } //counter存储每个单词及对应的出现次数 Map<String, Integer> counter = new HashMap<String, Integer>(); for (String word : words) { if (!counter.containsKey(word)) { counter.put(word, 1); } else { counter.put(word, counter.get(word) + 1); } } PriorityQueue<Pair> Q = new PriorityQueue<Pair>(k, pairComparator); for (String word : counter.keySet()) { Pair peak = Q.peek();//优先级队列的头 Pair newPair = new Pair(word, counter.get(word)); if (Q.size() < k) { Q.add(newPair); } else if (pairComparator.compare(newPair, peak) > 0) { Q.poll(); Q.add(newPair); } } String[] result = new String[k]; int index = k; while (!Q.isEmpty()) { result[--index] = Q.poll().key; } return result; }
注意:class Pair{String key; int value;...}记录单词及其出现的次数。首先使用HashMap<String, Integer> counter存储单词列表中每个单词及其出现的次数,然后使用PriorityQueue<Pair>从小到大存储当前已遍历单词集(counter.keySet())中出现次数最多的K个单词。由于当出现次数相同时,词典序小的排在前面,所以当pairComparator.compare(newPair, peak) > 0(此时peak的词典序比newPair大)时,弹出队列头peak,放入newPair。最后使用String类型的数组result从后往前依次放入队列中的元素即可。
private Comparator<Pair> pairComparator = new Comparator<Pair>() {
public int compare(Pair left, Pair right) {
if (left.value != right.value) {
return left.value - right.value;//出现次数不等时,从大到小排序
}
return right.key.compareTo(left.key);//出现次数相等时,比较字典序;>0说明right的字典序大
}
};
PriorityQueue<Pair> Q = new PriorityQueue<Pair>(k, pairComparator);
27.building-outline(大楼轮廓)【hard】
给定x轴上的N个建筑物,每个建筑物都是一个矩形,可以由三元组(start,end,height)表示,其中start是x轴上的起始位置,end是x轴上的结束位置,height是建筑物的高度。如果您从远处看到它们,找到它们的轮廓,建筑物可能会重叠。轮廓可以由三元组(start,end,height)表示,其中start是轮廓在x轴上的起始位置,end是轮廓在x轴上的结束位置,height是轮廓的高度。请注意合并同样高度的相邻轮廓,不同的轮廓线在x轴上不能有重叠。
样例 给出三座大楼:[
[1, 3, 3],
[2, 4, 4],
[5, 6, 1]
]
外轮廓线为:[
[1, 2, 3],
[2, 4, 4],
[5, 6, 1]
]
import java.util.*; public class Solution { /** * @param buildings: A list of lists of integers * @return: Find the outline of those buildings */ class HashHeap { ArrayList<Integer> heap; String mode; int size_t; HashMap<Integer, Node> hash; class Node { public Integer id; public Integer num; Node(Node now) { id = now.id; num = now.num; } Node(Integer first, Integer second) { this.id = first; this.num = second; } } public HashHeap(String mod) { // TODO Auto-generated constructor stub heap = new ArrayList<Integer>(); mode = mod; hash = new HashMap<Integer, Node>(); size_t = 0; } public int peek() { return heap.get(0); } public int size() { return size_t; } public Boolean isEmpty() { return heap.size() == 0; } int parent(int id) { if (id == 0) { return -1; } return (id - 1) / 2; } int lson(int id) { return id * 2 + 1; } int rson(int id) { return id * 2 + 2; } boolean comparesmall(int a, int b) { if (a <= b) { if (mode.equals("min")) { return true; } else { return false; } } else { if (mode.equals("min")) { return false; } else { return true; } } } void swap(int idA, int idB) { int valA = heap.get(idA); int valB = heap.get(idB); int numA = hash.get(valA).num; int numB = hash.get(valB).num; hash.put(valB, new Node(idA, numB)); hash.put(valA, new Node(idB, numA)); heap.set(idA, valB); heap.set(idB, valA); } public Integer poll() { size_t--; Integer now = heap.get(0); Node hashnow = hash.get(now); if (hashnow.num == 1) { swap(0, heap.size() - 1); hash.remove(now); heap.remove(heap.size() - 1); if (heap.size() > 0) { siftdown(0); } } else { hash.put(now, new Node(0, hashnow.num - 1)); } return now; } public void add(int now) { size_t++; if (hash.containsKey(now)) { Node hashnow = hash.get(now); hash.put(now, new Node(hashnow.id, hashnow.num + 1)); } else { heap.add(now); hash.put(now, new Node(heap.size() - 1, 1)); } siftup(heap.size() - 1); } public void delete(int now) { size_t--; Node hashnow = hash.get(now); int id = hashnow.id; int num = hashnow.num; if (hashnow.num == 1) { swap(id, heap.size() - 1); hash.remove(now); heap.remove(heap.size() - 1); if (heap.size() > id) { siftup(id); siftdown(id); } } else { hash.put(now, new Node(id, num - 1)); } } void siftup(int id) { while (parent(id) > -1) { int parentId = parent(id); if (comparesmall(heap.get(parentId), heap.get(id)) == true) { break; } else { swap(id, parentId); } id = parentId; } } void siftdown(int id) { while (lson(id) < heap.size()) { int leftId = lson(id); int rightId = rson(id); int son; if (rightId >= heap.size() || (comparesmall(heap.get(leftId), heap.get(rightId)) == true)) { son = leftId; } else { son = rightId; } if (comparesmall(heap.get(id), heap.get(son)) == true) { break; } else { swap(id, son); } id = son; } } } class Edge { int pos; int height; boolean isStart; public Edge(int pos, int height, boolean isStart) { this.pos = pos; this.height = height; this.isStart = isStart; } } class EdgeComparator implements Comparator<Edge> { @Override public int compare(Edge arg1, Edge arg2) { Edge l1 = (Edge) arg1; Edge l2 = (Edge) arg2; if (l1.pos != l2.pos) { return compareInteger(l1.pos, l2.pos); } if (l1.isStart && l2.isStart) { return compareInteger(l2.height, l1.height); } if (!l1.isStart && !l2.isStart) { return compareInteger(l1.height, l2.height); } return l1.isStart ? -1 : 1; } int compareInteger(int a, int b) { return a <= b ? -1 : 1; } } ArrayList<ArrayList<Integer>> output(ArrayList<ArrayList<Integer>> res) { ArrayList<ArrayList<Integer>> ans = new ArrayList<ArrayList<Integer>>(); if (res.size() > 0) { int pre = res.get(0).get(0); int height = res.get(0).get(1); for (int i = 1; i < res.size(); i++) { ArrayList<Integer> now = new ArrayList<Integer>(); int id = res.get(i).get(0); if (height > 0) { now.add(pre); now.add(id); now.add(height); ans.add(now); } pre = id; height = res.get(i).get(1); } } return ans; } public ArrayList<ArrayList<Integer>> buildingOutline(int[][] buildings) { // write your code here ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>(); if (buildings == null || buildings.length == 0 || buildings[0].length == 0) { return res; } ArrayList<Edge> edges = new ArrayList<Edge>(); for (int[] building : buildings) { Edge startEdge = new Edge(building[0], building[2], true); edges.add(startEdge); Edge endEdge = new Edge(building[1], building[2], false); edges.add(endEdge); } Collections.sort(edges, new EdgeComparator()); HashHeap heap = new HashHeap("max"); ArrayList<Integer> now = null; for (Edge edge : edges) { if (edge.isStart) { if (heap.isEmpty() || edge.height > heap.peek()) { now = new ArrayList<Integer>(Arrays.asList(edge.pos, edge.height)); res.add(now); } heap.add(edge.height); } else { heap.delete(edge.height); if (heap.isEmpty() || edge.height > heap.peek()) { if (heap.isEmpty()) { now = new ArrayList<Integer>(Arrays.asList(edge.pos, 0)); } else { now = new ArrayList<Integer>(Arrays.asList(edge.pos, heap.peek())); } res.add(now); } } } return output(res); } }
28.nested-list-weight-sum(嵌套链表权重和)
给定一个嵌套的整数列表,返回列表中所有整数加权深度的总和。每个元素都是整数或列表,其元素也可以是整数或其他列表。给定列表[[1,1],2,[1,1]],返回10.(深度为2的4个1,深度为1的1个2,4*1*2+1*2*1=10)
递归解法:
/** * // This is the interface that allows for creating nested lists. * // You should not implement it, or speculate about its implementation * public interface NestedInteger { * * // @return true if this NestedInteger holds a single integer, * // rather than a nested list. * public boolean isInteger(); * * // @return the single integer that this NestedInteger holds, * // if it holds a single integer * // Return null if this NestedInteger holds a nested list * public Integer getInteger(); * * // @return the nested list that this NestedInteger holds, * // if it holds a nested list * // Return null if this NestedInteger holds a single integer * public List<NestedInteger> getList(); * } */ public class Solution { public int depthSum(List<NestedInteger> nestedList) { // Write your code here return helper(nestedList, 1); } private int helper(List<NestedInteger> nestedList, int depth) { if (nestedList == null || nestedList.size() == 0) { return 0; } int sum = 0; for (NestedInteger ele : nestedList) { if (ele.isInteger()) { sum += ele.getInteger() * depth; } else { sum += helper(ele.getList(), depth + 1); } } return sum; } }
注意:使用helper(List<NestedInteger> nestedList, int depth)函数。对每个深度的元素进行遍历,如果遇到整数就求和,如果遇到链表就深度+1执行helper()。
非递归解法:
/** * // This is the interface that allows for creating nested lists. * // You should not implement it, or speculate about its implementation * public interface NestedInteger { * * // @return true if this NestedInteger holds a single integer, * // rather than a nested list. * public boolean isInteger(); * * // @return the single integer that this NestedInteger holds, * // if it holds a single integer * // Return null if this NestedInteger holds a nested list * public Integer getInteger(); * * // @return the nested list that this NestedInteger holds, * // if it holds a nested list * // Return null if this NestedInteger holds a single integer * public List<NestedInteger> getList(); * } */ public class Solution { public int depthSum(List<NestedInteger> nestedList) { // Write your code here if (nestedList == null || nestedList.size() == 0) { return 0; } Queue<NestedInteger> queue = new LinkedList<NestedInteger>(); for (NestedInteger ele : nestedList) { queue.add(ele); } int sum = 0; int depth = 0; while (!queue.isEmpty()) { depth++; int size = queue.size(); for (int i = 0; i < size; i++) { NestedInteger nested = queue.poll(); if (nested.isInteger()) { sum += nested.getInteger() * depth; } else { for (NestedInteger innerEle : nested.getList()) { queue.add(innerEle); } } } } return sum; } }
注意:将给定嵌套列表中的所有元素(数字或链表)放入初始化queue,然后使用bfs对queue中的元素进行遍历,如果遇到整数就求和,如果遇到链表就将该链表的所有元素放入队列。
29.lfu-cache【hard】
LFU是一个著名的缓存算法。实现LFU中的set
和 get。
LFU(Least Frequently Used)最近最少使用算法。它是基于“如果一个数据在最近一段时间内使用次数很少,那么在将来一段时间内被使用的可能性也很小”的思路。注意LFU和LRU算法的不同之处,LRU的淘汰规则是基于访问时间,而LFU是基于访问次数。
举个简单的例子:假设缓存大小为3,数据访问序列set(2,2),set(1,1),get(2),get(1),get(2),set(3,3),set(4,4),则在set(4,4)时对于LFU算法应该淘汰(3,3),而LRU应该淘汰(1,1)。那么LFU Cache应该支持的操作为:get(key):如果Cache中存在该key,则返回对应的value值,否则,返回-1;set(key,value):如果Cache中存在该key,则重置value值;如果不存在该key,则将该key插入到到Cache中,若Cache已满,则淘汰最少访问的数据。
public class LFUCache { private final Map<Integer, CacheNode> cache; private final LinkedHashSet[] frequencyList; private int lowestFrequency; private int maxFrequency; private final int maxCacheSize; // @param capacity, an integer public LFUCache(int capacity) { // Write your code here this.cache = new HashMap<Integer, CacheNode>(capacity); this.frequencyList = new LinkedHashSet[capacity * 2]; this.lowestFrequency = 0; this.maxFrequency = capacity * 2 - 1; this.maxCacheSize = capacity; initFrequencyList(); } // @param key, an integer // @param value, an integer // @return nothing public void set(int key, int value) { // Write your code here CacheNode currentNode = cache.get(key); if (currentNode == null) { if (cache.size() == maxCacheSize) { doEviction(); } LinkedHashSet<CacheNode> nodes = frequencyList[0]; currentNode = new CacheNode(key, value, 0); nodes.add(currentNode); cache.put(key, currentNode); lowestFrequency = 0; } else { currentNode.v = value; } addFrequency(currentNode); } public int get(int key) { // Write your code here CacheNode currentNode = cache.get(key); if (currentNode != null) { addFrequency(currentNode); return currentNode.v; } else { return -1; } } public void addFrequency(CacheNode currentNode) { int currentFrequency = currentNode.frequency; if (currentFrequency < maxFrequency) { int nextFrequency = currentFrequency + 1; LinkedHashSet<CacheNode> currentNodes = frequencyList[currentFrequency]; LinkedHashSet<CacheNode> newNodes = frequencyList[nextFrequency]; moveToNextFrequency(currentNode, nextFrequency, currentNodes, newNodes); cache.put(currentNode.k, currentNode); if (lowestFrequency == currentFrequency && currentNodes.isEmpty()) { lowestFrequency = nextFrequency; } } else { // Hybrid with LRU: put most recently accessed ahead of others: LinkedHashSet<CacheNode> nodes = frequencyList[currentFrequency]; nodes.remove(currentNode); nodes.add(currentNode); } } public int remove(int key) { CacheNode currentNode = cache.remove(key); if (currentNode != null) { LinkedHashSet<CacheNode> nodes = frequencyList[currentNode.frequency]; nodes.remove(currentNode); if (lowestFrequency == currentNode.frequency) { findNextLowestFrequency(); } return currentNode.v; } else { return -1; } } public int frequencyOf(int key) { CacheNode node = cache.get(key); if (node != null) { return node.frequency + 1; } else { return 0; } } public void clear() { for (int i = 0; i <= maxFrequency; i++) { frequencyList[i].clear(); } cache.clear(); lowestFrequency = 0; } public int size() { return cache.size(); } public boolean isEmpty() { return this.cache.isEmpty(); } public boolean containsKey(int key) { return this.cache.containsKey(key); } private void initFrequencyList() { for (int i = 0; i <= maxFrequency; i++) { frequencyList[i] = new LinkedHashSet<CacheNode>(); } } private void doEviction() { int currentlyDeleted = 0; double target = 1; // just one while (currentlyDeleted < target) { LinkedHashSet<CacheNode> nodes = frequencyList[lowestFrequency]; if (nodes.isEmpty()) { break; } else { Iterator<CacheNode> it = nodes.iterator(); while (it.hasNext() && currentlyDeleted++ < target) { CacheNode node = it.next(); it.remove(); cache.remove(node.k); } if (!it.hasNext()) { findNextLowestFrequency(); } } } } private void moveToNextFrequency(CacheNode currentNode, int nextFrequency, LinkedHashSet<CacheNode> currentNodes, LinkedHashSet<CacheNode> newNodes) { currentNodes.remove(currentNode); newNodes.add(currentNode); currentNode.frequency = nextFrequency; } private void findNextLowestFrequency() { while (lowestFrequency <= maxFrequency && frequencyList[lowestFrequency].isEmpty()) { lowestFrequency++; } if (lowestFrequency > maxFrequency) { lowestFrequency = 0; } } private class CacheNode { public final int k; public int v; public int frequency; public CacheNode(int k, int v, int frequency) { this.k = k; this.v = v; this.frequency = frequency; } } }