C#:可以返回新添加对象的排序位置的通用排序容器?

时间:2009-01-16 15:45:45

标签: c# .net generics sorting

我需要一个通用容器来保持其元素的排序,并且可以询问它将插入新元素的位置(在哪个位置),而不实际插入它。

.NET库中是否存在这样的容器? 最好的例子是一个例子(容器按ASCII值对字符进行排序,假设unicode不存在):

sortedContainer.Add('d');
sortedContainer.Add('b');
sortedContainer.Add('g');

//container contains elements ordered like 'b' 'd' 'g'
//index  -------------------------------->  0   1   2

sortedContainer.GetSortedIndex('a'); //returns 0
sortedContainer.GetSortedIndex('b'); //returns 0

sortedContainer.GetSortedIndex('c'); //returns 1
sortedContainer.GetSortedIndex('d'); //returns 1

sortedContainer.GetSortedIndex('e'); //returns 2
sortedContainer.GetSortedIndex('f'); //returns 2
sortedContainer.GetSortedIndex('g'); //returns 2

sortedContainer.GetSortedIndex('h'); //returns 3
[...]

搜索位置应该利用元素排序的事实。

3 个答案:

答案 0 :(得分:6)

如果您对List<T>进行排序,然后使用List<T>.BinarySearch,它将为您提供条目的索引(如果存在),或者 的索引的按位补码>插入后插入然后排序。从那以后,您应该能够轻松地构建您的方法。

示例代码与您的示例匹配,但不符合结果 - 如果您查看示例,则只有3个条目,因此“h”返回4或“g”返回3没有意义我希望这是你的例子稍微偏离,而不是我误解了问题:)注意排序不是自动的 - 你必须在调用GetSortedIndex之前显式排序列表。

using System;
using System.Collections.Generic;

static class Test
{
    static int GetSortedIndex<T>(this List<T> list, T entry)
    {
        int index = list.BinarySearch(entry);
        return index >= 0 ? index : ~index;
    }

    static void Main()
    {
        List<char> container = new List<char> { 'b', 'd', 'g' };
        Console.WriteLine(container.GetSortedIndex('a'));
        Console.WriteLine(container.GetSortedIndex('b'));
        Console.WriteLine(container.GetSortedIndex('c'));
        Console.WriteLine(container.GetSortedIndex('d'));
        Console.WriteLine(container.GetSortedIndex('e'));
        Console.WriteLine(container.GetSortedIndex('f'));
        Console.WriteLine(container.GetSortedIndex('g'));
        Console.WriteLine(container.GetSortedIndex('h'));
    }
}

答案 1 :(得分:1)

最接近你要找的是SortedList<TKey,TValue>。此类将维护排序列表顺序。

但是,没有方法可以为新值添加索引。但是,您可以编写一个扩展方法,为您提供新索引

public int GetSortedIndex<TKey,TValue>(this SortedList<TKey,TValue> list, TKey key) {
  var comp = list.Comparer;
  for ( var i = 0; i < list.Count; i++ ) {
    if ( comp.Compare(key, list.GetKey(i)) < 0 ) {
      return i;
    }
  }
  return list.Count;
}

答案 2 :(得分:0)

听起来像一个有趣的测试,所以我试了一下。可能不是最优雅,也不是最有效,但这应该有效:

 public class SortedContainer<T> : IList<T> where T : IComparable<T>
    {
        private List<T> internalList = new List<T>();

        public int IndexOf(T item)
        {
            return internalList.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            internalList.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            internalList.RemoveAt(index);
        }

        public T this[int index]
        {
            get
            {
                return internalList[index];
            }
            set
            {
                internalList[index] = value;
            }
        }

        public void Add(T item)
        {
            internalList.Add(item);
            this.Sort();
        }

        public void Clear()
        {
            internalList.Clear();
        }

        public bool Contains(T item)
        {
            return internalList.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            internalList.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return internalList.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(T item)
        {
            bool result = internalList.Remove(item);
            this.Sort();
            return result;
        }



        public IEnumerator<T> GetEnumerator()
        {
            return internalList.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return internalList.GetEnumerator();
        }

        private void Sort()
        {
            internalList.Sort();
        }

        private List<T> GetCopy()
        {
            return internalList.ToList();
        }

        public int GetSortedIndex(T item)
        {
            List<T> copy = GetCopy();
            copy.Add(item);
            copy.Sort();
            return copy.IndexOf(item);
        }

    }

基本上,实现IList,并保留一个internatl List。每次调用Add方法时,都会在内部列表上调用sort。然后,当您想要获取已排序的索引而不实际添加时,它会创建该列表的副本,将该项插入到副本中,然后对该副本进行排序。然后它返回该项目的索引。此时的内部列表尚未受到影响。我测试了它并且它有效。

同样,可能效率不高。