代码之家  ›  专栏  ›  技术社区  ›  Jim Balter

如何合并排序双链接列表?

  •  0
  • Jim Balter  · 技术社区  · 7 年前

    我还没有找到C#的实现,而且在我看来,其他语言的实现并不令人满意。。。需要事先知道节点的数量,不是针对双链表还是单链表进行优化,或者效率低下,过于复杂。。。我会贴出我自己的答案。

    1 回复  |  直到 7 年前
        1
  •  1
  •   Jim Balter    7 年前

    这是我的解决方案。在我自己的代码中,这个类中有更多的方法,以及一个附带的头类,但是我去掉了除merge sort之外的所有内容。这个实现假设列表的头和尾指向指向它们的头节点,空列表由一个header节点表示,Next和Prev指向自身。(这是一种经典的技术,它简化了在头部和尾部使用空指针的情况。)

    public class Node<T> where T: Node<T>
    {
        public T Next, Prev;
    
        /// <summary>
        /// Sorts the nodes from <paramref name="first"/> to <paramref name="last"/> inclusive, in place.
        /// </summary>
        public static void MergeSort(T first, T last, IComparer<T> comparer)
        {
            // return if 0 or 1 nodes
            if (ReferenceEquals(first, last))
                return;
    
            // optimization for two nodes
            if (ReferenceEquals(first.Next, last))
            {
                if (comparer.Compare(first, last) > 0)
                    MoveBefore(first, last, last);
                return;
            }
    
            var mid = Midpoint(first, last);
    
            // a little messy here ... get the nodes before and after each
            // segment before MergeSort reorders it,
            // then determine the new beginning and end from those
    
            var p1 = first.Prev;
            var p2 = mid.Next;
            MergeSort(first, mid, comparer);
            p1 = p1.Next;
            mid = p2.Prev;
    
            var ln = last.Next;
            MergeSort(p2, last, comparer);
            p2 = mid.Next;
            last = ln.Prev;
    
            // merge p1 and p2
            for (;;)
            {
                for (;; p1 = p1.Next)
                {
                    if (ReferenceEquals(p1, p2))
                        return;
                    if (comparer.Compare(p1, p2) > 0)
                        break;
                }
                var start = p2;
                do
                {
                    if (ReferenceEquals(p2, last))
                    {
                        MoveBefore(p1, start, p2);
                        return;
                    }
                    p2 = p2.Next;
                }
                while (comparer.Compare(p1, p2) > 0);
    
                MoveBefore(p1, start, p2.Prev);
            }
        }
    
        /// <summary>
        /// Moves the nodes from <paramref name="first"/> and <paramref name="last"/> inclusive
        /// from where they are in the list to before <paramref name="target"/>.
        /// </summary>
        public static void MoveBefore(T target, T first, T last)
        {
            first.Prev.Next = last.Next;
            last.Next.Prev = first.Prev;
    
            (first.Prev = target.Prev).Next = first;
            (last.Next = target).Prev = last;
        }
    
        /// <summary>
        /// Returns the node midway between <paramref name="first"/> and <paramref name="last"/>.
        /// If there are an even number of nodes, returns the last node in the first half.
        /// </summary>
        /// <remarks>
        /// Undefined if <paramref name="first"/> and <paramref name="last"/> aren't in the same list.
        /// </remarks>
        public static T Midpoint(T first, T last)
        {
            for (;; first = first.Next)
                if (ReferenceEquals(first, last) || ReferenceEquals(first, last = last.Prev))
                    return first;
        }
    }