乘风破浪:LeetCode真题_023_Merge k Sorted Lists

一、前言

上次我们学过了合并两个链表,这次我们要合并N个链表要怎么做呢,最先想到的就是转换成2个链表合并的问题,然后解决,再优化一点的,就是两个两个合并,当然我们也可以一次性比较所有的元素,然后一点点的进行合并等等。

二、Merge k Sorted Lists

2.1 问题

2.2 分析与解决

首先我们看看官方的解法:

第一种方法:暴力算法,将所有的元素放到一个数组里面,排序之后再用指针链接。

# Definition for singly-linked list.
# class ListNode(object):
# def __init__(self, x):
# self.val = x
# self.next = None class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
self.nodes = []
head = point = ListNode(0)
for l in lists:
while l:
self.nodes.append(l.val)
l = l.next
for x in sorted(self.nodes):
point.next = ListNode(x)
point = point.next
return head.next

    第二种方法:用N个指针,每次比较最小的,然后连接起来。

  第三种方法:通过优先级队列减少了一些比较的时间。

from Queue import PriorityQueue

class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
head = point = ListNode(0)
q = PriorityQueue()
for l in lists:
if l:
q.put((l.val, l))
while not q.empty():
val, node = q.get()
point.next = ListNode(val)
point = point.next
node = node.next
if node:
q.put((node.val, node))
return head.next

  第四种,我们提到过的转换成两个链表合并问题。

    第五种,分治法合并,减少合并次数。

class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
amount = len(lists)
interval = 1
while interval < amount:
for i in range(0, amount - interval, interval * 2):
lists[i] = self.merge2Lists(lists[i], lists[i + interval])
interval *= 2
return lists[0] if amount > 0 else lists def merge2Lists(self, l1, l2):
head = point = ListNode(0)
while l1 and l2:
if l1.val <= l2.val:
point.next = l1
l1 = l1.next
else:
point.next = l2
l2 = l1
l1 = point.next.next
point = point.next
if not l1:
point.next=l2
else:
point.next=l1
return head.next

  我们的解法:其实和优先级队列很相似,都是使用了小根堆这样的结构来处理。

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List; public class Solution {
/**
* <pre>
* Merge k sorted linked lists and return it as one sorted list.
* Analyze and describe its complexity.
*
* 题目大意:
* 合并k个排好的的单链表
*
* 解题思路:
* 使用一个小堆来进行操作,先将k个单链表的第一个结点入堆,再取堆中的最小素,此为最小的元素,
* 将这个元素的下一个结点堆,再取堆中最小的,依次操作直到堆为空
* </pre>
*
* @param lists
* @return
*/
public ListNode mergeKLists(ListNode[] lists) { // 为空或者没有元素
if (lists == null || lists.length < 1) {
return null;
} // 只有一个元素
if (lists.length == 1) {
return lists[0];
} // 创建一个小顶堆,并且使用一个匿名内部类作为比较器
MinHeap<ListNode> minHeap = new MinHeap<ListNode>(new Comparator<ListNode>() {
@Override
public int compare(ListNode o1, ListNode o2) {
if (o1 == null) {
return -1;
} if (o2 == null) {
return 1;
} return o1.val - o2.val;
}
}); // 将数组中链表的第一个结点入堆
for (ListNode node : lists) {
if (node != null) {
minHeap.add(node);
}
} // 头结点,作辅助使用
ListNode head = new ListNode(0);
// 当前处理的结点
ListNode curr = head; while (!minHeap.isEmpty()) {
ListNode node = minHeap.deleteTop(); // 结点的下一个结点不为空就将下一个结点入堆
if (node.next != null) {
minHeap.add(node.next);
} curr.next = node;
curr = node;
} return head.next;
} /**
* 小顶堆
*
* @param <T>
*/
private static class MinHeap<T> {
// 堆中元素存放的集合
private List<T> items; private Comparator<T> comp; /**
* 构造一个椎,始大小是32
*/
public MinHeap(Comparator<T> comp) {
this(32, comp);
} /**
* 造诣一个指定初始大小的堆
*
* @param size 初始大小
*/
public MinHeap(int size, Comparator<T> comp) {
items = new ArrayList<>(size);
this.comp = comp;
} /**
* 向上调整堆
*
* @param index 被上移元素的起始位置
*/
public void siftUp(int index) {
T intent = items.get(index); // 获取开始调整的元素对象 while (index > 0) { // 如果不是根元素
int parentIndex = (index - 1) / 2; // 找父元素对象的位置
T parent = items.get(parentIndex); // 获取父元素对象
if (comp.compare(intent, parent) < 0) { //上移的条件,子节点比父节点小
items.set(index, parent); // 将父节点向下放
index = parentIndex; // 记录父节点下放的位置
} else { // 子节点不比父节点小,说明父子路径已经按从小到大排好顺序了,不需要调整了
break;
}
} // index此时记录是的最后一个被下放的父节点的位置(也可能是自身),
// 所以将最开始的调整的元素值放入index位置即可
items.set(index, intent);
} /**
* 向下调整堆
*
* @param index 被下移的元素的起始位置
*/
public void siftDown(int index) {
T intent = items.get(index); // 获取开始调整的元素对象
int leftIndex = 2 * index + 1; // // 获取开始调整的元素对象的左子结点的元素位置 while (leftIndex < items.size()) { // 如果有左子结点
T minChild = items.get(leftIndex); // 取左子结点的元素对象,并且假定其为两个子结点中最小的
int minIndex = leftIndex; // 两个子节点中最小节点元素的位置,假定开始时为左子结点的位置 int rightIndex = leftIndex + 1; // 获取右子结点的位置
if (rightIndex < items.size()) { // 如果有右子结点
T rightChild = items.get(rightIndex); // 获取右子结点的元素对象
if (comp.compare(rightChild, minChild) < 0) { // 找出两个子节点中的最小子结点
minChild = rightChild;
minIndex = rightIndex;
}
} // 如果最小子节点比父节点小,则需要向下调整
if (comp.compare(minChild, intent) < 0) {
items.set(index, minChild); // 将子节点向上移
index = minIndex; // 记录上移节点的位置
leftIndex = index * 2 + 1; // 找到上移节点的左子节点的位置
} else { // 最小子节点不比父节点小,说明父子路径已经按从小到大排好顺序了,不需要调整了
break;
}
} // index此时记录是的最后一个被上移的子节点的位置(也可能是自身),
// 所以将最开始的调整的元素值放入index位置即可
items.set(index, intent);
} /**
* 向堆中添加一个元素
*
* @param item 等待添加的元素
*/
public void add(T item) {
items.add(item); // 将元素添加到最后
siftUp(items.size() - 1); // 循环上移,以完成重构
} /**
* 删除堆顶元素
*
* @return 堆顶部的元素
*/
public T deleteTop() {
if (items.isEmpty()) { // 如果堆已经为空,就报出异常
throw new RuntimeException("The heap is empty.");
} T maxItem = items.get(0); // 获取堆顶元素
T lastItem = items.remove(items.size() - 1); // 删除最后一个元素
if (items.isEmpty()) { // 删除元素后,如果堆为空的情况,说明删除的元素也是堆顶元素
return lastItem;
} items.set(0, lastItem); // 将删除的元素放入堆顶
siftDown(0); // 自上向下调整堆
return maxItem; // 返回堆顶元素
} /**
* 判断堆是否为空
*
* @return true是空,false否
*/
public boolean isEmpty() {
return items.isEmpty();
}
} }

三、总结

通过上面的分析我们可以发现只有不断的优化我们的算法,才能使得效率不断地提升,同时也锻炼了我们的思考能力。

乘风破浪:LeetCode真题_023_Merge k Sorted Lists的更多相关文章

  1. 乘风破浪:LeetCode真题_021_Merge Two Sorted Lists

    乘风破浪:LeetCode真题_021_Merge Two Sorted Lists 一.前言 关于链表的合并操作我们是非常熟悉的了,下面我们再温故一下将两个有序链表合并成一个的过程,这是基本功. 二 ...

  2. 【LeetCode练习题】Merge k Sorted Lists

    Merge k Sorted Lists Merge k sorted linked lists and return it as one sorted list. Analyze and descr ...

  3. [Leetcode][Python]23: Merge k Sorted Lists

    # -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 23: Merge k Sorted Listshttps://oj.leet ...

  4. leetcode第22题--Merge k Sorted Lists

    problem:Merge k sorted linked lists and return it as one sorted list. Analyze and describe its compl ...

  5. 【LeetCode】23. Merge k Sorted Lists 合并K个升序链表

    作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:合并,链表,单链表,题解,leetcode, 力扣,Py ...

  6. 【一天一道LeetCode】#23. Merge k Sorted Lists

    一天一道LeetCode系列 (一)题目 Merge k sorted linked lists and return it as one sorted list. Analyze and descr ...

  7. Leetcode Week3 Merge Two(k) Sorted Lists

    Question Q1.Merge two sorted linked lists and return it as a new list. The new list should be made b ...

  8. LeetCode OJ:Merge k Sorted Lists(归并k个链表)

    Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 类 ...

  9. LeetCode LinkList 23. Merge k Sorted Lists

    这两天一直也没有顾上记录一下自己做过的题目,回头看看,感觉忘的好快,今天做了一个hard,刚开始觉得挺难得,想了两种方法,一种是每次都从k个list中选取最小的一个,为空的直接跳过,再就是每次合并其中 ...

随机推荐

  1. spring-boot-mail

    1. 功能 发送普通邮件 发送htm邮件 发送带附件的邮件 发送带静态资源的邮件 2. 实现 类结构图 3. 实现 接口 package com.jihite.service; public inte ...

  2. 基于Hadoop2.6.5(HA)的Hive1.2.1的MySQL方式配置

    1.Hive配置MySQL Hive只是一个工具,无需配置多台机器,我在CentOS7One机器上配置Hive /usr/local/hive/apache-hive-1.2.1-bin/conf c ...

  3. FFmpeg在JAVA中的使用-音频提取&字幕压缩

    由于项目需求中涉及到视频中音频提取,以及字幕压缩的功能,一直在研究ffmpeg,仅仅两个功能,却深受ffmpeg的折磨. 今天谈谈ffmpeg在java中的简单使用,首先下载FFmpeg包,官方地址: ...

  4. Docker 使用官方镜像

    Docker 使用官方镜像 如何使用官方镜像 Docker 中国官方镜像加速可通过 registry.docker-cn.com 访问.目前该镜像库只包含流行的公有镜像,而私有镜像仍需要从美国镜像库中 ...

  5. POJ 1679 The Unique MST(判断最小生成树是否唯一)

    题目链接: http://poj.org/problem?id=1679 Description Given a connected undirected graph, tell if its min ...

  6. VC++6.0调试:Watch窗口的使用

    #include <stdio.h> #include <windows.h> class AutoExpand { public: AutoExpand(int val, c ...

  7. Mybatis插件开发

    前面几篇文章介绍了Mybtis中四个重要的对象,其中提到它们都是在Configuration中被创建的,我们一起看一下创建四大对象的方法,代码如下所示: public ParameterHandler ...

  8. Vue: ES6常用语法

    ES6 模板字符串:  ~ ${变量名}~ <div id="app"></div> <script> /* 找到对应id */ let ite ...

  9. Java 基础:变量 与 字符串

    变量 Java中没有初始化的变量是不能直接使用的 局部变量 String msg; System.out.print(msg); 就会提示错误,我们必须显式的为变量指定一个初值如null.刚开始学Ja ...

  10. cf250D. The Child and Sequence(线段树 均摊复杂度)

    题意 题目链接 单点修改,区间mod,区间和 Sol 如果x > mod ,那么 x % mod < x / 2 证明: 即得易见平凡, 仿照上例显然, 留作习题答案略, 读者自证不难. ...