Подтвердить что ты не робот

Удаление альтернативных элементов в списке <T>

Каков наиболее эффективный способ удаления альтернативных (нечетных индексированных или даже индексированных) элементов в List<T> без использования переменной списка владельцев места?

Также было бы полезно, если бы вы могли указать стоимость с каждым вашим ответом.

Я ищу способ эффективный, чтобы сделать это

Заранее спасибо

4b9b3361

Ответ 1

Если вы вызываете RemoveAt для каждого объекта, который вы удаляете, вы будете перемещать много данных. Наиболее эффективным является перемещение элементов вместе, которые вы хотите сохранить, а затем удалить неиспользуемые элементы в конце:

int pos = 0;
for (int i = 0; i < values.Count; i += 2, pos++) {
    values[pos] = values[i];
}
values.RemoveRange(pos, values.Count - pos);

Edit:
Этот метод обработает список из миллиона ints за 15 мс. Используя RemoveAt, это займет три минуты...

Edit2:
Фактически вы можете начать с pos = 1 и я = 2 (или 3), так как первый элемент не нужно копировать самому себе. Это делает код немного менее очевидным, хотя.

Ответ 2

Только для рассмотрения решения, которое создает новый список, со старым списком вы можете сделать это:

var newList = old.Where((_, i) => i%2 != 0).ToList();

или, очевидно,

var newList = l.Where((_, i) => i%2 == 0).ToList();

в зависимости от выбранного вами варианта.

ИЗМЕНИТЬ

Ответ довольно быстрый. Если вы читаете что-то еще здесь, это потому, что я измерял на выходных и в выходные дни мозг смешно.:( Решение о закрытии примерно на 40% быстрее, чем ответ. На 2 порядка быстрее. Я полагаю, что это будет действительно зависеть от того, насколько велик ваш список!

Ответ 3

И еще один вариант, похожий на Фрэнка, но с использованием закрытий. И это быстрее, чем версия Фрэнка.

bool isEven = true;            
var newList = list.Where(x => isEven = !isEven).ToList();

Ответ 4

Я не уверен, что вы подразумеваете под альтернативным, но если вы имеете в виду "каждый другой элемент", следующий код будет работать. Он начнется с удаления 2-го элемента, затем 4-го и т.д.

List<T> list = GetTheList();
int i = 1;
while ( i < list.Count ) {
  list.RemoveAt(i);
  i++;
}

Ответ 5

Путь к Нирване вымощен отсроченным исполнением. Или что-то.

    public static IEnumerable<T> AlternateItems<T>(this IEnumerable<T> source)
    {
        while (source.Any())
        {
            yield return source.First();

            source = source.Skip(1);

            if (source.Any()) source = source.Skip(1);                
        }
    }

Это работает для всех последовательностей, а не только IList<>. Стоимость итерации откладывается до итерации, что может быть большой победой, если в конце вам не нужно касаться всех элементов в списке.

В моих простых тестах производительность, когда вы перебираете весь список, не очень хороша, поэтому не забудьте проанализировать свою реальную ситуацию.

Ответ 6

for (int i=myList.length-1; i >= 0; i--)
  if (i % 2 == 0)
    myList.Remove(myList[i]);

Ответ 7

Очевидно, что это зависит от использования, но вы можете иметь обертку IList, которая умножает индекс, который вы даете ему на 2, и сообщает, что длина списка равна 1/2 (подробности указаны). Это O (1).

Ответ 8

Я бы использовал стандартный шаблон, обычно используемый для контейнеров STL. Сделайте удаление, за которым следует удаление.

Таким образом, вы не будете путать людей, которые привыкли видеть этот шаблон.

template<typename T>
struct RemoveEven
{
    RemoveEven():count(0)   {}
    bool operator()(T const&)
    {
        bool    result  =  count%2 == 0;
        count++;
        return result;
    }
    private:
        std::size_t count;
};
int main()
{
    std::list<int>  a;
    a.erase(std::remove_if(a.begin(),a.end(),RemoveEven<int>()),a.end());

}