views:

1964

answers:

4

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, I can implement a sorting function? The language I am using is C#. Is there an example of this in C#?

I am working from this sample: http://www.c-sharpcorner.com/UploadFile/jeradus/UsingLinkedListInCS11102005005525AM/UsingLinkedListInCS.aspx

Thanks

+10  A: 

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.

unwind
+1 For suggesting Merge sort, which is very well-suited towards linked lists.
Brian
A: 

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

Marc Gravell
I'm sorry mate, I have to vote you down. I'd expect that whom ever implemented their own linked list did it for a reason (can't think of one but who knows). I had to do this in an interview and I doubt saying use STL would have sufficed.
baash05
OK... kinda freaky - 3 downvotes in 2 days on a 4-week-old reply? Something dubious going on...
Marc Gravell
I stand by my reply to baash05: Re your comment "I doubt saying use STL would have sufficed" - when interviewing, I'd question the sanity of anyone who *didn't* choose to use the pre-canned BCL without a good reason. The OP didn't state a reason, therefore the BCL should be the default.
Marc Gravell
I tried to vote you back up Marc... Assume though Marc that you come to a project that has a link list implemented locally and you have to sort it. Moving it into a the STL and then back out so the rest of the app can use it would be a huge strain on resources, not to mention memory. The question didn't say don't use, but it didn't say don't send it to India and have someone else do it either. It merely asked how and you didn't say how. Still I don't agree with a big neg on the answer. I hate neg values on answers. They negate the effort and I tell you I appreciate all "our" efforts.
baash05
Cheers for the sentiment ;-p
Marc Gravell
+12  A: 

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);
    }
}
Jules
I am unsure how it works. I do know how quicksort works in theory, but I am not sure about this code.Could you show me an example where you use quicksort on a list and it would display the sorted list when it is done?
CasperT
I added an example. This quicksort works like this: Select the first item as the pivot. Then split the remaining items in two lists: less and more. Less contains all items less than or equal to the pivot, and more contains the rest (items that are larger than the pivot). The next step is tot sort these two lists seperately. When that's done we build a list from the three parts, less+pivot+more in that order.
Jules
I looked high and low and can't find the definition of 'x' in your qsort function. Can you pls edit this.. I find it very interesting.
baash05
Also.. can you do any of these sorts without the use of new? In a memory tight world how would you accomplish these feats?
baash05
you can do both quicksort and mergesort in-place.
Nathaniel Flath
@Aseraphim: I'd like to see you try to implement in-place sorting on a linked list. Easy with the O(n^2) sorts, but with quicksort and mergesort, I wouldn't be so fast to say that it's possible.
ephemient
X comes from the parameter of the lambda expression. When you do Filter(x => (x < 3), theList) it computes a new list containing only the elements that are less than 3. So it's a bit like for(x in theList){ ... } but instead of just executing the (...) it also computes a new list.
Jules
An in-place Quicksort for linked lists is possible. You could just use the linked list as an array but because of O(n) indexing the sort wouldn't be O(n log n) anymore.But it's possible to implement a O(n log n) in-place quicksort for linked lists. I'm pretty sure it's possible for mergesort too.I added an in-place quicksort that doesn't new up at all (but it uses O(log n) stack space of course, but the in-place array quicksort also uses O(log n) stack space).
Jules
Do this functionality really not already exist in c#?
Catskul
It does exist for the built-in data types in C#, but not for a custom *linked* list.
Jules
A: 

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;
}
baash05
Re your comment "I doubt saying use STL would have sufficed" - when interviewing, I'd question the sanity of anyone who *didn't* choose to use the pre-canned BCL without a good reason. The OP didn't state a reason, therefore the BCL should be the default.
Marc Gravell
Agreed. professionally 100% on board with you. I'd never think to implement a linked list. I'd also point that out in the interview (were I being questioned). Often they want to see you sweat though so you still got to implement it.
baash05
Well now.. Here I go disagreeing with myself. --- I'm working in code that has no access to STL.. So I need to create a sorter myself.. Happily I wrote this answer, so I'm going to use it..:)
baash05