Sorting a linked list

前端 未结 8 734
-上瘾入骨i
-上瘾入骨i 2020-11-29 09:26

I have written a basic linked list class in C#. It has a Node object, which (obviously) represents every node in the list.

The code does not use IEnumerable, however

相关标签:
8条回答
  • 2020-11-29 10:06

    If you want to really utilize the fact that you're using a linked list, as opposed to working around it, I would suggest insertion sort.

    Normally an insertion sort is not very efficient - O(n^2) in the worst case, but with linked list this can be improved to O(n log n)

    pseudo:

    for i in range (1,n):
        item = arr[i]
        location = binary_search(l=root, r=i, value=item.val)  // O(log i)
        insert_item_before(arr[location], item) // O(1)
    

    In the regular algorithm the insert part takes O(i) because we may need to shift all the items that are larger then item. because a linked list enables us to do the insertion without shifting, we can search for the location with a binary search and so the insertion part takes only O(log i)

    note: usually an insertion sort has O(n) performance in the best case (if the array is sorted). Unfortunately this isn't the case here because binary search takes always O(log n) steps.

    0 讨论(0)
  • 2020-11-29 10:13

    Functional Quicksort and Mergesort

    Here's a linked list with quicksort and mergesort methods written in a functional style:

    class List
    {
        public int item;
        public List rest;
    
        public List(int item, List rest)
        {
            this.item = item;
            this.rest = rest;
        }
    
        // helper methods for quicksort
    
        public static List Append(List xs, List ys)
        {
            if (xs == null) return ys;
            else return new List(xs.item, Append(xs.rest, ys));
        }
    
        public static List Filter(Func<int,bool> p, List xs)
        {
            if (xs == null) return null;
            else if (p(xs.item)) return new List(xs.item, Filter(p, xs.rest));
            else return Filter(p, xs.rest);
        }
    
        public static List QSort(List xs)
        {
            if (xs == null) return null;
            else
            {
                int pivot = xs.item;
                List less = QSort(Filter(x => x <= pivot, xs.rest));
                List more = QSort(Filter(x => x > pivot, xs.rest));
                return Append(less, new List(pivot, more));
            }
        }
    
        // Helper methods for mergesort
    
        public static int Length(List xs)
        {
            if (xs == null) return 0;
            else return 1 + Length(xs.rest);
        }
    
        public static List Take(int n, List xs)
        {
            if (n == 0) return null;
            else return new List(xs.item, Take(n - 1, xs.rest));
        }
    
        public static List Drop(int n, List xs)
        {
            if (n == 0) return xs;
            else return Drop(n - 1, xs.rest);
        }
    
        public static List Merge(List xs, List ys)
        {
            if (xs == null) return ys;
            else if (ys == null) return xs;
            else if (xs.item <= ys.item) return new List(xs.item, Merge(xs.rest, ys));
            else return new List(ys.item, Merge(xs, ys.rest));
        }
    
        public static List MSort(List xs)
        {
            if (Length(xs) <= 1) return xs;
            else
            {
                int len = Length(xs) / 2;
                List left  = MSort(Take(len, xs));
                List right = MSort(Drop(len, xs));
                return Merge(left, right);
            }
        }
    
        public static string Show(List xs)
        {
            if(xs == null) return "";
            else return xs.item.ToString() + " " + Show(xs.rest);
        }
    }
    

    Functional heapsort using a Pairing Heap

    Bonus: heapsort (using functional pairing heap).

    class List
    {
        // ...
    
        public static Heap List2Heap(List xs)
        {
            if (xs == null) return null;
            else return Heap.Merge(new Heap(null, xs.item, null), List2Heap(xs.rest));
        }
    
        public static List HSort(List xs)
        {
            return Heap.Heap2List(List2Heap(xs));
        }
    }
    
    class Heap
    {
        Heap left;
        int min;
        Heap right;
    
        public Heap(Heap left, int min, Heap right)
        {
            this.left = left;
            this.min = min;
            this.right = right;
        }
    
        public static Heap Merge(Heap a, Heap b)
        {
            if (a == null) return b;
            if (b == null) return a;
    
            Heap smaller = a.min <= b.min ? a : b;
            Heap larger = a.min <= b.min ? b : a;
            return new Heap(smaller.left, smaller.min, Merge(smaller.right, larger));
        }
    
        public static Heap DeleteMin(Heap a)
        {
            return Merge(a.left, a.right);
        }
    
        public static List Heap2List(Heap a)
        {
            if (a == null) return null;
            else return new List(a.min, Heap2List(DeleteMin(a)));
        }
    }
    

    For actual use you want to rewrite the helper methods without using recursion, and maybe use a mutable list like the built-in one.

    How to use:

    List xs = new List(4, new List(2, new List(3, new List(1, null))));
    Console.WriteLine(List.Show(List.QSort(xs)));
    Console.WriteLine(List.Show(List.MSort(xs)));
    Console.WriteLine(List.Show(List.HSort(xs)));
    

    Imperative In-place Quicksort for linked lists

    An in-place version was requested. Here's a very quick implementation. I wrote this code top to bottom without looking for opportunities to make the code better, i.e. every line is the first line that came to mind. It's extremely ugly because I used null as the empty list :) The indentation is inconsistent, etc.

    Additionally I tested this code on only one example:

            MList ys = new MList(4, new MList(2, new MList(3, new MList(1, null))));
            MList.QSortInPlace(ref ys);
            Console.WriteLine(MList.Show(ys));
    

    Magically it worked the first time! I'm pretty sure that this code contains bugs though. Don't hold me accountable.

    class MList
    {
        public int item;
        public MList rest;
    
        public MList(int item, MList rest)
        {
            this.item = item;
            this.rest = rest;
        }
    
        public static void QSortInPlace(ref MList xs)
        {
            if (xs == null) return;
    
            int pivot = xs.item;
            MList pivotNode = xs;
            xs = xs.rest;
            pivotNode.rest = null;
            // partition the list into two parts
            MList smaller = null; // items smaller than pivot
            MList larger = null; // items larger than pivot
            while (xs != null)
            {
                var rest = xs.rest;
                if (xs.item < pivot) {
                    xs.rest = smaller;
                    smaller = xs;
                } else {
                    xs.rest = larger;
                    larger = xs;
                }
                xs = rest;
            }
    
            // sort the smaller and larger lists
            QSortInPlace(ref smaller);
            QSortInPlace(ref larger);
    
            // append smaller + [pivot] + larger
            AppendInPlace(ref pivotNode, larger);
            AppendInPlace(ref smaller, pivotNode);
            xs = smaller;
        }
    
        public static void AppendInPlace(ref MList xs, MList ys)
        {
            if(xs == null){
                xs = ys;
                return;
            }
    
            // find the last node in xs
            MList last = xs;
            while (last.rest != null)
            {
                last = last.rest;
            }
            last.rest = ys;
        }
    
        public static string Show(MList xs)
        {
            if (xs == null) return "";
            else return xs.item.ToString() + " " + Show(xs.rest);
        }
    }
    
    0 讨论(0)
  • 2020-11-29 10:19

    Some people (including me) may have wanted to sort LinkedList<T> from .net library.

    The easy way is to use Linq, sort and finally create a new linked list. but this creates garbage. for small collections it would not be a problem, but for large collections it may not be so efficient.

    for people who want some degree of optimization, here is generic In-place quick sort implementation for .net doubly linked list.

    this implementation does not split/merge, instead it checks nodes for boundaries of each recursion.

    /// <summary>
    /// in place linked list sort using quick sort.
    /// </summary>
    public static void QuickSort<T>(this LinkedList<T> linkedList, IComparer<T> comparer)
    {
        if (linkedList == null || linkedList.Count <= 1) return; // there is nothing to sort
        SortImpl(linkedList.First, linkedList.Last, comparer);
    }
    
    private static void SortImpl<T>(LinkedListNode<T> head, LinkedListNode<T> tail, IComparer<T> comparer)
    {
        if (head == tail) return; // there is nothing to sort
    
        void Swap(LinkedListNode<T> a, LinkedListNode<T> b)
        {
            var tmp = a.Value;
            a.Value = b.Value;
            b.Value = tmp;
        }
    
        var pivot = tail;
        var node = head;
        while (node.Next != pivot)
        {
            if (comparer.Compare(node.Value, pivot.Value) > 0)
            {
                Swap(pivot, pivot.Previous);
                Swap(node, pivot);
                pivot = pivot.Previous; // move pivot backward
            }
            else node = node.Next; // move node forward
        }
        if (comparer.Compare(node.Value, pivot.Value) > 0)
        {
            Swap(node, pivot);
            pivot = node;
        }
    
        // pivot location is found, now sort nodes below and above pivot.
        // if head or tail is equal to pivot we reached boundaries and we should stop recursion.
        if (head != pivot) SortImpl(head, pivot.Previous, comparer);
        if (tail != pivot) SortImpl(pivot.Next, tail, comparer);
    }
    
    0 讨论(0)
  • 2020-11-29 10:19
    public LinkedListNode<int> Sort2(LinkedListNode<int> head, int count)
        {
            var cur = head;
            var prev = cur;
            var min = cur;
            var minprev = min;
    
            LinkedListNode<int> newHead = null;
            LinkedListNode<int> newTail = newHead;
    
            for (int i = 0; i < count; i++)
            {
                cur = head;
                min = cur;
                minprev = min;
    
                while (cur != null)
                {
                    if (cur.Value < min.Value)
                    {
                        min = cur;
                        minprev = prev;
                    }
                    prev = cur;
                    cur = cur.Next;
                }
    
                if (min == head)
                    head = head.Next;
                else if (min.Next == null)
                    minprev.Next = null;
                else
                    minprev.Next = minprev.Next.Next;
    
                if (newHead != null)
                {
                    newTail.Next = min;
                    newTail = newTail.Next;
                }
                else
                {
                    newHead = min;
                    newTail = newHead;
                }
            }
            return newHead;
        }
    
    0 讨论(0)
  • 2020-11-29 10:20

    This might not be the best solution, but it's as simple as I can come up with. If anyone can think of something simpler but still fast, I'd love to hear it.
    SORRY THAT IT'S C++ it should translate.

    List * SortList(List * p_list)
    {
         if(p_list == NULL || p_list->next == NULL) 
              return p_list;
    
         List left, right;
         List * piviot = p_list;
         List * piviotEnd = piviot;
         List * next = p_list->next;
         do
         {
                  p_list = next;
                  next = next->next;
                  //FIGURE OUT WHICH SIDE I'M ADDING TO.
                  if(p_list->data > piviot->data ) 
                      right.InsertNode(p_list);
                  else if(p_list->data < piviot->data)
                      left.InsertNode(p_list);
                  else
                  {   //we put this in it's own list because it doesn't need to be sorted
                      piviotEnd->next = p_list;
                      piviotEnd= p_list;
                  }  
         }while(next);
    
         //now left contains values < piviot and more contains all the values more
         left.next = SortList(left.next);
         right.next = SortList(right.next);
    
         //add the piviot to the list then add the rigth list to the full list
         left.GetTail()->next = piviot;
         piviotEnd->next = right.next;
    
         return left.next;
    }
    
    0 讨论(0)
  • 2020-11-29 10:23

    The simplest option is probably to extract the data, sort it in a mechanism that already supports sorting (arrays, List<T> or IEnumerable<T> via LINQ), and re-build the linked list with the sorted data.

    If you want to write your own sort algorithm, then you may find Comparer<T>.Default useful (assuming you are using generics). This should allow you to compare any items that are IComparable<T> or IComparable.

    As an aside - note that .NET already includes LinkedList<T> etc; if this is just for learning etc, then fine ;-p

    0 讨论(0)
提交回复
热议问题