乘风破浪:LeetCode真题_023_Merge k Sorted Lists
乘风破浪: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的更多相关文章
- 乘风破浪:LeetCode真题_021_Merge Two Sorted Lists
乘风破浪:LeetCode真题_021_Merge Two Sorted Lists 一.前言 关于链表的合并操作我们是非常熟悉的了,下面我们再温故一下将两个有序链表合并成一个的过程,这是基本功. 二 ...
- 【LeetCode练习题】Merge k Sorted Lists
Merge k Sorted Lists Merge k sorted linked lists and return it as one sorted list. Analyze and descr ...
- [Leetcode][Python]23: Merge k Sorted Lists
# -*- coding: utf8 -*-'''__author__ = 'dabay.wang@gmail.com' 23: Merge k Sorted Listshttps://oj.leet ...
- leetcode第22题--Merge k Sorted Lists
problem:Merge k sorted linked lists and return it as one sorted list. Analyze and describe its compl ...
- 【LeetCode】23. Merge k Sorted Lists 合并K个升序链表
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 个人公众号:负雪明烛 本文关键词:合并,链表,单链表,题解,leetcode, 力扣,Py ...
- 【一天一道LeetCode】#23. Merge k Sorted Lists
一天一道LeetCode系列 (一)题目 Merge k sorted linked lists and return it as one sorted list. Analyze and descr ...
- 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 ...
- LeetCode OJ:Merge k Sorted Lists(归并k个链表)
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 类 ...
- LeetCode LinkList 23. Merge k Sorted Lists
这两天一直也没有顾上记录一下自己做过的题目,回头看看,感觉忘的好快,今天做了一个hard,刚开始觉得挺难得,想了两种方法,一种是每次都从k个list中选取最小的一个,为空的直接跳过,再就是每次合并其中 ...
随机推荐
- java web 手动部署项目步骤
java Web 手动部署项目步骤 1 在tomcat下面的webapps下面建立需要部署的文件夹(eg:demo);2 在demo下建立 WEB-INF WETA-INF src 文件夹;3 在sr ...
- mongodb备份策略
概述 数据库的备份非常非常非常重要!!!否则出问题连哭的机会有没有(欲哭无泪)今天主要是做一个mongodb的数据库备份. 1.关于备份 备份其实很简单,这里选择的是对mongodb中的某个库进行全备 ...
- SpringMVC之使用Servlet原生API作为参数
SpringMVC的handler接收如下的ServletAPI类型的参数: • HttpServletRequest • HttpServletResponse • HttpSession • ja ...
- [原创]C#中的堆和栈理解
引言:程序运行时,它的数据必须存在内存中,一个数据需要多大内存.存储在什么地方以及如何存储都依赖于该数据的数据类型. 1.什么是栈 栈是一个内存数组,是一个LIFO(Last-In-First-Out ...
- 在.net中序列化读写xml方法的总结--转载过来学习学习
原文章地址:http://www.cnblogs.com/fish-li/archive/2013/05/05/3061816.html 首先做个大概的总结,XML包括的元素有XmlElement,X ...
- JUC源码1-原子量
什么是原子量,原子量就是一次操作,要么成功,要么失败.比如java中的i++ 或i-- , 不具备原子性,每次读取的值都是不一样的,探究其原因,x86体系中,他的总线是32位的,i++的操作指令必须是 ...
- Thinkphp 图片上传
案例:广告的增删改查 步骤: 1引用 js 2 填写 input type=" file" 的id 3 填写 url 4后台保存地址 5前台成功后的处理 广告添加页 <div ...
- Oracle数据库操作---基础使用(二)
此篇承接上一篇的基本原理,继续展开学习,本篇主要面向数据的使用和管理,也就是开发者常用的基础语句,开始喽…… >>>对整表的操作 >创建表 关键字 Create creat ...
- 【Linux】查看所使用的Linux系统是32位还是64 位的方法
查看所使用的Linux系统是32位还是64 位的方法 方法一:getconf LONG_BIT # getconf LONG_BIT 1 1 我的Linux是32位!!! 方法二:arch # arc ...
- Spring是什么 包括SpringBean SpringMVC SpringBoot SpringCloud
什么是Spring:spring是个开源框架,spring mvc是基于spring的一个mvc框架,spring boot是基于spring4的条件注册的一套快速开发整合包. Spring两大特征: ...