这次的interview questions有一道我觉得很有意思的题,之前没有遇到过。
Inorder traversal with constant extra space. Design an algorithm to perform an inorder traversal of a binary search tree using only a constant amount of extra space.
这道题是Leetcode #94题 Binary Tree Inorder Traversal, 在dicuss中大家的方法大多都是recurssion和iterative的。iterative的话用一个栈解决就可以了。但是没有想到可以用O(1)的空间复杂度就可以解决这个问题。查阅了一些资料之后发现这其实是Morris Traversal的应用。
Morris Traversal的大致思想就是找到当前节点A的左节点的最右子节点B, 将A作为B的右节点,这样避免了用栈来记录整个便利过程。
算法步骤
- 如果当前节点的左孩子为空,则输出当前节点并将其右孩子作为当前节点。
- 如果当前节点的左孩子不为空,在当前节点的左子树中找到当前节点在中序遍历下的前驱节点。
a) 如果前驱节点的右孩子为空,将它的右孩子设置为当前节点。当前节点更新为当前节点的左孩子。
b) 如果前驱节点的右孩子为当前节点,将它的右孩子重新设为空(恢复树的形状)。输出当前节点。当前节点更新为当前节点的右孩子。 - 重复以上1、2直到当前节点为空。
代码实现
1 | // morris traversal |
值得注意的是,尽管这个算法的空间复杂度为O(1),但是相比于递归和iterative的方法,算法在找最右节点的时候多花费了一些时间。
Dynamic median. Design a data type that supports insert in logarithmic time, find-the-median in constant time, and remove-the-median in logarithmic time. If the number of keys in the data type is even, find/remove the lower median.
这道题在leetcode中也有,可以用两个优先队列解决,一个队列存储比中位数小的数,一个存放更大的数。这两个优先数列一个使用大顶堆,一个使用小顶堆,这样的话获取中位数的时间复杂度只要O(1)。具体代码如下:
1 | class MedianFinder { |
Randomized priority queue. Describe how to add the methods sample() and delRandom() to our binary heap implementation. The two methods return a key that is chosen uniformly at random among the remaining keys, with the latter method also removing that key. The sample() method should take constant time; the delRandom() method should take logarithmic time. Do not worry about resizing the underlying array.
sample()只要随机出一个随机数i即可;至于delRandom(),将随即出来的A[i]与A[N]交换,删除A[N],再调用swim或者sink函数。
Taxicab numbers. A taxicab number is an integer that can be expressed as the sum of two cubes of positive integers in two different ways: $a^{3} + b^{3} = c^{3} + d^{3}$. For example, 1729 is the smallest taxicab number: $9^{3}+ 10^{3} = 1^{3} + 12^{3}$. Design an algorithm to find all taxicab numbers with a, b, c, and d less than n.
- Version 1: Use time proportional to $n^{2}logn$ and space proportional to $n^{2}$.
- Version 2: Use time proportional to $n^{2}logn$ and space proportional to $n$.
Version 1很简单,就是一个4sum的变形。
对于Version2我找了很多资料最后找到了一个最佳解。首先我们将$a^{3} + b^{3}$制成一个矩阵,如图所示。
从图中可以看出每一行和每一列都在逐渐递增,而且我们只需要关注上三角或者下三角即可。利用优先队列来动态的寻找解。在整个过程中始终保持优先队列的size<=N.整个算法如下:
1.从上三角从上往下,从左往右的顺序加入优先队列,直至size为N。
2.弹出优先队列中的最小元素a,如果优先队列peek()仍等于a,则不断弹出并加入解集中。
3.假设上三角中遍历到的元素为b,则比较a与b,如果a=b则加入解集中,否则将b加入优先队列中。
参考网站:keweishang/algorithm_princeton
& stackoverflow1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64class Taxicab implements Comparable<Taxicab>{
int n1;
int n2;
int cube;
Taxicab(int n1, int n2) {
this.n1 = n1;
this.n2 = n2;
this.cube = n1 * n1 * n1 + n2 * n2 * n2;
}
public int compareTo(Taxicab that) {
if (that.cube > this.cube) return -1;
if (that.cube < this.cube) return 1;
return 0;
}
public boolean equals(Object o) {
if (o instanceof Taxicab) {
if (((Taxicab)o).compareTo(this) == 0)
return true;
}
return false;
}
public String toString() {
return "number: " + cube + " (" + n1 + ", " + n2 + ")";
}
}
public void findTaxinumber(int N) {
MinPQ<Taxicab> candidates = new MinPQ<Taxicab>();
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
Taxicab t = new Taxicab(i, j);
if (candidates.size() < N) {
candidates.insert(t);
}
else {
Queue<Taxicab> temp = new Queue<Taxicab>();
Taxicab min = candidates.delMin();
while (candidates.min().equals(min)) {
temp.enqueue(candidates.delMin());
}
if (!t.equals(min)) {
candidates.insert(t);
}
else {
temp.enqueue(t);
}
if (!temp.isEmpty()) {
for (Taxicab taxi: temp) {
System.out.println(taxi);
}
System.out.println(min);
}
}
}
}
}