「前端刷题」23. 合并K个升序链表

231 阅读1分钟

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

题目

给你一个链表数组,每个链表都已经按升序排列。

请你将所有链表合并到一个升序链表中,返回合并后的链表。

 

示例 1:

输入: lists = [[1,4,5],[1,3,4],[2,6]]

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

解释: 链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6

示例 2:

输入: lists = []

输出:[]

示例 3:

输入: lists = [[]]

输出:[]

 

提示:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i]升序 排列
  • lists[i].length 的总和不超过 10^4

解题思路

思路1:双指针法 + 两两合并

  • 时间复杂度:O(kn)(k为链表个数,n为当前两个合并中链表的长度)
  • 空间复杂度:O(1)
  • 区别
  • 不止两个链表
  • 两两合并
    • 遍历数组,合并两个成新的一个链表再继续合并下一个链表
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    let mergeTwoLists = (l1,l2) => {
        let preHead = new ListNode(-1)
        let preNode = preHead
        while(l1 && l2){
            if(l1.val <= l2.val){
                preNode.next = l1
                l1 = l1.next
            }else{
                preNode.next = l2
                l2 = l2.next
            }
            preNode = preNode.next
        }
        preNode.next = l1 ? l1 : l2
        return preHead.next
    }
    let n = lists.length
    if(n == 0) return null
    let res = lists[0]
    for(let i = 1;i < n;i++){
        if(lists[i]){
            res = mergeTwoLists(res,lists[i])
        }
    }
    return res
};

思路2:递归 + 分治

  • 时间复杂度:O(nlogK)
  • k为链表总数
  • n为合并两个链表所用时间
  • 空间复杂度:O(n)
  • 分治
  • 借用官方一张图说明分治合并过程
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    let n = lists.length;
    if(n == 0) return null;
    let mergeTwoLists = (l1,l2) => {
        if(l1 == null) return l2;
        if(l2 == null) return l1;
        if(l1.val <= l2.val){
            l1.next = mergeTwoLists(l1.next,l2);
            return l1;
        }else{
            l2.next = mergeTwoLists(l1,l2.next);
            return l2;
        }
    }
    let merge = (left,right) => {
        if(left == right) return lists[left];
        let mid = (left + right) >> 1;
        let l1 = merge(left,mid);
        let l2 = merge(mid+1,right);
        return mergeTwoLists(l1,l2);
    }
    return merge(0,n-1);
};

思路3:优先级队列

  • 参考大佬的
  • 时间复杂度:O(Nlogk)
  • k为链表数目
  • 弹出操作时,比较操作为O(logK)
  • 找最小值操作为O(1)
  • N为链表元素总数和
  • 空间复杂度:O(n+k)
  • n为创建新链表开销
  • k为额外队列开销
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    let queue = new PriorityQueue();
    lists.forEach(list => {
        if(list) queue.enqueue(list, list.val)
    });

    let res = new ListNode(-1);
    let cur = res;
    while(!queue.isEmpty()) {
        cur.next = queue.dequeue();
        cur = cur.next;
        if(cur.next) queue.enqueue(cur.next, cur.next.val);
    }
    return res.next;
}

class Node {
	constructor(val, priority) {
		this.val = val;
		this.priority = priority;
	}
}

class PriorityQueue {
	constructor() {
		this.values = [];
	}

	enqueue(val, priority) {
		let node = new Node(val, priority);
		this.values.push(node);
		this.bubbleUp();
	}

	dequeue() {
		let max = this.values[0];
		let end = this.values.pop();
		if(this.values.length) {
			this.values[0] = end;
			this.bubbleDown();
		}
		return max.val;
	}
    
    isEmpty() {
        return !this.values.length;
    }
    
    bubbleUp(index = this.values.length - 1) {
		if(index <= 0) return;
		let parentIndex = Math.floor((index - 1) / 2);
		if(this.values[index].priority <= this.values[parentIndex].priority) {
			[this.values[index], this.values[parentIndex]] = [this.values[parentIndex], this.values[index]];
			this.bubbleUp(parentIndex);
		}
	}
	
	bubbleDown(index = 0, swapIndex = null) {
		let leftIndex = index * 2 + 1,
			rightIndex = index * 2 + 2,
			length = this.values.length;

		if(leftIndex < length) {
			if(this.values[leftIndex].priority <= this.values[index].priority) {
				swapIndex = leftIndex;
			}
		}

		if(rightIndex < length) {
			if((swapIndex === null && this.values[rightIndex].priority <= this.values[index].priority) || (swapIndex !== null && this.values[rightIndex].priority <= this.values[leftIndex].priority)) {
				swapIndex = rightIndex;
			}
		}

		if(swapIndex !== null) {
			[this.values[index], this.values[swapIndex]] = [this.values[swapIndex], this.values[index]];
			this.bubbleDown(swapIndex, null);
		}
	}
};

思路4:数组

  • 时间复杂度:O(NlogN)
  • N:节点总数
  • O(N):遍历开销
  • O(NlogN):排序(假设稳定)
  • O(N):创建新的有序链表
  • 空间复杂度:O(N)
  • O(N):排序
  • O(N):创建新的有序链表
  • 思路
  • 遍历所有链表元素,将元素值放到一个数组中
  • 排序数组,相当于按优先级排序优先级队列
  • 遍历排好序的数组,重新构造一个新的有序链表即为所求
/**
 * Definition for singly-linked list.
 * function ListNode(val) {
 *     this.val = val;
 *     this.next = null;
 * }
 */
/**
 * @param {ListNode[]} lists
 * @return {ListNode}
 */
var mergeKLists = function(lists) {
    if(!lists || lists.length == 0) return null;
    let arr = [];
    let res = new ListNode(0);
    lists.forEach(list => {
        let cur = list;
        while(cur){
            arr.push(cur.val);
            cur = cur.next;
        }
    })
    let cur = res;
    arr.sort((a,b) => a-b).forEach(val => {
        let node = new ListNode(val);
        cur.next = node;
        cur = cur.next;
    })
    return res.next;
};

最后

曾梦想仗剑走天涯

看一看世界的繁华

年少的心总有些轻狂

终究不过是个普通人

无怨无悔我走我路

「前端刷题」No.23