K路歸併問題小結

K路歸併問題小結

聲明

文章均爲本人技術筆記,轉載請註明出處:
[1] https://segmentfault.com/u/yzwall
[2] blog.csdn.net/j_dark/java

1 二路歸併

1.1 歸併排序-數組

歸併排序思想:歸併排序可用分治法實現,先保證局部有序,而後二路歸併局部有序部分爲總體有序;算法

/**
 * http://www.lintcode.com/en/problem/sort-integers-ii/
 * 歸併排序一個整型數組(升序)
 * @author yzwall
 */
class Solution {
    public void sortIntegers2(int[] A) {
        if (A == null || A.length == 0) {
            return;
        }
        
        mergeSort(A, 0, A.length - 1);
    }
    // 分治遞歸空間O(logn),逐層合併時間複雜度O(n)
    private void mergeSort(int[] A, int start, int end) {
        if (start >= end) {
            return;
        }
        int mid = start + (end - start) / 2;
        mergeSort(A, start, mid);
        mergeSort(A, mid + 1, end);
        mergeTwoArrays(A, start, end);
    }
    
    // 二路歸併時間複雜度O(n),空間複雜度O(n)
    private void mergeTwoArrays(int[] A, int start, int end) {
        int mid = start + (end - start) / 2;
        int i = start, j = mid + 1;
        int index = 0;
        int[] temp = new int[end - start + 1];
        while (i <= mid && j <= end) {
            if (A[i] < A[j]) {
                temp[index++] = A[i++];
            } else {
                temp[index++] = A[j++];
            }
        }
        while (i <= mid) {
            temp[index++] = A[i++];
        }
        while (j <= end) {
            temp[index++] = A[j++];
        }
        
        for (int k = 0; k < index; k++) {
            A[start + k] = temp[k];
        }
    }
}

1.2 合併兩個排序數組

class Solution {
    public int[] mergeSortedArray(int[] A, int[] B) {
        if (A == null || B == null) {
            return new int[0]; 
        }
        ArrayList<Integer> list = new ArrayList<>();
        int i = 0, j = 0;
        while (i < A.length && j < B.length) {
            if (A[i] <= B[j]) {
                list.add(A[i]);
                i++;
            } else {
                list.add(B[j]);
                j++;
            }
        }
        while (i < A.length) {
            list.add(A[i++]);
        }
        while (j < B.length) {
            list.add(B[j++]);
        }
        
        int[] results = new int[list.size()];
        for (i = 0; i < list.size(); i++) {
            results[i] = list.get(i);
        }
        return results;
    }
}

1.2 合併兩個排序數組

/**
 * 合併兩個有序數組,雙指針法二路歸併算法,最後覆蓋A數組
 * http://www.lintcode.com/zh-cn/problem/merge-sorted-array/
 * @author yzwall
 */
class Solution {
    public void mergeSortedArray(int[] A, int m, int[] B, int n) {
        int i = 0, j = 0;
        ArrayList<Integer> list = new ArrayList<>();
        while (i < m && j < n) {
            if (A[i] <= B[j]) {
                list.add(A[i]);
                i++;
            } else {
                list.add(B[j]);
                j++;
            }
        }
        while (i < m) {
            list.add(A[i++]);
        }
        while (j < n) {
            list.add(B[j++]);
        }
        for (int k = 0; k < list.size(); k++) {
            A[k] = list.get(k);
        }
    }
}

1.3 合併兩個排序鏈表

class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null && l2 == null) {
            return null;
        }
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        
        ListNode pIter1 = l1, pIter2 = l2;
        ListNode head = new ListNode(-1);
        ListNode pIter = head;
        while (pIter1 != null && pIter2 != null) {
            if (pIter1.val < pIter2.val) {
                pIter.val = pIter1.val;
                pIter1 = pIter1.next;
            } else {
                pIter.val = pIter2.val;
                pIter2 = pIter2.next;
            }
            ListNode pNew = new ListNode(-1);
            pIter.next = pNew;
            pIter = pIter.next;
        }
        
        while (pIter1 != null) {
            pIter.val = pIter1.val;
            pIter1 = pIter1.next;
            if (pIter1 != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
        }
        
        while (pIter2 != null) {
            pIter.val = pIter2.val;
            pIter2 = pIter2.next;
            if (pIter2 != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
        }
        return head;
    }
}

1.4 歸併排序-鏈表

2 K路歸併排序

2.1 K路歸併算法實現小結

假定k路數組全爲升序數組,元素總數爲n,code

  • 解法1:兩兩二路歸併O(n*k)實現
    對k路數組兩兩進行二路歸併,時間複雜度高;blog

  • 解法2:分治法O(nlogk)實現
    分治思想自底向上,逐層二路歸併,遞歸空間爲O(logk),逐層二路歸併時間複雜度O(n);排序

  • 解法3:最小堆O(nlogk)實現
    用最小堆維護k路數組or鏈表的當前元素,問題轉換爲求k個數的最小值,堆頂出堆後將堆頂所在部分的下一個元素(若是存在你在)加入堆中,歸併過程當中訪問堆k次,每次訪問offerpoll的開銷爲O(log k);

2.2 K路歸併數組多種解法實現

/**
 * 解法1:逐個合併數組,時間複雜度O(n*k),n >> k
 * 合併k個排序(升序)數組
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-arrays/
 * @author yzwall
 */
class Solution {
    public List<Integer> mergekSortedArrays(int[][] arrays) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (arrays == null || arrays.length == 0 || arrays[0].length == 0) {
            return list;
        }
        if (arrays.length == 1) {
            Arrays.sort(arrays[0]);
            for (int num : arrays[0]) {
                list.add(num);
            }
            return list;
        }
        
        int[] temp = mergeTwoArrays(arrays[0], arrays[1]);
        for (int i = 2; i < arrays.length; i++) {
            temp = mergeTwoArrays(temp, arrays[i]);
        }
        for (int num : temp) {
            list.add(num);
        }
        return list;
    }
    
    private int[] mergeTwoArrays(int[] A, int[] B) {
        if (A.length == 0 || B.length == 0) {
            return new int[0];
        }
        int[] temp = new int[A.length + B.length];
        int index = 0, i = 0, j = 0;
        while (i < A.length && j < B.length) {
            if (A[i] < B[j]) {
                temp[index++] = A[i++];
            } else {
                temp[index++] = B[j++];
            }
        }
        while (i < A.length) {
            temp[index++] = A[i++];
        }
        while (j < B.length) {
            temp[index++] = B[j++];
        }
        return temp;
    }
}

/**
 * 解法2:分治法K路歸併,時間複雜度O(n logk)
 * 合併k個排序(升序)數組
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-arrays/
 * @author yzwall
 */    
class Solution20 {
    public List<Integer> mergekSortedArrays(int[][] arrays) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (arrays == null || arrays.length == 0 || arrays[0].length == 0) {
            return list;
        }
        int[] ans = kMergeSort(arrays, 0, arrays.length - 1);
        for (int num : ans) {
            list.add(num);
        }
        return list;
    }
    
    // 分治遞歸深度爲O(log k), 每層合併時間複雜度O(n)
    private int[] kMergeSort(int[][] arrays, int start, int end) {
        if (start >= end) {
            return arrays[start];
        }
        int mid = start + (end - start) / 2;
        int[] left = kMergeSort(arrays, start, mid);
        int[] right = kMergeSort(arrays, mid + 1, end);
        return mergeTwoArrays(left, right);
    }
    
    private int[] mergeTwoArrays(int[] A, int[] B) {
        int[] temp = new int[A.length + B.length];
        int index = 0, i = 0, j = 0;
        while (i < A.length && j < B.length) {
            if (A[i] < B[j]) {
                temp[index++] = A[i++];
            } else {
                temp[index++] = B[j++];
            }
        }
        while (i < A.length) {
            temp[index++] = A[i++];
        }
        while (j < B.length) {
            temp[index++] = B[j++];
        }
        return temp;
    }
}

/**
 * 解法3:最小堆實現K路歸併,時間複雜度O(n logk)
 * 合併k個排序(升序)數組
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-arrays/
 * @author yzwall
 */
class Solution18 {
    private class NewInteger {
        int value, row, col;
        public NewInteger(int value, int row, int col) {
            this.value = value;
            this.row = row;
            this.col = col;
        }
    }
    
    public List<Integer> mergekSortedArrays(int[][] arrays) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (arrays == null || arrays.length == 0 || arrays[0].length == 0) {
            return list;
        }
        PriorityQueue<NewInteger> pq = new PriorityQueue<>(arrays.length, new Comparator<NewInteger>() {
            public int compare(NewInteger o1, NewInteger o2) {
                return o1.value < o2.value ? -1 : 1;
            }
        });
        
        for (int i = 0; i < arrays.length; i++) {
            pq.offer(new NewInteger(arrays[i][0], i, 0));
        }
        while (!pq.isEmpty()) {
            NewInteger min = pq.poll();
            if (min.col + 1 < arrays[min.row].length) {
                pq.offer(new NewInteger(arrays[min.row][min.col + 1], min.row, min.col + 1));
            }
            list.add(min.value);
        }
        
        return list;
    }
}

/**
 * 解法4:暴力方法,將全部數組添加到List,統一排序,時間複雜度O(n*k + nlogn), n >> k
 * 合併k個排序(升序)數組
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-arrays/
 * @author yzwall
 */
class Solution19 {
    public List<Integer> mergekSortedArrays(int[][] arrays) {
        ArrayList<Integer> list = new ArrayList<Integer>();
        if (arrays == null || arrays.length == 0 || arrays[0].length == 0) {
            return list;
        }
        for (int i = 0; i < arrays.length; i++) {
            addToList(list, arrays[i]);
        }
        Collections.sort(list);
        return list;
    }
    
    private void addToList(ArrayList<Integer>list, int[] nums) {
        for (int num : nums) {
            list.add(num);
        }
    }
}

2.3 K路歸併鏈表多種解法實現

/**
 * 解法1:最小堆實現K路歸併,時間複雜度O(nlogk)
 * 合併K路排序鏈表
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-lists/
 * @author yzwall
 */
class Solution {
    public ListNode mergeKLists(List<ListNode> lists) {
        if (lists == null || lists.size() == 0) {
            return null;
        }
        
        PriorityQueue<ListNode> pq = new PriorityQueue<>(lists.size(), new Comparator<ListNode>() {
            public int compare(ListNode o1, ListNode o2) {
                return o1.val < o2.val ? -1 : 1;
            }
        });
        
        for (ListNode head : lists) {
            if (head != null) {
                pq.offer(head);
            }
        }
        ListNode head = new ListNode(-1);
        ListNode pIter = head;
        while (!pq.isEmpty()) {
            ListNode min = pq.poll();
            if (min.next != null) {
                pq.offer(min.next);
            }
            ListNode pNew = new ListNode(min.val);
            pIter.next = pNew;
            pIter = pIter.next;
        }
        head = head.next;
        return head;
    }    
}

/**
 * 解法2:分治法K路歸併
 * 合併K路排序鏈表
 * http://www.lintcode.com/zh-cn/problem/merge-k-sorted-lists/
 * @author yzwall
 */
class Solution22 {
    public ListNode mergeKLists(List<ListNode> lists) {
        if (lists == null || lists.size() == 0) {
            return null;
        }
        return kMergeSort(lists, 0, lists.size() - 1);
    }
    
    private ListNode kMergeSort(List<ListNode> lists, int start, int end) {
        if (start >= end) {
            return lists.get(start);
        }
        int mid = start + (end - start) / 2;
        ListNode left = kMergeSort(lists, start, mid);
        ListNode right = kMergeSort(lists, mid + 1, end);
        return mergeTwoLists(left, right);
    }

    private ListNode mergeTwoLists(ListNode left, ListNode right) {
        if (left == null) {
            return right;
        }
        if (right == null) {
            return left;
        }
        
        ListNode head = new ListNode(-1);
        // pIter始終指向新節點
        ListNode pIter = head;
        ListNode p1 = left, p2 = right;
        while (p1 != null && p2 != null) {
            if (p1.val < p2.val) {
                pIter.val = p1.val;
                p1 = p1.next;
            } else {
                pIter.val = p2.val;
                p2 = p2.next;
            }
            ListNode pNew = new ListNode(-1);
            pIter.next = pNew;
            pIter = pIter.next;
        }
        while (p1 != null) {
            pIter.val = p1.val;
            if (p1.next != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
            p1 = p1.next;
        }
        while (p2 != null) {
            pIter.val = p2.val;
            if (p2.next != null) {
                ListNode pNew = new ListNode(-1);
                pIter.next = pNew;
                pIter = pIter.next;
            }
            p2 = p2.next;
        }
        return head;
    }
}
相關文章
相關標籤/搜索