使用最小插入数和删除数重新排序IList <t> </t>

时间:2013-09-24 18:55:24

标签: c# sorting

我需要一个带有两个参数的扩展方法:目标IList<T>和自定义比较器。

public static void CustomSort<T>( IList<T> target, IComparer<T> comparer )
{
    ...
}

扩展方法的工作是按照自定义比较器指示的顺序排列目标元素。

以下是无法工作的一些解决方案:

  • List<T>.Sort进行就地排序,这基本上就是我所追求的。但我不能假设目标的具体类型是List<T>
  • LINQ提供了一些解决方案,但不幸的是,他们将结果输出为新的可枚举,而不是修改目标列表。

相反,我需要调用目标的RemoveInsert方法,以使其元素按正确顺序排列。目标列表可能是可观察的,因此重要的是解决方案不会进行比获得所需订单所需的更多删除和插入。

3 个答案:

答案 0 :(得分:2)

我觉得这样的事情对你有用。

我们对一组偏移进行排序,然后确定需要应用的增量集,最后应用它们来生成新排序的IList。

public static void CustomSort<T>( IList<T> list , IComparer<T> comparer )
{
  int[] unsorted = Enumerable.Range(0,list.Count).ToArray() ;
  int[] sorted   = Enumerable.Range(0,list.Count).OrderBy( x => list[x] , comparer ).ToArray() ;
  var   moves    = sorted.Zip( unsorted , (x,y) => new{ Src = x , Dest = y , } ).Where( x => x.Src != x.Dest ).OrderBy( x => x.Src ).ToArray() ;

  // at this point, moves is a list of moves, from a source position to destination position.
  // We enumerate over this and apply the moves in order.
  // To do this we need a scratch pad to save incomplete moves, where an existing item has
  // been over-written, but it has not yet been moved into its final destination.

  Dictionary<int,T> scratchPad = new Dictionary<int, T>() ; // a parking lot for incomplete moves
  foreach ( var move in moves )
  {
    T value ;

    // see if the source value is on the scratchpad.
    // if it is, use it and remove it from the scratch pad.
    // otherwise, get it from the indicated slot in the list.
    if ( scratchPad.TryGetValue( move.Src , out value ) )
    {
      scratchPad.Remove( move.Src );
    }
    else
    {
      value = list[ move.Src ] ;
    }

    // if the destination is after the source position, we need to put
    // it on the scratch pad, since we haven't yet used it as a source.
    if ( move.Dest > move.Src )
    {
      scratchPad.Add( move.Dest , list[ move.Dest ]);
    }

    // finally, move the source value into its destination slot.
    list[ move.Dest ] = value ;

  }

  return ;
}

答案 1 :(得分:1)

评论表明,如上所述,这实际上可能是一个难题。那么如果我们从不同的角度来看待这个问题呢:

  1. 为什么这个系列的观察者需要这么长时间?他们应该只根据个人变化做快速的事情。例如。如果它是用于UI更改,它可能会等到下一个UI重绘之后再关注更改。这应该让整个重新整理完成一次。或
  2. 使用ObservableRangeCollection<T>之类的内容,以便在可观察的集合中获得AddRange。如果可能,一些简单的代码可以确定集合是List<T>还是ObservableRangeCollection<T>以使用AddRange
  3. E.g。

    public static void SmartSort<T>(IList<T> source)
    {
        var sortedList = source.OrderBy(x => x).ToList();
        if (source.SequenceEqual(sortedList))
            return;
        var list = source as List<T>;
        var collection = source as ObservableRangeCollection<T>;
        if (list != null)
        {
            list.Clear();
            list.AddRange(sortedList);
        }
        else if (collection != null)
            collection.ReplaceRange(sortedList);
        else
        {
            for (int i = 0; i < source.Count; i++)
                source[i] = sortedList[i];
        }
    }
    

答案 2 :(得分:1)

  1. 使用LINQ扩展方法排序并获取一个新的List,最后隐式调用ToList();

  2. 然后告诉您的可观察列表暂停(不关心更改)

  3. 清除您的可观察列表

  4. 从linq-ordered列表中将项添加到observable列表中。

  5. 告诉您的可观察列表继续观看更改

  6. 观察者观察的变化数量是否有效;从算法效率的角度来看,我试试这个解决方案。您可能永远不需要进一步优化。请记住: premature optimization is the root of all evil