Is there a way to generically quickly insert (log(n)) an item into any IList<T>

蓝咒 提交于 2020-06-17 12:55:05

问题


In WPF, I want to keep my ObservableCollection sorted. Items are added randomly and not in batch. I do not want to sort the entire IList each time. I want to insert at the proper place on each insert.

Is there a way to generically insert quickly (log(n)) an item into any IList of T.

Does anyone wrote the code for it?

Note: Yes, the list should already be sorted before calling this method.

Note2: Sometimes you have to use an IList and can't replace it with a SortedList and do not want to maintain 2 collections: For example: in WPF using an ObservableCollection.

Update, yes there is a way to do it. By using dichotomy. I ask the question to save me time. I'm writing it and will show it as soon as it is debugged.


回答1:


Here are some useful extension methods for sorted IList<T> collections (SortedBinarySearch, SortedIndexOf, SortedInsert and SortedRemove). The binary search algorithm is stolen from the source code of the ArraySortHelper<T>.InternalBinarySearch method.

/// <summary>Searches within the sorted <see cref="IList{T}"/> for the
/// specified item and returns the zero-based index of the item if found;
/// otherwise, a negative number that is the bitwise complement of the index of
/// the next item that is larger than item or, if there is no larger item,
/// the bitwise complement of <see cref="IList{T}.Count"/>.</summary>
public static int SortedBinarySearch<T>(this IList<T> list, T item,
    IComparer<T> comparer = null)
{
    if (list == null) throw new ArgumentNullException(nameof(list));
    comparer = comparer ?? Comparer<T>.Default;

    int lo = 0;
    int hi = list.Count - 1;
    while (lo <= hi)
    {
        int i = lo + ((hi - lo) >> 1);
        int order = comparer.Compare(list[i], item);

        if (order == 0) return i;
        if (order < 0)
        {
            lo = i + 1;
        }
        else
        {
            hi = i - 1;
        }
    }
    return ~lo;
}

/// <summary>Searches for the specified item within the sorted
/// <see cref="IList{T}"/> and returns the zero-based index of the item
/// if found; otherwise, -1.</summary>
public static int SortedIndexOf<T>(this IList<T> list, T item,
    IComparer<T> comparer = null)
{
    int index = SortedBinarySearch(list, item, comparer);
    if (index < 0) return -1;
    return index;
}

/// <summary>Inserts an item into the sorted <see cref="IList{T}"/>.</summary>
public static int SortedInsert<T>(this IList<T> list, T item,
    IComparer<T> comparer = null)
{
    int index = SortedBinarySearch(list, item, comparer);
    if (index < 0) index = ~index;
    list.Insert(index, item);
    return index;
}

/// <summary>Removes an item from the sorted <see cref="IList{T}"/> and returns
/// true if the item is successfully removed; otherwise, false.</summary>
public static bool SortedRemove<T>(this IList<T> list, T item,
    IComparer<T> comparer = null)
{
    int index = SortedBinarySearch(list, item, comparer);
    if (index < 0) return false;
    list.RemoveAt(index);
    return true;
}



回答2:


OK... I did it before someone would close my question (it almost happen with 2 votes to close)...

Although there was 2 persons who vote to close, I think it was a good question and I also think the code worth being kept.

It took me about 5 hours to do it and debug it. It seems to work fine. To save 5 hours... that's why I asked the question. To save time.

/// <summary>
/// Insert item in list in log(n). The list should already be sorted.
/// Item will be inserted and there will be duplicate if comparer return 0.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <param name="item"></param>
/// <param name="comparer"></param>
/// <returns></returns>
public static int InsertInSortedList<T>(this IList<T> list, T item, Func<T, T, int> comparer = null)
{
    if (comparer == null)
    {
        comparer = Comparer<T>.Default.Compare;
    }

    int first = 0;
    int last = list.Count;
    int middle = 0;
    int compareResult = 0;

    if (last > 0)
    {
        while (true)
        {
            middle = first + ((last - first) / 2);

            compareResult = comparer(item, list[middle]);

            if (compareResult > 0)
            {
                first = middle + 1;

                if (first >= last)
                {
                    middle++;
                    break;
                } 

                continue;
            }

            if (compareResult < 0)
            {
                last = middle;

                if (first == last)
                {
                    break;
                }

                continue;
            }

            break;
        }
    }

    if (middle == list.Count)
    {
        list.Add(item);
    }
    else
    {
        list.Insert(middle, item);
    }

    return middle;

}


来源:https://stackoverflow.com/questions/61893643/is-there-a-way-to-generically-quickly-insert-logn-an-item-into-any-ilistt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!