【问题标题】:Merging two lists into one and sorting the items将两个列表合并为一个并对项目进行排序
【发布时间】:2011-01-17 20:50:42
【问题描述】:

有没有办法将两个给定的列表合并(没有欺骗的联合)为一个,并使用 ONE for 循环以排序方式存储项目?

另外,我正在寻找一种不使用 API 方法(如联合、排序等)的解决方案。

示例代码。

private static void MergeAndOrder() 
{
var listOne = new List<int> {3, 4, 1, 2, 7, 6, 9, 11}; 
var listTwo = new List<int> {1, 7, 8, 3, 5, 10, 15, 12}; 

//Without Using C# helper methods...
//ToDo.............................

//Using C# APi.
var expectedResult = listOne.Union(listTwo).ToList(); 
expectedResult.Sort();//Output: 1,2,3,4,5,6,7,8,9,10,11,12,15
//I need the same result without using API methods, and that too by iterating over items only once.


}

PS:我在一次采访中被问到这个问题,但还没有找到答案。

【问题讨论】:

  • 如果您不能使用 API 方法,您将如何以任何方式访问或操作列表?
  • 我怀疑OP的意思是不使用Union、Sort等方法来执行合并排序的核心操作。
  • 两个列表都已排序的前提条件?您说“一个循环”,所以我假设您的意思是整个操作的 O(n)。如果它们都没有排序,我不确定这是可能的。
  • @Jonanthon,两个列表都未排序。
  • 不要因为不知道这个问题的答案而沮丧;除非您正在面试编写专门的排序算法的工作,否则询问您在解决实际问题方面的能力是一个特别不相关的问题。

标签: c# sorting


【解决方案1】:

为什么不能使用 api 方法?重新发明轮子是愚蠢的。此外,是 .ToList() 电话正在杀死你。 永远不要打电话给.ToList().ToArray(),除非你必须这样做,因为它们会破坏你的懒惰评估。

这样做,您将枚举所需的最少数量的列表:

var expectedResult = listOne.Union(listTwo).OrderBy(i => i);

这将使用哈希集在一个循环中执行联合,并且延迟执行意味着排序的基本传递将搭载联合。但我认为不可能在一次迭代中完成排序,因为排序不是 O(n) 操作。

【讨论】:

  • @Joel,1. 目的是打破惰性评估,因为测试代码我想显示输出项。 2. 我们没有 IEnumerable 上的“排序”方法,所以我不认为你的代码行会编译。
  • @Amby 哎呀:现在应该是“OrderBy”。而且你仍然不应该调用 ToList()。您无需调用 ToList() 即可输出结果。
  • @JoelCoehoorn - 干得好!
【解决方案2】:

如果没有在合并 + 排序操作之前对两个列表进行排序的前提条件,则无法在 O(n) 时间内完成此操作(或“使用一个循环”)。

加上那个前提,问题就很简单了。

保留两个迭代器,每个列表一个。在每个循环中,比较每个列表中的元素并选择较小的元素。增加该列表的迭代器。如果您要在最终列表中插入的元素已经是该列表中的最后一个元素,请跳过插入。

在伪代码中:

List a = { 1, 3, 5, 7, 9 }
List b = { 2, 4, 6, 8, 10 }
List result = { }
int i=0, j=0, lastIndex=0
while(i < a.length || j < b.length)
    // If we're done with a, just gobble up b (but don't add duplicates)
    if(i >= a.length)
        if(result[lastIndex] != b[j])
            result[++lastIndex] = b[j]
        j++
        continue

    // If we're done with b, just gobble up a (but don't add duplicates)
    if(j >= b.length)
        if(result[lastIndex] != a[i])
            result[++lastIndex] = a[i]
        i++
        continue

    int smallestVal

    // Choose the smaller of a or b
    if(a[i] < b[j])
        smallestVal = a[i++]
    else
        smallestVal = b[j++]

    // Don't insert duplicates
    if(result[lastIndex] != smallestVal)
        result[++lastIndex] = smallestVal
end while

【讨论】:

    【解决方案3】:
        private static void MergeTwoSortedArray(int[] first, int[] second)
        {
            //throw new NotImplementedException();
    
            int[] result = new int[first.Length + second.Length];
    
            int i=0 , j=0 , k=0;
    
            while(i < first.Length && j <second.Length)
            {
                if(first[i] < second[j])
                {
                    result[k++] = first[i++];
                }
                else
                {
                    result[k++] = second[j++];
                }
            }
    
            if (i < first.Length)
            {
                for (int a = i; a < first.Length; a++)
                    result[k] = first[a];
            }
    
            if (j < second.Length)
            {
                for (int a = j; a < second.Length; a++)
                    result[k++] = second[a];
            }
    
            foreach (int a in result)
                Console.Write(a + " ");
    
            Console.WriteLine();
        }
    

    【讨论】:

      【解决方案4】:

      使用迭代器和流接口,任务并不复杂:

      类 MergeTwoSortedLists { 静态 void Main(string[] args) {

              var list1 = new List<int?>() {
                  1,3,5,9,11
              };
      
              var list2 = new List<int?>() {
                  2,5,6,11,15,17,19,29
              };
      
              foreach (var c in SortedAndMerged(list1.GetEnumerator(), list2.GetEnumerator())) {
                  Console.Write(c+" ");
              }
      
              Console.ReadKey();
          }
      
          private static IEnumerable<int> SortedAndMerged(IEnumerator<int?> e1, IEnumerator<int?> e2) {
              e2.MoveNext();
              e1.MoveNext();
      
              do {
                  while (e1.Current < e2.Current) {
                      if (e1.Current != null) yield return e1.Current.Value;
                      e1.MoveNext();
                  }
      
                  if (e2.Current != null) yield return e2.Current.Value;
                  e2.MoveNext();
              } while (!(e1.Current == null && e2.Current == null));
          }
      }
      

      【讨论】:

        【解决方案5】:

        您可以编写一个循环来合并和删除列表,并使用二进制搜索方法将新值插入目标列表。

        【讨论】:

          【解决方案6】:
          var listOne = new List<int> { 3, 4, 1, 2, 7, 6, 9, 11 };
          var listTwo = new List<int> { 1, 7, 8, 3, 5, 10, 15, 12 };
          var result = listOne.ToList();
          
          foreach (var n in listTwo)
          {
            if (result.IndexOf(n) == -1)
              result.Add(n);
          }
          

          【讨论】:

          • 每个 IndexOf 都像一个内部循环,所以这不是“一个循环”。
          【解决方案7】:

          我看到的最接近的解决方案是分配一个知道整数有界到某个值的数组。

          int[] values = new int[ Integer.MAX ]; // initialize with 0
          int size1 = list1.size();
          int size2 = list2.size();
          
          for( int pos = 0; pos < size1 + size2 ; pos++ )
          {
               int val =  pos > size1 ? list2[ pos-size1 ] : list1[ pos ] ;
               values[ val ]++;
          }
          

          然后你可以争辩说你有一个“特殊”形式的排序数组:-)为了得到一个干净的排序数组,你需要遍历values数组,用0计数跳过所有位置,然后构建最终名单。

          【讨论】:

          • 问题指示一个 for循环。
          • ...根据其他答案,除非问题存在其他条件(例如数组已经排序),否则似乎不可能。不过,我已经完善了我的答案。
          • 你的回答让我想起了 sleepsort:dis.4chan.org/read/prog/1295544154 - 每次都让我咯咯笑
          【解决方案8】:

          这仅适用于整数列表,但很高兴这就是你所拥有的!

          List<int> sortedList = new List<int>();
          foreach (int x in listOne)
          {
              sortedList<x> = x;
          }
          foreach (int x in listTwo)
          {
              sortedList<x> = x;
          }
          

          这是使用每个列表中的值作为存储值的索引位置。任何重复的值都将覆盖该索引位置的前一个条目。它满足仅对值进行一次迭代的要求。

          这当然意味着列表中会有“空”位置。

          我怀疑这个职位现在已经被填补了.... :-)

          【讨论】:

            【解决方案9】:

            试试这个:

                  public static IEnumerable<T> MergeWith<T>(IEnumerable<T> collection1, IEnumerable<T> collection2,
                        IComparer<T> comparer)
                    {
                        using (var enumerator1 = collection1.GetEnumerator())
                        using (var enumerator2 = collection2.GetEnumerator())
                        {
                            var isMoveNext1 = enumerator1.MoveNext();
                            var isMoveNext2 = enumerator2.MoveNext();
            
                            do
                            {
                                while (comparer.Compare(enumerator1.Current, enumerator2.Current) < 0 || !isMoveNext2)
                                {
                                    if (isMoveNext1)
                                        yield return enumerator1.Current;
                                    else
                                        break;
            
                                    isMoveNext1 = enumerator1.MoveNext();
                                }
            
                                if (isMoveNext2)
                                    yield return enumerator2.Current;
            
                                isMoveNext2 = enumerator2.MoveNext();
                            } while (isMoveNext1 || isMoveNext2);
                        }
                    }
            

            【讨论】:

            • 请您解释您的答案以及您的答案比其他人提供的答案更好的地方?
            • 这是 IEnumerable 的扩展方法。你可以在 LINQ 中使用它
            猜你喜欢
            • 2022-10-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2019-06-25
            • 1970-01-01
            • 2015-05-12
            • 1970-01-01
            相关资源
            最近更新 更多