简体   繁体   English

对链表进行排序

[英]Sorting a linked list

I have written a basic linked list class in C#. It has a Node object, which (obviously) represents every node in the list.我在 C# 中写了一个基本链表 class。它有一个节点 object,它(显然)代表列表中的每个节点。

The code does not use IEnumerable, however, can I implement a sorting function?代码不使用IEnumerable,但是,我可以实现排序function吗? The language I am using is C#. Is there an example of this in C#?我使用的语言是C#。C#中有这样的例子吗?

I am working from this sample :我正在使用这个示例

Thanks谢谢

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, ie 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.这非常难看,因为我使用 null 作为空列表 :) 缩进不一致等。

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);
    }
}

Of course you can implement a sorting function using a plain linked list.当然,您可以使用普通链表来实现排序功能。 Merge sort might be a suitable algorithm to try, it's fairly simple.合并排序可能是一种适合尝试的算法,它相当简单。

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.最简单的选择可能是提取数据,以已经支持排序的机制(数组、 List<T>IEnumerable<T>通过 LINQ)对其进行排序,然后使用排序后的数据重新构建链表。

If you want to write your own sort algorithm, then you may find Comparer<T>.Default useful (assuming you are using generics).如果您想编写自己的排序算法,那么您可能会发现Comparer<T>.Default很有用(假设您使用的是泛型)。 This should allow you to compare any items that are IComparable<T> or IComparable .这应该允许您比较IComparable<T>IComparable任何项目。

As an aside - note that .NET already includes LinkedList<T> etc; LinkedList<T> - 请注意,.NET 已经包含LinkedList<T>等; if this is just for learning etc, then fine ;-p如果这只是为了学习等,那很好;-p

Some people (including me) may have wanted to sort LinkedList<T> from .net library.有些人(包括我)可能想从 .net 库中LinkedList<T>进行排序

The easy way is to use Linq, sort and finally create a new linked list.简单的方法是使用 Linq,排序并最终创建一个新的链表。 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.对于想要某种程度优化的人,这里是 .net 双向链表的通用就地快速排序实现。

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);
}

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.抱歉,它是 C++,它应该翻译。

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;
}

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)通常,插入排序的效率不是很高——在最坏的情况下为O(n^2) ,但使用链表可以将其改进为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 .在常规算法中,插入部分需要O(i)因为我们可能需要移动所有比 item 大的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)因为链表使我们能够在不移位的情况下进行插入,所以我们可以使用二分查找来搜索位置,因此插入部分只需要O(log i)

note: usually an insertion sort has O(n) performance in the best case (if the array is sorted).注意:在最佳情况下(如果数组已排序),插入排序通常具有O(n)性能。 Unfortunately this isn't the case here because binary search takes always O(log n) steps.不幸的是,这里的情况并非如此,因为二分查找总是需要O(log n)步。

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;
    }

Here is an implementation based on Insertion sort .这是一个基于插入排序的实现。 It should be fast enough for small linked lists and/or if the list is almost already sorted.对于小型链接列表和/或列表几乎已经排序,它应该足够快。 For large lists, check M.kazem Akhgary answer (which use Quicksort).对于大型列表,请查看M.kazem Akhgary答案(使用快速排序)。

static void Sort<T>(this LinkedList<T> list, IComparer<T> comparer)
{
    var node = list.First;
    while (node != null)
    {
        var next = node.Next;
        
        var min = node;
        for (var comp = node.Previous; 
             comp != null && comparer.Compare(node.Value, comp.Value) < 0; 
             comp = comp.Previous)
        {
            min = comp;
        }
        
        if (node != min) 
        {               
            list.Remove(node);
            list.AddBefore(min, node);
        }
        
        node = next;
    }
}
for(int i=0; i<counter;i++)
{
  while(current.next!=null)
  {
    if(current.elemen>current.next.element)
    {
      Swap(current.elment,current.next.element);
    }
    current=current.next;
  }
}

increment counter when you add or insert elements to your linked lists向链表添加或插入元素时增加计数器

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM