標籤:數組 歸併 面試 演算法 offer
【023-Merge k Sorted Lists(合并k個排好的的單鏈表)】
【LeetCode-面試演算法經典-Java實現】【所有題目目錄索引】
原題
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity.
題目大意
合并k個排好的的單鏈表。分析和描述它的複雜性。
解題思路
使用小頂堆來實現,先將K個鏈表的頭結點入堆,取堆頂元素,這個結點就是最小的,接著讓取出的這個結點的下一個結點入堆,再取堆頂元素,其為第二小的結點,一直這樣子操作,直到所有的結點都處理完,這樣就完成了鏈表的合并,更多細節請見代碼和注釋。假設K個鏈表一共有N個結點,時間複雜度是O(N*log(K)),空間複雜度是O(K)。
代碼實現
結點類
public class ListNode { int val; ListNode next; ListNode(int x) { val = x; }}
演算法實作類別
public class Solution { 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(); } }}
評測結果
點擊圖片,滑鼠不釋放,拖動一段位置,釋放後在新的視窗中查看完整圖片。
特別說明
歡迎轉載,轉載請註明出處【http://blog.csdn.net/derrantcm/article/details/47016131】
著作權聲明:本文為博主原創文章,未經博主允許不得轉載。
【LeetCode-面試演算法經典-Java實現】【023-Merge k Sorted Lists(合并k個排好的的單鏈表)】