[英]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谢谢
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);
}
}
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)));
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.