当Rust遇上LeetCode #23. 合并k个排序链表 [困难]

2020/2/17

题目描述

  • 合并 k 个排序链表,返回合并后的排序链表。

示例

输入:
[
  1->4->5,
  1->3->4,
  2->6
]
输出: 1->1->2->3->4->4->5->6

相关标签


列表
分治算法

解题思路

  • 逐一比较法
    算法
    比较 k 个节点(每个链表的首节点),获得最小值的节点。
    将选中的节点接在最终有序链表的后面。
    复杂度分析
    时间复杂度
    O(kN) ,其中 k 是链表的数目。几乎最终有序链表中每个节点的时间开销都为 O(k) ( k-1 次比较)。总共有 N 个节点在最后的链表中。
    空间复杂度
    O(n) 。创建一个新的链表空间开销为 O(n) 。O(1) 。重复利用原来的链表节点,每次选择节点时将它直接接在最后返回的链表后面,而不是创建一个新的节点。

  • 优先队列法
    算法
    几乎与上述方法一样,除了将 比较环节优先队列 进行了优化。
    复杂度分析
    时间复杂度
    O(Nlogk) ,其中 k 是链表的数目。弹出操作时,比较操作的代价会被优化到 O(logk) 。同时,找到最小值节点的时间开销仅仅为 O(1)。最后的链表中总共有 N 个节点。
    空间复杂度:同上。

  • 分治法
    算法
    分治法将k个链表进行逐对的合并,每一轮合并相比上一轮合并的次数减半,总计共进行log2(k)轮。重复这一过程,最终就可以得到需要的唯一有序链表。

    合并过程

    复杂度分析
    时间复杂度: O(Nlogk),n为两个链表中的总节点数
    空间复杂度:O(1)

源代码

1. 逐一比较法

// Definition for singly-linked list.
// #[derive(PartialEq, Eq, Clone, Debug)]
// pub struct ListNode {
//   pub val: i32,
//   pub next: Option<Box<ListNode>>
// }
// 
// impl ListNode {
//   #[inline]
//   fn new(val: i32) -> Self {
//     ListNode {
//       next: None,
//       val
//     }
//   }
// }
impl Solution {
    pub fn merge_k_lists(lists: Vec<Option<Box<ListNode>>>) -> Option<Box<ListNode>> {
        let mut val = 9999;
        let len: usize = lists.len();
        let mut ans: Option<Box<ListNode>> = None;
        let mut ans_p = &mut ans;
        let mut ps: Vec<&Option<Box<ListNode>>> = vec![];
        for list_node in lists.iter() {
            ps.push(&list_node);
        }
        let mut count = 0;
        let mut min_val = 9999;
        let mut min_index = 0;
        loop {
            for i in 0..len{
                if let Some(node) = ps[i]{
                    // println!("node_val: {}, min_val: {}", node.val, min_val);
                    if(min_val > node.val){
                        min_val = node.val;
                        min_index = i;
                    }                    
                    count += 1;
                }
                // println!("{}", count);
            }
            if(count == 0) {
                break;
            }else{
                // println!("{}", min_val);
                *ans_p = Some(Box::new(ListNode::new(min_val)));
                if let Some(node) = ans_p{
                    ans_p = &mut node.next;
                }
                if let Some(node) = ps[min_index]{
                    ps[min_index] = &node.next;
                }                
                count = 0;
                min_val = 9999;
            }
        };
        ans
    }
}
  • 执行用时 : 288 ms, 在所有 Rust 提交中击败了11.43%的用户
  • 内存消耗 : 3.1 MB, 在所有 Rust 提交中击败了53.57%的用户

2. 优先队列优化

Rust标准库中并没有包含优先队列,需要自己实现比较麻烦

use std::collections::{BTreeSet, BTreeMap, HashSet, HashMap, BinaryHeap};
use std::{i32, i64, u32, u64};
use std::cmp::{Reverse, Ordering};

struct KVPair {
    value: i32,
    from_list: usize
}

impl PartialEq for KVPair {
    fn eq(&self, other: &Self) -> bool {
        self.value == other.value
    }

    fn ne(&self, other: &Self) -> bool {
        self.value != other.value
    }
}

impl PartialOrd for KVPair {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.value.cmp(&other.value))
    }

    fn lt(&self, other: &Self) -> bool {
        self.value < other.value
    }

    fn le(&self, other: &Self) -> bool {
        self.value <= other.value
    }

    fn gt(&self, other: &Self) -> bool {
        self.value > other.value
    }

    fn ge(&self, other: &Self) -> bool {
        self.value >= other.value
    }
}

impl Eq for KVPair {}

impl Ord for KVPair {
    fn cmp(&self, other: &Self) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl KVPair {
    pub fn new(value: i32, from_list: usize) -> Self {
        KVPair { value, from_list }
    }
}

impl Solution {
    pub fn merge_k_lists(lists: Vec<Option<Box<ListNode>>>) -> Option<Box<ListNode>> {
        let mut heap = BinaryHeap::new();
        let mut heads = Vec::new();
        for list in lists.iter() {
            heads.push(list);
        }

        for (i, head) in heads.iter_mut().enumerate() {
            if let Some(node) = head {
                heap.push(Reverse(KVPair::new(node.val, i)));
                *head = &node.next;
            }
        }

        let mut merged_list: Option<Box<ListNode>> = None;
        let mut list_tail = &mut merged_list;

        while let Some(kv_pair) = heap.pop() {
            let new_node = Box::new(ListNode::new(kv_pair.0.value));
            if let Some(node) = heads[kv_pair.0.from_list] {
                heap.push(Reverse(KVPair::new(node.val, kv_pair.0.from_list)));
                heads[kv_pair.0.from_list] = &node.next;
            }
            if let Some(tail) = list_tail {
                tail.next = Some(new_node);
                list_tail = &mut tail.next;
            } else {
                *list_tail = Some(new_node);
            }
        }

        merged_list
    }
}
  • 执行用时 : 4 ms, 在所有 Rust 提交中击败了100.00%的用户
  • 内存消耗 : 3.3 MB, 在所有 Rust 提交中击败了17.86%的用户

2. 分治法

impl Solution {
    //分治法
    pub fn merge_k_lists(lists: Vec<Option<Box<ListNode>>>) -> Option<Box<ListNode>> {
        let (len, mut interval) = (lists.len(), 1);
        let mut heads = lists;

        if len == 0 {
            return None;
        }

        while interval < len {
            let mut i = 0;
            while i < len-interval {
                heads[i] = Self::merge_two_lists(&mut heads[i] as *mut Option<Box<ListNode>>,
                                                 &mut heads[i+interval] as *mut Option<Box<ListNode>>);
                i += interval*2;
            }
            interval *= 2;
        }

        heads[0].clone()
    }
    
   pub fn merge_two_lists(l1: *mut Option<Box<ListNode>>, l2: *mut Option<Box<ListNode>>) -> Option<Box<ListNode>> {        
        let mut head = Some(Box::new(ListNode::new(0)));
        let (mut p1, mut p2, mut pr) = (l1, l2, &mut head as *mut Option<Box<ListNode>>);
        let (mut l1, mut l2) = (None, None);

        unsafe {
            let node_val = |x: *mut Option<Box<ListNode>>| {
                if (*x) == None {
                    None
                }else {
                    Some((*x).as_ref().unwrap().val)
                }
            };

            let node_next = |x: *mut Option<Box<ListNode>>| {
                if (*x) == None {
                    x
                }else {
                    &mut (*x).as_mut().unwrap().next as *mut Option<Box<ListNode>>
                }
            };

            loop {
                match (node_val(p1), node_val(p2)) {
                    (Some(v1), Some(v2)) => {
                       if v1 < v2 {
                            if (*node_next(pr)) != None && node_next(pr) == p2 {
                                l2 = (*node_next(pr)).take();
                                p2 = &mut l2 as *mut Option<Box<ListNode>>;                                
                            }         
                            (*node_next(pr)) = (*p1).take();
                            pr = node_next(pr);
                            p1 = node_next(pr);
                        }else {
                            if (*node_next(pr)) != None && node_next(pr) == p1 {
                                
                                 = (*node_next(pr)).take();
                                p1 = &mut l1 as *mut Option<Box<ListNode>>;                                
                            }
                            (*node_next(pr)) = (*p2).take();
                            pr = node_next(pr);
                            p2 = node_next(pr);
                        };
                    },
                    (Some(_), None) => {
                        (*pr).as_mut().unwrap().next = (*p1).take();
                        break;
                    },
                    (None, Some(_)) => {
                        (*pr).as_mut().unwrap().next = (*p2).take();
                        break;
                    },
                    (None, None) => {
                        return None;
                    },
                }
            }
        }
        head.unwrap().next
    }
}
  • 执行用时 : 8 ms, 在所有 Rust 提交中击败了84.62%的用户
  • 内存消耗 :3 MB, 在所有 Rust 提交中击败了72.00%的用户

总结

分治法中,合并两个链表的部分比较头疼,虽然在“21. 合并两个有序链表”中尝试的“迭代法”和“取链表头组合法”都是0ms通过,但放在这道题中,前者要比后者慢100倍之多。猜测是迭代法的实现存在大量的clone,拖慢了时间。若要在摆脱clone,转为调用指针的方法,那就要通过rc智能指针或者unsafe的裸指针,前者与所给的数据结构不匹配,后者打算之后进一步尝试。递归法和取链表头组合差不多时间。目前最快的还是自己构造优先队列的方法,能达到4ms。
另外,本题中使用了迭代而非递归的方法完成归并,可以留心一下写法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 220,699评论 6 513
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,124评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,127评论 0 358
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,342评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,356评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,057评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,654评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,572评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,095评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,205评论 3 339
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,343评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,015评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,704评论 3 332
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,196评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,320评论 1 271
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,690评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,348评论 2 358

推荐阅读更多精彩内容