【算法】合并k个有序的链表-基于最小堆的思想
来源:互联网 发布:w10怎么安装软件 编辑:程序博客网 时间:2024/05/21 19:49
题目大意
合并k个已经排序好了的链表。
大致思路
可以借助堆排序的思想,使用小顶堆来实现。
先将K个链表的头元素构建成一个最小堆,之后,取堆顶元素,这个结点就是最小的,放入保存结果的集合,接着将原堆顶元素所在链表的下一元素入堆并重新构建最小堆,之后,再取堆顶元素,这个结点就是第二小的……如此往复操作,知道将所有链表的所有元素都取完。
但是,在将原堆顶元素对应的链表中的下一元素入堆时,会遇到一种情况——不存在下一个元素,即原堆顶元素所在链表都取完值了,此时则将最小堆的末尾元素换置换置堆顶,此时真是的最小堆大小将减一。
具体实现如下:
public class MergeKList { static class Node implements Comparable<Node> { int val;//元素的值 int indexForLists;//该元素所在的链表处于K个链表的位置 int indexInList;//该元素处在对应链表的位置 public Node(int val, int indexForLists, int indexInList) { this.val = val; this.indexForLists = indexForLists; this.indexInList = indexInList; } @Override public int compareTo(Node o) { if (val == o.val) return 0; else if (val > o.val) return 1; else return -1; } } public static List<Integer> mergeKList(ArrayList<ArrayList<Integer>> lists) { if (lists == null || lists.size() == 0) return null; List<Integer> list = new LinkedList<>(); //首先将k个链表的首个元素取出构成最小堆 Node[] minHeap = new Node[lists.size()]; int trueLen = 0; Iterator<ArrayList<Integer>> iterator = lists.iterator(); while (iterator.hasNext()) { ArrayList<Integer> arrayList = iterator.next(); if (arrayList != null && arrayList.size() > 0) { minHeap[trueLen] = new Node(arrayList.get(0), trueLen, 0); trueLen++; } else { //去除无效的链表 iterator.remove(); } } for (int i = trueLen / 2 - 1; i >= 0; i--) { buildMinHeap(minHeap, i, trueLen); } while (true) { //取出最小堆堆堆顶元素 Node heapTop = minHeap[0]; list.add(heapTop.val); //取原堆顶元素所在链表的下一元素 ArrayList<Integer> arrayList = lists.get(heapTop.indexForLists); if (heapTop.indexInList < arrayList.size() - 1) {//如果还有下一个元素,则把它对应的Node节点放到堆定 int indexInList = heapTop.indexInList + 1; minHeap[0] = new Node(arrayList.get(indexInList), heapTop.indexForLists, indexInList); //重新构建最小堆 buildMinHeap(minHeap, 0, trueLen); } else {//如果不存在下一个元素,即原堆顶元素所在链表都取完值了,则将最小堆的末尾元素换置换置堆顶,trueLen-- minHeap[0] = minHeap[trueLen - 1]; minHeap[trueLen - 1] = null; trueLen--; //如果最小堆真实的大小只剩为1,则表示之剩下一个链表的剩下元素没有合并了,且直接储存结果的集合末尾添加即可 if (trueLen==1) { heapTop = minHeap[0]; list.add(heapTop.val); ArrayList<Integer> lastArrayList = lists.get(heapTop.indexForLists); for (int i = heapTop.indexInList+1; i < lastArrayList.size(); i++) { list.add(lastArrayList.get(i)); } break; } else { //重新构建最小堆 buildMinHeap(minHeap, 0, trueLen); } } } return list; } public static void buildMinHeap(Node[] nodes, int i, int heapSize) { int min, left, right; while (true) { min = i; left = 2 * i + 1; right = left + 1; if (left < heapSize && nodes[left].compareTo(nodes[min]) < 0) { min = left; } if (right < heapSize && nodes[right].compareTo(nodes[min]) < 0) { min = right; } if (min != i) { Node temp = nodes[min]; nodes[min] = nodes[i]; nodes[i] = temp; i = min; } else { break; } } }}
假设总共有k个list,每个list的最大长度是n
维护一个大小为k的堆,每次取堆顶的最小元素放到结果中,然后读取该元素的下一个元素放入堆中,重新维护好。因为每个链表是有序的,每次又是去当前k个元素中最小的,所以当所有链表都读完时结束,这个时候所有元素按从小到大放在结果链表中。这个算法每个元素要读取一次,即是k*n次,然后每次读取元素要把新元素插入堆中要log k的复杂度,所以总时间复杂度是O(nk logk)
。空间复杂度是堆的大小,即为O(k)
。
阅读全文
0 0
- 【算法】合并k个有序的链表-基于最小堆的思想
- 合并K个有序链表-堆的使用
- 算法导论第三版第六章 合并K个有序链表的三种解法(最小堆法和分治递归法)
- 合并k个有序的链表
- 经典算法——合并K个有序链表
- 两有序数组两两之和的最小k个值, 最小堆解法之完整版
- 基于最小二叉堆的优先级队列-C#实现,以此为基础的K路合并排序算法
- LeetCode 合并k个有序链表
- 合并k个有序链表
- 合并k个有序链表
- 合并K个有序链表
- 基于堆的K路合并问题
- 2个有序链表的合并
- 俩个有序链表的合并
- 合并k个有序链表 Merge k Sorted Lists
- 合并两个有序链表的算法
- 算法:两个有序链表的合并
- 算法(时间复杂度为O(nlgk)的k有序链(从小到大)表合并为一个有序链表
- 12月7号
- Mysql
- 深入理解Spring 之 源码剖析AOP(XML配置方式)
- ACM 某种序列
- 经典面试题golang实现方式(一)
- 【算法】合并k个有序的链表-基于最小堆的思想
- python通过string运行代码
- 动态存储器分配:内存动态分区分配方式的理解以及模拟(一)
- 数据结构:一元多项式相加和相减
- matlab写二次指数平滑
- poi 3.8 SXSSFWorkbook没有删除本地缓存文件方法
- win 10 PHP开发环境配置
- 全面提高你的搜索技巧
- MapReduce中的Shuffle和Sort分析