📣 Back to School开学季 - VIP通行证5折优惠!蓝莓、Offer多多同步优惠
查看: 3147| 回复: 8
跳转到指定楼层
上一主题 下一主题
收起左侧

[二分/排序/搜索] 请问一道google面试题: merge k sorted list with timestamp

全局:

注册一亩三分地论坛,查看更多干货!

您需要 登录 才可以下载或查看附件。没有帐号?注册账号

x
面试google,碰到一道新题:
Given K sorted list, each list contains many (timestamp, key, value) entries sorted by key. 写一个基于iterator的merger function,把这些entries按照顺利合并。同一个(key, value)的entry, 比较timestamp, timestamp 大的覆盖带哦timestamp 小的。
面试时候给了一个近似以下的解法。但是只能排序合并,不能merge timestamp。
请问各位有好的解法没有?


public class Solution {
    public static Iterable<Integer> mergeKSortedIterators(List<Iterator<Integer>> iterators) {
        List<Integer> result = new ArrayList<>();
        if (iterators == null || iterators.size() == 0) {
            return result;
        }
         
        PriorityQueue<MyIterator> pq = new PriorityQueue<>(iterators.size());
         
        for (Iterator<Integer> iterator : iterators) {
            if (iterator.hasNext()) {
                pq.add(new MyIterator(iterator.next(), iterator));
            }
        }
         
        while (!pq.isEmpty()) {
            MyIterator curr = pq.poll();
            result.add(curr.val);
            if (curr.hasNext()) {
                pq.add(curr);
            }
        }
         
        return result;
    }

private static class MyIterator implements Comparable<MyIterator> {
        private Integer val;
        private Iterator<Integer> iterator;
         
        public MyIterator(Integer val, Iterator<Integer> iterator) {
            this.val = val;
            this.iterator = iterator;
        }
         
        public boolean hasNext() {
            if (iterator.hasNext()) {
                val = iterator.next();
                return true;
            }
            
            return false;
        }
         
        public int compareTo(MyIterator that) {
            return this.val - that.val;
        }
    }

public static void main(String[] args) {
        List<Integer> a = new ArrayList<>();
        a.add(1);
        a.add(3);
        a.add(5);
         
        List<Integer> b = new ArrayList<>();
        b.add(2);
        b.add(4);
         
        List<Iterator<Integer>> iterators = new ArrayList<>();
        iterators.add(a.iterator());
        iterators.add(b.iterator());
         
        Iterable<Integer> result = mergeKSortedIterators(iterators);
         
        for (Integer num : result) {
            System.out.println(num);
        }
    }
}


上一篇:LC 716 max stack
下一篇:大家都是按tag刷的吗?有没有发现tag有时候是不准的
推荐
Mickeypeng 2019-2-23 22:17:40 | 只看该作者
全局:
同志,你的算法是将所有元素一股脑塞进一个优先队列,然后按顺序取出来?
如果是这样的话,假设一共n元素,时间复杂度应当是nlogn,如果你的优先队列是用堆写的话。(事实上这不就是一个堆排序吗)
在此情况下,采用 平衡树作为优先队列, 以 (key, value) 作为 键,timestamp 作为值, 不停插入和更新平衡树, 最后做中序遍历输出

但是,这显然并不是最优的,因为你丢掉了每个初始队列都是有序的这一性质。
本题的名字也提示了我们,往归并排序上去想。
设想有k个指针分别指向k个队头,比较这k个指针指向元素,选择key最小的加进答案,相应的指针++
复杂度很好分析,因为每次必然选入一个元素,时间复杂度是 n*选择的代价

那么选择的代价是多少呢?naive的想法,k元素选最小的是O(k)
更好的算法,将这k个元素组成一个堆,选择堆顶元素是O(1), 插入新元素是O(logk)

评分

参与人数 2大米 +4 收起 理由
kkchenk + 1 赞一个
14417335 + 3 给你点个赞!

查看全部评分

回复

使用道具 举报

🔗
zhxiaog 2019-2-24 10:19:08 | 只看该作者
全局:
Mickeypeng 发表于 2019-2-23 22:17
同志,你的算法是将所有元素一股脑塞进一个优先队列,然后按顺序取出来?
如果是这样的话,假设一共n元素 ...

选择代价是 O(k) 吧,组成堆也至少是 O(k) 的
回复

使用道具 举报

🔗
Mickeypeng 2019-2-24 13:48:03 | 只看该作者
全局:
zhxiaog 发表于 2019-2-24 10:19
选择代价是 O(k) 吧,组成堆也至少是 O(k) 的

当然不是了
回复

使用道具 举报

🔗
zhxiaog 2019-2-24 16:09:46 | 只看该作者
全局:

仔细想了下,确实是 O(k),多谢了

补充内容 (2019-2-24 16:10):
应该是 O(logk),上面写错了,手误
回复

使用道具 举报

🔗
14417335 2019-2-24 22:27:23 | 只看该作者
全局:
Mickeypeng 发表于 2019-2-23 22:17
同志,你的算法是将所有元素一股脑塞进一个优先队列,然后按顺序取出来?
如果是这样的话,假设一共n元素 ...

因为考虑到楼顶“比较timestamp, timestamp 大的覆盖带哦timestamp 小的”,所以从PQ中拿出来的那个sorted list后应该不停的移动指针直到该sorted list指针指向不是该key的,再把它加回PQ。同时,PQ的顶部如果peek出来也是同样的key,也要重复此操作。知道PQ的顶部peek出来不是同样的key。这时返回最大的timestamp的value。

整体复杂度是O(N * Log K)
回复

使用道具 举报

🔗
AldridgeShawn 2019-2-25 03:22:58 | 只看该作者
全局:
Mickeypeng 发表于 2019-2-23 22:17
同志,你的算法是将所有元素一股脑塞进一个优先队列,然后按顺序取出来?
如果是这样的话,假设一共n元素 ...

有道理,所以k 比较小的话就直接几个if else 判断累加指针咯
回复

使用道具 举报

🔗
沉风 2019-2-26 17:35:33 | 只看该作者
全局:
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        auto cmp = [](ListNode*& a, ListNode*& b) {
            return a->val > b->val; //最小堆
        };
        priority_queue<ListNode*, vector<ListNode*>, decltype(cmp) > q(cmp);
        for (auto node : lists) {
            // 入堆排序
            if (node) q.push(node);
        }
        ListNode *dummy = new ListNode(-1), *cur = dummy;
        while (!q.empty()) {
            // 出堆
            auto t = q.top();
            q.pop();
            // 作为下一个节点
            cur->next = t;
            cur = cur->next;
            if (cur->next) {
                // 取下一个入堆
                q.push(cur->next);
            }
        }
        return dummy->next;
   
        
    }
}

主要这种思路如下:
初始化 将每个链表头入优先级队列
后续操作就是从队列中取出最小的元素,再将这个最小元素下一个节点入队列  
这样每次操作时间复杂O(log(k))  
整体复杂度是 nO(log(k))   
如果全部入队列是  nO(log(n))   

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册账号
隐私提醒:
  • ☑ 禁止发布广告,拉群,贴个人联系方式:找人请去🔗同学同事飞友,拉群请去🔗拉群结伴,广告请去🔗跳蚤市场,和 🔗租房广告|找室友
  • ☑ 论坛内容在发帖 30 分钟内可以编辑,过后则不能删帖。为防止被骚扰甚至人肉,不要公开留微信等联系方式,如有需求请以论坛私信方式发送。
  • ☑ 干货版块可免费使用 🔗超级匿名:面经(美国面经、中国面经、数科面经、PM面经),抖包袱(美国、中国)和录取汇报、定位选校版
  • ☑ 查阅全站 🔗各种匿名方法

本版积分规则

>
快速回复 返回顶部 返回列表