【问题标题】:Natural mergesort a linked list自然归并排序一个链表
【发布时间】:2012-05-03 00:07:58
【问题描述】:

我一直在寻找一个自然合并排序实现(链接列表),但没有成功。

Merge Sort a Linked List

这里我们同时拥有递归和迭代实现,但我不知道如何将其变成自然归并排序。

如何检查运行以在最佳情况下获得 O(n) 复杂度?它不必是 C/C++,可以是任何语言甚至是伪代码。

谢谢。

【问题讨论】:

    标签: sorting linked-list mergesort


    【解决方案1】:

    Wikipedia 上有一个伪代码实现:

     # Original data is on the input tape; the other tapes are blank
     function mergesort(input_tape, output_tape, scratch_tape_C, scratch_tape_D)
         while any records remain on the input_tape
             while any records remain on the input_tape
                 merge( input_tape, output_tape, scratch_tape_C)
                 merge( input_tape, output_tape, scratch_tape_D)
             while any records remain on C or D
                 merge( scratch_tape_C, scratch_tape_D, output_tape)
                 merge( scratch_tape_C, scratch_tape_D, input_tape)
    
     # take the next sorted chunk from the input tapes, and merge into the single given output_tape.
     # tapes are scanned linearly.
     # tape[next] gives the record currently under the read head of that tape.
     # tape[current] gives the record previously under the read head of that tape.
     # (Generally both tape[current] and tape[previous] are buffered in RAM ...)
     function merge(left[], right[], output_tape[])
         do
            if left[current] ≤ right[current]
                append left[current] to output_tape
                read next record from left tape
            else
                append right[current] to output_tape
                read next record from right tape
        while left[current] < left[next] and right[current] < right[next]
        if left[current] < left[next]
            append current_left_record to output_tape
        if right[current] < right[next]
            append current_right_record to output_tape
        return
    

    【讨论】:

    • 谢谢,它不是来自维基百科。这似乎适用于链表,但这种设计需要通过引用传递或双指针,这在我的目标语言中不可用。仍在尝试解决此问题。
    • 不,不能将此逻辑应用于链表。
    • 伪代码要么不完整,要么我误解了,要么就是错了。也不能让它在纸上/理论上起作用。
    【解决方案2】:

    这是我在 F# 中的尝试。一个正则归并排序的实现供参考:

    // Sorts a list containing elements of type T.  Takes a comparison
    // function comp that takes two elements of type T and returns -1
    // if the first element is less than the second, 0 if they are equal,
    // and 1 if the first element is greater than the second.
    let rec sort comp = function 
    | []  -> []  // An empty list is sorted
    | [x] -> [x] // A single element list is sorted
    | xs  ->
        // Split the list in half, sort both halves,
        // and merge the sorted halves.
        let half = (List.length xs) / 2
        let left, right = split half xs
        merge comp (sort comp left) (sort comp right)
    

    现在尝试自然版本。最好的情况是 O(n),但最好的情况是输入列表是反向排序的。

    let rec sort' comp ls =
    
        // Define a helper function.  Streaks are stored in an accumulator.
        let rec helper accu = function
        | [] -> accu 
        | x::xs -> 
            match accu with
            // If we are not in a streak, start a new one
            | [] -> helper [x] xs 
    
            // If we are in a streak, check if x continues
            // the streak.
            | y::ys -> 
                if comp y x > 0 
    
                // x continues the streak so we add it to accu
                then helper (x::y::ys) xs
    
                // The streak is over. Merge the streak with the rest
                // of the list, which is sorted by calling our helper function on it.
                else merge comp accu (helper [x] xs)
    
        helper [] ls
    

    第二次尝试。在最好的情况下,这也是 O(n),现在最好的情况是输入列表已经排序。我否定了比较功能。排序后的列表将以相反的顺序构建,因此您需要在最后反转它。

    let rec sort'' comp ls =
        // Flip the comparison function
        let comp' = fun x y -> -1 * (comp x y)
        let rec helper accu = function
        | [] -> accu
        | x::xs -> 
            match accu with
            | [] -> helper [x] xs
            | y::ys -> 
                if comp' y x > 0 
                then helper (x::y::ys) xs
                else merge comp' accu (helper [x] xs)
    
        // The list is in reverse sorted order so reverse it.
        List.rev (helper [] ls)
    

    【讨论】:

      【解决方案3】:

      我不确定什么是自然归并排序,但是对于链表的归并排序,我是这样写的:

      [Java 代码]

      // Merge sort the linked list.
      // From min to max.
      // Time complexity = O(nlgn).
      public static Node mergeSortLLFromMinToMax (Node head) {
          if (head == null || head.next == null) return head; // No need to sort.
          // Get the mid point of this linked list.
          Node prevSlower = head;
          Node slower = head;
          Node faster = head;
          while (faster != null && faster.next != null) {
              prevSlower = slower;
              slower = slower.next;
              faster = faster.next.next;
          }
          // Cut of the main linked list.
          prevSlower.next = null;
      
          // Do recursion.
          Node left = mergeSortLLFromMinToMax (head);
          Node right = mergeSortLLFromMinToMax (slower);
      
          // Merge the left and right part from min to max.
          Node currHead = new Node ();
          Node tempCurrHead = currHead;
          while (left != null && right != null) {
              if (left.data <= right.data) {
                  // Add the elem of the left part into main linked list.
                  tempCurrHead.next = left;
                  left = left.next;
              } else {
                  // Add the elem of the right part into main linked list.
                  tempCurrHead.next = right;
                  right = right.next;
              }
              tempCurrHead = tempCurrHead.next;
          }
          if (left != null) {
              // Add the remaining part of left part into main linked list.
              tempCurrHead.next = left;
              left = left.next;
              tempCurrHead = tempCurrHead.next;
          } else if (right != null) {
              // Add the remaining part of right part into main linked list.
              tempCurrHead.next = right;
              right = right.next;
              tempCurrHead = tempCurrHead.next;
          }
      
          return currHead.next;
      }
      

      【讨论】:

        【解决方案4】:

        我使用 C# 对算法的非常原始的实现

        public static class LinkedListSort
        {
            public static DataStructures.Linear.LinkedListNode<T> Sort<T>(DataStructures.Linear.LinkedListNode<T> firstNode) where T : IComparable<T>
            {
                if (firstNode == null)
                    throw new ArgumentNullException();
        
                if (firstNode.Next == null)
                    return firstNode;
        
                var head = firstNode;
                var leftNode = head;
                int iterNum = 0;
        
                while (leftNode != null)
                {
                    //Let's start again from the begining
                    leftNode = head;
                    iterNum = 0;
                    DataStructures.Linear.LinkedListNode<T> tailNode = null;
        
                    while (leftNode != null)
                    {
                        //Let's get the left sublist
        
                        //Let's find the node which devides sublist into two ordered sublists
                        var sentinelNode = GetSentinelNode(leftNode);
                        var rightNode = sentinelNode.Next;
        
                        //If the right node is null it means that we don't have two sublist and the left sublist is ordered already
                        //so we just add the rest sublist to the tail
                        if (rightNode == null)
                        {
                            if (tailNode == null)
                                break;
                            tailNode.Next = leftNode;
                            break;
                        }
        
                        sentinelNode.Next = null;
        
                        //Let's find the node where the right sublist ends
                        sentinelNode = GetSentinelNode(rightNode);
                        var restNode = sentinelNode.Next;
                        sentinelNode.Next = null;
        
                        DataStructures.Linear.LinkedListNode<T> newTailNode = null;
        
                        //Merging of two ordered sublists   
                        var mergedList = Merge(leftNode, rightNode, ref newTailNode);
                        //If we're at the beginning of the list the head of the merged sublist becomes the head of the list
                        if (iterNum == 0)                   
                            head = mergedList;                  
                        else //add the                  
                            tailNode.Next = mergedList;                     
        
                        tailNode = newTailNode;
                        leftNode = restNode;
                        iterNum++;
                    }
                    if (iterNum == 0)
                        break;
                }
                return head;
            }
        
            /// <summary>
            /// Merges two ordered sublists   
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="aNode">Left part of sublist</param>
            /// <param name="bNode">Right part of sublist</param>
            /// <param name="tailNode">Tail node of the merged list</param>
            /// <returns>The result of merging</returns>
            private static DataStructures.Linear.LinkedListNode<T> Merge<T>(DataStructures.Linear.LinkedListNode<T> leftNode,                                                                       
                                                                            DataStructures.Linear.LinkedListNode<T> rightNode,
                                                                            ref DataStructures.Linear.LinkedListNode<T> tailNode) where T : IComparable<T>
            {
                var dummyHead = new DataStructures.Linear.LinkedListNode<T>();
                var curNode = dummyHead;
        
                while (leftNode != null || rightNode != null)
                {
                    if (rightNode == null)
                    {
                        curNode.Next = leftNode;
                        leftNode = leftNode.Next;
                    }
                    else if (leftNode == null)
                    {
                        curNode.Next = rightNode;
                        rightNode = rightNode.Next;
                    }
                    else if (leftNode.Value.CompareTo(rightNode.Value) <= 0)
                    {
                        curNode.Next = leftNode;
                        leftNode = leftNode.Next;
                    }
                    else
                    {
                        curNode.Next = rightNode;
                        rightNode = rightNode.Next;
                    }
                    curNode = curNode.Next;
                }
                tailNode = curNode;
                return dummyHead.Next;
            }
        
            /// <summary>
            /// Returns the sentinel node
            /// </summary>
            /// <typeparam name="T"></typeparam>
            /// <param name="firstNode"></param>
            /// <returns></returns>
            private static DataStructures.Linear.LinkedListNode<T> GetSentinelNode<T>(DataStructures.Linear.LinkedListNode<T> firstNode) where T : IComparable<T>
            {
                var curNode = firstNode;
        
                while (curNode != null && curNode.Next != null && curNode.Value.CompareTo(curNode.Next.Value) <= 0)             
                    curNode = curNode.Next;
        
                return curNode;
            }
        }
        

        【讨论】:

        • 我看不到这在输入数据中标识运行的位置,正如问题的 natural 部分和 O(n) 最佳情况运行时所要求的那样。你能启发我吗?
        • @MarkRansom 自然归并排序假设我们利用自然发生的排序序列。在我的实现中,我利用了这种情况。当输入数据的所有元素都已排序时,我们得到的最佳情况为 O(n)。我编辑的实现也利用了这种情况。
        猜你喜欢
        • 2015-06-10
        • 1970-01-01
        • 2016-09-11
        • 2018-01-04
        • 2019-07-29
        • 1970-01-01
        • 2016-06-07
        • 2019-09-26
        • 2014-09-18
        相关资源
        最近更新 更多