【问题标题】:How to implement MergeSort on Linked List using only O(1) space?如何仅使用 O(1) 空间在链表上实现 MergeSort?
【发布时间】:2017-04-23 07:52:55
【问题描述】:

如何去做这件事?自下而上的归并排序方式是否满足O(1) 额外空间要求?在传统的归并排序中是什么使它不是O(1) 空间?

这是一种方法吗?

void mergeSort() { 
    ListNode merge = this.head;
    this.head = mergeSort(merge);
}

ListNode mergeSort(ListNode head) {
    if(head == null || head.next == null)
        return head;

    ListNode slow = head; ListNode fast = head.next; 
    while(fast!=null && fast.next !=null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    ListNode left = head; ListNode right = slow.next;
    slow.next = null;
    left = mergeSort(left);
    right = mergeSort(right);
    return merge(left, right);
}

ListNode merge(ListNode L, ListNode R) {
    if(L==null) return R;
    if(R==null) return L;
    ListNode h = null;
    if(L.val <= R.val )){
        h = L;
        h.next = merge(L.next, R);
    } else {
        h = R;
        h.next = merge(L, R.next);
    }
    return h;
}

【问题讨论】:

标签: java linked-list mergesort space-complexity


【解决方案1】:

链表的自顶向下合并排序将消耗 O(log(n)) 堆栈空间,并且由于扫描列表以拆分它们,它比自底向上方法慢。由于递归 merge(),问题中的示例代码将占用 O(n) 堆栈空间。

链表的自下而上合并排序使用一个小的(25 到 32)固定大小的节点引用(或指针)数组,这将满足 O(1) 空间要求。链接到维基文章。如果有兴趣,我可以发布示例 java 代码。

https://en.wikipedia.org/wiki/Merge_sort#Bottom-up_implementation_using_lists

示例java代码:

    // merge two already sorted lists
    public ListNode merge(ListNode list0, ListNode list1) {
        if(list0 == null)
            return list1;
        if(list1 == null)
            return list0;
        ListNode temp = new ListNode(0);   // dummy node
        ListNode dest = temp;
        while(true){
            if(list0.element <= list1.element){
                dest.next = list0;
                dest = list0;
                list0 = list0.next;
                if(list0 == null){
                    dest.next = list1;
                    break;
                }
            } else {
                dest.next = list1;
                dest = list1;
                list1 = list1.next;
                if(list1 == null){
                    dest.next = list0;
                    break;
                }
            }
        }
        return temp.next;
    }
    // sort list
    public ListNode sort(ListNode head) {
        final int NUMLIST = 32;
        ListNode[] alist = new ListNode[NUMLIST];
        ListNode node;
        ListNode next;
        int i;
        // if < 2 nodes, return
        if(head == null || head.next == null)
            return;
        node = head;
        // merge node into array
        while(node != null){
            next = node.next;
            node.next = null;
            for(i = 0; (i < NUMLIST) && (alist[i] != null); i++){
                node = merge(alist[i], node);
                alist[i] = null;
            }
            if(i == NUMLIST)   // don't go past end of array
                i--;
            alist[i] = node;
            node = next;
        }
        // node == null
        // merge array into single list
        for(i = 0; i < NUMLIST; i++)
            node = merge(alist[i], node);
        return node;
    }

【讨论】:

  • 再次感谢。您说问题中的 merge 函数将占用 O(n) 空间,这是否意味着 mergeSort 函数 O(1) 和 merge 使其成为 O(n)?如果我使用您提供的merge 函数,那会使整个归并排序的空间复杂度为 O(1) 吗?
  • @AlonzoRobbe - 问题中的合并排序代码占用 O(log(n)) 堆栈空间,因为它是自上而下的合并排序。
  • 开枪,真的吗?我曾尝试在您的答案中实现您的sort 代码,但是我无法从数组转换为链表。你介意帮助我吗?
  • @AlonzoRobbe - 我的答案中的示例代码已经对链表进行了排序。数组 alist[] 用于在排序期间存储内部工作列表。 alist[i] 要么为空,要么指向具有 2 的 i 次幂节点的列表。如果不为空,则 alist[0] 指向一个有 1 个节点的列表, alist[1] 指向一个有 2 个节点的列表, alist[2] 指向一个有 4 个节点的列表, alist[3] 指向一个有8 个节点,... .
  • @rcdldr 哦,我明白了。这就说得通了。非常感谢。所以你提供的代码使用 O(1) 空间,据我所知,需要 O(nlogn) 时间,对吗?
猜你喜欢
  • 2012-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
相关资源
最近更新 更多