2009-03-07 20 views
18

¿Cuál es la forma más eficiente de eliminar elementos alternativos (indexados impares o incluso indexados) en un List<T> sin usar una variable de lista de marcadores de posición?Eliminando elementos alternativos en una lista <T>

También se agradecería si pudiera mencionar el costo con cada una de sus respuestas.

Busco un eficiente manera de hacer esto

Gracias de antemano

+0

¿Qué quiere decir con alternar exactamente? –

+0

¿Quiere eliminar todos los demás elementos, es decir, todos los elementos con índice par o todos los índices impares? –

+0

sí, incluso elementos indexados o impares. – abhilash

Respuesta

25

Si llama RemoveAt para cada elemento que eliminar, va a mover una gran cantidad de datos. El más eficiente es para mover los elementos juntos que desea mantener, a continuación, eliminar los elementos no utilizados al final:

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

Editar:
Este método procesará una lista de un millón de enteros en 15 ms. Usando RemoveAt se tardará más de tres minutos ...

Edit2:
En realidad podría comenzar con pos = 1 e i = 2 (o 3), como el primer elemento no tiene que ser copiado a sí mismo. Sin embargo, esto hace que el código sea menos obvio.

+1

Creo que esto solo funcionará si la cantidad de elementos en la lista es pareja. – tvanfosson

+0

Creo que solo necesito comenzar en 1 no 0, entonces este código funcionará – AnthonyWJones

+0

@tvanfosson: No, he verificado que el código funciona tanto para números pares como impares. @AnthonyWJones: si desea eliminar los índices pares en lugar de los impares, comenzará en 1 en lugar de 0. – Guffa

2

No estoy seguro de lo que entendemos por alternativo, pero si te refieres a "todos los demás elementos" el siguiente código trabajará. Se iniciará mediante la eliminación del segundo elemento, a continuación, el cuarto, y así sucesivamente

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

Cuando elimina un elemento al principio, el resto se desplazará hacia abajo uno. Esto ciertamente no eliminará los elementos alternativos y puede dar como resultado una excepción dependiendo de si la protección se vuelve a calcular en cada paso, es decir, list.Count cambiará. – tvanfosson

+0

@tvanfonsson: ¿El código se ve bien para mí? – AnthonyWJones

+0

@Anthony: cuando quita el elemento en la posición 2, el elemento en la posición 3 cambia a 2, 4 cambia a 3, etc., por lo que el siguiente elemento que se quitó estaba en la posición 5 en la lista original, no en la posición 4. – tvanfosson

7

Sólo para la consideración de una solución que crea una nueva lista, con una lista edad usted puede hacer esto:

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

o, evidentemente,

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

según la cual la alternancia que elija.

EDITAR

La respuesta es un poco más rápido. Si lees algo más aquí, es porque medí un fin de semana y el cerebro del fin de semana es divertido. :( La solución de cierre es aproximadamente un 40% más rápida mientras que la respuesta es aproximadamente 2 órdenes de magnitud más rápida. Supongo que realmente dependerá de qué tan grande sea su lista!

+0

Creo que pones un código F # allí :) – JaredPar

+0

No estoy seguro si dices eso irónicamente? Definitivamente es código C#, lo probé antes de publicarlo aquí. Francamente, estoy un poco sorprendido de que esto no obtenga ninguna reputación, considerando que es la respuesta más simple de todas ... – flq

+0

Esto no responde a ninguno de los requisitos de OP. Él quiere eliminar elementos de la lista, que este código no hace. Él quiere rendimiento, que este código tampoco proporciona. –

5

Y otra opción, similar a la de Frank, pero con el uso de cierres. Y es más rápido que la versión de Frank.

bool isEven = true;    
var newList = list.Where(x => isEven = !isEven).ToList(); 
1
for (int i=myList.length-1; i >= 0; i--) 
    if (i % 2 == 0) 
    myList.Remove(myList[i]); 
0

me gustaría utilizar el patrón estándar que se utiliza generalmente para contenedores STL. hacer un quite seguido de un proceso de borrado.

de esta manera usted no lo hará Confundir a las personas que están acostumbradas a ver este patrón.

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()); 

} 
1

Obviamente el uso dependiente, pero que podría tener un IList envoltorio que multiplica el índice que le dan por 2, y los informes de la longitud de la lista para ser 1/2 (detalles elididas). Este es O (1).

+0

¡listo! Pero frágil –

2

El camino a Nirvana está pavimentado con ejecución diferida. O algo.

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);     
     } 
    } 

Esto funciona para todas las secuencias, no solo para IList<>. El costo de la iteración se difiere hasta la iteración, lo que puede ser una gran ganancia si, al final, no es necesario que toque todos los elementos de la lista.

En mis simples pruebas, el rendimiento cuando iteras en toda la lista no es muy bueno, así que asegúrate de crear un perfil de tu situación real.

Cuestiones relacionadas