找到列表中元素的最佳位置

时间:2009-04-14 17:25:58

标签: c# .net algorithm list sorting

我有按特定顺序填充的List集合(要求是,此顺序无法更改)。此列表包含实体类型对象。

在列表的初始填充之后,我需要插入更多来自另一个数据源的对象。这些对象需要插入特定位置,以便排序正确。

例如,如果初始列表具有以下元素

  1. AAA
  2. AAB
  3. AAC
  4. ACC
  5. ADA
  6. 在初始填充后我想插入“ABB”元素,需要在3和4之间插入。

    目前,我有以下方法为新元素找到正确的位置。

        private static int FindPositionForArticle(string word)        
        {
            string key = word.ToLower();
            for (int i = word.Length; i >= 0; i--)
            {
                if(i < word.Length)
                    key = key.Remove(i, 1);
    
                int pos = 0;
                int insertPos = 0;
                foreach(ArticleEntity article in list)
                {
                    if(article.Text.ToLower().StartsWith(key))
                        insertPos = pos;
                    else if (!article.Text.ToLower().StartsWith(key) && insertPos > 0)
                        return insertPos++;
                    pos++;
                }
            }
            return 0;
        }
    

    这种方法背后的目的是:

    1. 选择需要插入的“单词”并尝试查找与“单词”同名的元素的位置

    2. 如果未找到任何内容,请从“字词”中删除最后一个字符,然后重新搜索。

    3. 重复删除最后一个字符,直到找到最佳位置。

    4. 不幸的是我的方法有bug(实现不正确)。目前我的方法表明最佳位置为0,这是完全错误的。

      如果您想使用我的示例代码,可以在以下位置下载:

      http://dl.getdropbox.com/u/204110/FindPosition.cs.txt

      提前谢谢你。

7 个答案:

答案 0 :(得分:5)

int index = list.BinarySearch(word);

如果index为正数或零,则在列表中找到该项目。如果为负数,则它包含列表中下一个最高项的索引的按位补码。

所以,为此:

List<string> list = new List<string>{"AAA","AAB","AAC","ACC","ADA"};
int index = list.BinarySearch("ABB"); // => -4
int insertIndex = ~index; // => 3
list.Insert(insertIndex, "ABB");

答案 1 :(得分:2)

类型字符串的SortedList不会更好吗?

答案 2 :(得分:2)

由于您可以通过索引访问该列表,因此您可能希望实现类似于Chris Doggett的anwser的二进制搜索。这将为您提供比当前按顺序实现它更好的运行时间:

您的运行时当前与列表中的项目数呈线性关系,如果您插入 n 这样的项目,您将获得大约 n 2 操作。

使用二进制搜索(顺便说一句,内部为SortedList),每个插入的 log2(n)的运行时间会更好,例如: * n *** log2(n)*插入n个项目时的总运行时间。

二元搜索的基本思想很简单:

  1. 列出项目

  2. 首先,您将整个范围视为可能的位置(左= 0,右=列表中的当前计数)

  3. 如果左边等于右边,你找到了合适的位置

  4. 否则,选择中间元素(左+右)/ 2并将其与要插入的键进行比较。

    • 如果比较结果是&lt; 0,你设置为中间索引-1,因此将下一次迭代的搜索范围重置为列表的左半部分
    • 如果结果大于0,则将左侧设置为中间索引+ 1,以便下一次搜索位于右侧
    • 否则比较为0并且您有一个重复的句柄(忽略或插入相同的索引),并打破循环(参见5.)
  5. 3点回路。搜索范围现在变小......

答案 3 :(得分:1)

您是否考虑过使用SortedList数据结构?如果它将包含需要以特定方式排序的复杂数据类型,则可以创建指定IComparer对象的数据类型。

IComparer唯一要做的就是能够确定一个项目之前或之后是否属于一个项目。鉴于此,SortedList将正确保持顺序。

使用此结构可以保证在内部指定时保持顺序,这样您就不必维护基本上实现排序的代码。

答案 4 :(得分:1)

此代码:

    private static System.Collections.SortedList list = new System.Collections.SortedList();

    static void Main(string[] args)
    {
        list.Add("AAA" , "AAA");
        list.Add("AAB", "AAB");
        list.Add("AAC", "AAC");
        list.Add("ACC", "ACC");
        list.Add("ADA", "ADA");
        Console.WriteLine("List before");
        for (int j = 0; j < list.Count ; j++)
        {
            Console.WriteLine(list.GetByIndex(j));
        }
        list.Add("ABB","ABB");


        Console.WriteLine(list.IndexOfKey("ABB"));

        for (int j = 0; j < list.Count; j++)
        {
            Console.WriteLine(list.GetByIndex(j));
        }


        Console.ReadLine();

    }

正在您需要的位置插入项目..

或者你还需要别的吗?没有比较Icompare,你正在排序常见的字符串。

答案 5 :(得分:1)

我已经完成了你想要做的事情。您有一个ArticleEntity对象列表。您需要对此列表进行排序。首先,您需要添加对您的类进行排序的功能。我的例子是在VB中,不应该太难转换为C#。

为您的班级添加排序,如下所示:

 Public Class ArticleEntity
    Implements IComparable(Of ArticleEntity)

    Public Overloads Function CompareTo(ByVal Other As ArticleEntity) As Integer _
        Implements IComparable(Of ArticleEntity).CompareTo
        Return Me._PropertyToSort.CompareTo(Other._PropertyToSort)
    End Function

然后,在添加了需要添加到List Of(ArticleEntity)的所有内容之后,您可以:

MyListOfArticleEntities.Sort()

我认为这就是你要找的东西。如果不是,请告诉我。

答案 6 :(得分:1)

您的收藏是否需要List

如果在插入新元素之前不需要从列表中访问元素,heap听起来会更好地完成工作。基本上你会:

  1. 将所有初始元素插入堆
  2. 稍后,以完全相同的方式插入额外的项目
  3. 按排序顺序将每个项目从堆中读出到List
  4. 每个步骤中的每个步骤仅为每个对象的O(log n)时间。我确信C#实现了堆。

    注意:我的意思是“堆”在数据结构的意义上,而不是全局内存池。