Marcin Hoppe

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
How to keep List<T> sorted?

The Problem 

Recently I ran into a need to keep a list of integers sorted in ascending order. The list was used as an internal representation of a set, so I didn't have to create a new class implementing IList<T> or anything similar. The whole issue was to implement adding new elements properly. Of course, I started with writing a routine to find a proper place for a new element, move all subsequent elements and so on. This is quite a bit of code and it is easy to make a mistake here.

The scaffolding looks like this:

public class Set
{
    List<int> sortedList = new List<int>();
    public void Add(int i)
    {
        // To implement.
    }
}

The Solution 

There is, however, a very simple way to do add elements in such a way that the whole list stays sorted. How do you find a proper place for a new element if you have a sorted list? You use binary search. As it turned out, List<T>.BinarySearch method returns very useful return value. If the element I am looking for is already in the list, it returns its index. This way I know if I should add the element to the list or not (I don't want duplicate elements so I don't do anything if this happens). If the element is not in the list, List<T>.BinarySearch method returns a negative number that is a binary complement of an index of the first element that is greater that the element I passed to binary search method. If there is no greater element in the list, a binary complement of the value of Count property is returned.

How can that be useful? If we take a binary complement of the value returned by List<T>.BinarySearch (if it is lower than 0, of course), we have an index that we should use to insert our element in order to keep the list sorted! If the binary complement we get is equal to Count, the element is appended at the end of the list, as expected. Here is how the simple implementation might look like:

public void Add(int i)
{
    int index = sortedList.BinarySearch(i);
    if (index < 0)
    {
        sortedList.Insert(~index, i);
    }
}

Little Optimization

There is a little optimization that I wanted to implement. The typical scenario for my set class is to initialize it by adding multiple elements in ascending order. With the current implementation, there would be many binary searches that would always end up returning the binary complement of the Count value. We can circumvent this easily by checking if the added element is greater than the last element and directly appending it at the end of the list, if this is the case. Here is how the final implementation could look like:

public void Add(int i)
{
    if (sortedList.Count == 0)
    {
        sortedList.Add(i);
        return;
    }
    if (sortedList[sortedList.Count - 1] < i)
    {
        sortedList.Add(i);
        return;
    }
    int index = sortedList.BinarySearch(i);
    if (index < 0)
    {
        sortedList.Insert(~index, i);
    }
}

Posted 05-15-2007 10:46 AM by Marcin Hoppe
Filed under:

[Advertisement]

Comments

krECik wrote re: How to keep List<T> sorted?
on 05-15-2007 5:17 PM

You could optimize it more in similar way adding after second if block another one.

if( sortedList[0] > i ) {

 sortedList.Insert(0, i);

 return;

}

Keith Nicholas wrote re: How to keep List<T> sorted?
on 05-15-2007 11:26 PM

The first two inserts should be Add, it won't make any difference to the performance (other than probably eliminating one check) but it simplifies the code a bit

Marcin Hoppe wrote re: How to keep List<T> sorted?
on 05-16-2007 4:35 AM

@krECik:

This is indeed an easy optimization. It didn't come to my mind as in my scenario (during the initialization of the program) I am always adding new items in ascending order, so new items always end up at the end of the list.

@Keith:

This is a good suggestion. Reflector tells me that Add is a little bit simpler than Insert. I will update the entry.

dwvisser wrote re: How to keep List<T> sorted?
on 05-18-2007 12:39 PM

I would actually have taken a different approach. I'm lazy by nature, and would have just taken advantage of the existing SortedList class in .Net 2.0:

   public class Set

   {

       SortedList<int, int> sortedList = new SortedList<int, int>();

       public void Add(int i)

       {

           sortedList.Add(i, i);

       }

       public int this[int index]

       {

           get

           {

               return sortedList.Keys[index];

           }

       }

   }

It's annoying that the only SortedList implementation they give uses KeyValue pairs as the type, but oh well. Also, .Net's SortedList requires unique keys, but since you're calling your class Set, that's probably not an issue in this case.

Marcin Hoppe wrote re: How to keep List<T> sorted?
on 05-21-2007 6:02 AM

I feel a little bit uncomfortable with storing twice as much data as needed. This might be an issue in my scenario.

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)