快手算法岗日常实习面试经验
时间:2019.4.3 10:00 地点:北京市海淀区上地西路6号(快手总部)-E座
这次快手的面试一共两面,每一场面试都是先针对简历问问题然后手写一道算法题。由于我简历里科研的内容比较多,所以两位面试官都问了我关于我的一作在投论文的问题。由于是现场面试,所以代码是用笔写在草稿纸上然后面试官肉眼判题的。下面的面试流程就不叙述讨论论文的部分,只叙述手写算法题的部分。
一面
【二叉树形态变换】给定一个用中序遍历定义的二叉搜索树,用in-place的算法把二叉树变成有序链表,其中链表用左子树为null的二叉树等价表示。下面是例子,其中N表示null:
这道题类似LeetCode上的Flatten Binary Tree to Linked List,只不过LeetCode上原二叉搜索树是用先序遍历定义的。大致思路是一样的,都是在先序遍历或中序遍历的过程中加入节点指针的重定向操作,注意一下左子树或右子树为空的情形即可。更加详细的解题报告见我的博客LeetCode 114. Flatten Binary Tree to Linked List(二叉树)。LeetCode上那题的代码如下,可供参考:
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private TreeNode preorder(TreeNode root)
{
if (root == null)
{
return null;
}
TreeNode tmp = root.right, left_tail = preorder(root.left);
if (root.left != null)
{
root.right = root.left;
}
root.left = null;
if (left_tail != null)
{
left_tail.left = null;
left_tail.right = tmp;
}
TreeNode right_tail = preorder(tmp);
return right_tail == null ? (left_tail == null ? root: left_tail): right_tail;
}
public void flatten(TreeNode root) {
preorder(root);
}
}
二面
合并k个有序链表为一个有序链表。这是LeetCode上的原题:Merge k Sorted Lists. 我的做法是用一个优先队列维护k个链表的表头,每次从优先队列中取出队首元素作为合成链表的一个节点,并将所在链表的头指针后移并加入优先队列,如此循环直到优先队列为空。记合成链表的长度为n, 则建优先队列的复杂度为O(k), 从优先队列中取队首并将新元素插入优先队列的复杂度为O(logk), 这样的出队入队操作要重复n次,所以总复杂度为O(k+nlogk) = O(nlogk). 更加详细的解题报告见我的博客LeetCode 23. Merge k Sorted Lists(最小堆)。由于现场面试官允许使用Java自带的优先队列类,故我在面试现场没有手写一个最小堆来实现优先队列:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
class Node {
public int val;
public int index;
public Node(int val, int index)
{
this.val = val;
this.index = index;
}
public Node(Node node)
{
this.val = node.val;
this.index = node.index;
}
}
public ListNode mergeKLists(ListNode[] lists) {
int k = lists.length, i = 0, value = 0;
if (k == 0)
{
return null;
}
PriorityQueue<Node> queue = new PriorityQueue<Node>(1, new NodeComparator implements Comparator<Node>() {
@Override
public int compare(Node n1, Node n2)
{
return n1.val - n2.val;
}
} );
ListNode head = null, ret = null;
for (i=0; i<k; i++)
{
if (lists[i] != null)
{
queue.add(new Node(lists[i].val, i));
lists[i] = lists[i].next;
}
}
if (queue.size() == 0)
{
return null;
}
while (!queue.isEmpty())
{
Node cur = queue.poll();
if (head == null)
{
head = new ListNode(cur.val);
ret = head;
}
else
{
head.next = new ListNode(cur.val);
head = head.next;
}
if (lists[cur.index] != null)
{
queue.add(new Node(lists[cur.index].val, cur.index));
lists[cur.index] = lists[cur.index].next;
}
}
return ret;
}
}