¿Por qué la eliminación suele ser mucho más difícil de implementar que la inserción en muchas estructuras de datos?

33

¿Puede pensar en alguna razón específica por la cual la eliminación es generalmente mucho más difícil de implementar que la inserción para muchas (la mayoría) de estructuras de datos?

Ejemplo rápido: listas vinculadas. La inserción es trivial, pero la eliminación tiene algunos casos especiales que la hacen significativamente más difícil. Los árboles de búsqueda binarios de equilibrio automático como AVL y Rojo-negro son ejemplos clásicos de implementación de eliminación dolorosa.

Me gustaría decir que tiene que ver con la forma en que la mayoría de la gente piensa: es más fácil para nosotros definir las cosas de manera constructiva, lo que lleva muy bien a inserciones fáciles.

Leo Brito
fuente
44
¿Qué pasa con pop, extract-min?
coredump
55
"Más difícil de implementar" es más una cuestión de psicología (cognición y fortalezas y debilidades de la mente humana) que de programación (propiedades de estructuras de datos y algoritmos).
outis
1
Como creo que aludía al coredump, las pilas deberían ser al menos tan fáciles de eliminar como agregar (para una pila respaldada por una matriz, el estallido es solo una disminución del puntero [1], mientras que empujar podría requerir una copia completa de la matriz si golpeas el tamaño máximo del formación). También hay algunos casos de uso en los que se supone que las inserciones serán frecuentes y las eliminaciones menos, pero sería una estructura de datos muy mágica donde el número de eliminaciones excede las inserciones. [1] Probablemente también deberías anular la referencia ahora invisible al objeto reventado para evitar pérdidas de memoria, lo cual recuerdo porque el libro de texto de Liskov no lo hizo
Foon
43
"Camarero, ¿podría agregar más mayonesa a este sándwich?" "Claro, no hay problema, señor". "¿Podrías también eliminar toda la mostaza?" "Uh ......"
cobaltduck
3
¿Por qué la resta es más complicada que la suma? ¿La división (o factorización prima) es más complicada que la multiplicación? ¿Raíces más complicadas que la exponenciación?
mu es demasiado corto el

Respuestas:

69

Es más que un simple estado mental; Hay razones físicas (es decir, digitales) por las que la eliminación es más difícil.

Cuando eliminas, dejas un agujero donde solía estar algo. El término técnico para la entropía resultante es "fragmentación". En una lista vinculada, esto requiere "parchear" el nodo eliminado y desasignar la memoria que está utilizando. En árboles binarios, causa un desequilibrio del árbol. En los sistemas de memoria, hace que la memoria no se use por un tiempo si los bloques recién asignados son más grandes que los bloques que quedan por eliminación.

En resumen, la inserción es más fácil porque puedes elegir dónde vas a insertar. La eliminación es más difícil porque no puede predecir de antemano qué elemento se eliminará.

Robert Harvey
fuente
3
La fragmentación no es un problema donde los punteros y la indirección entran en juego, ya sea para la estructura en memoria o en los diagramas. En la memoria, no importa dónde existan nodos individuales debido a la indirección. Para las listas, eliminar un nodo interno (que es donde tendría un agujero en el diagrama) implica un poco menos de operaciones que la inserción (1 asignación de puntero y 1 asignación libre versus 1 asignación y 2 asignaciones de puntero). Para los árboles, la inserción de un nodo puede desequilibrar un árbol tanto como la eliminación. Son los casos extremos los que causan las dificultades a las que se refiere brito, donde la fragmentación no importa.
outis
12
No estoy de acuerdo con que las inserciones y eliminaciones difieran en la previsibilidad. "Parchear" un nodo de lista es exactamente lo que sucede al revés si se va a insertar el mismo nodo. No hay incertidumbre en ninguna dirección en ningún punto, y en cualquier contenedor sin estructura intrínseca a sus elementos (por ejemplo, un árbol binario equilibrado, una matriz con una relación estricta entre las compensaciones de elementos) no hay ningún "agujero" en absoluto. Por lo tanto, me temo que no sé de qué estás hablando aquí.
sqykly
2
Muy interesante, pero diría que se pierden los argumentos. Puede organizar estructuras de datos en torno a la eliminación simple / rápida sin ningún problema. Es menos común, probablemente menos útil también.
luk32
@sqykly Creo que la lista fue un mal ejemplo porque la inserción media y la relación media son igualmente difíciles. Un caso asigna memoria donde el otro reasigna. Uno abre un agujero donde el otro sella un agujero. Entonces, no todos los casos son eliminar más complejos que agregar.
ydobonebi
36

¿Por qué tiende a ser más difícil eliminar que insertar? Las estructuras de datos están diseñadas más teniendo en cuenta la inserción que la eliminación, y con razón.

Considere esto: para eliminar algo de una estructura de datos, tiene que estar allí en primer lugar. Por lo tanto, primero debe agregarlo, lo que significa que a lo sumo tiene tantas eliminaciones como inserciones. Si optimiza una estructura de datos para la inserción, se garantiza que obtendrá al menos tanto beneficio como si se hubiera optimizado para su eliminación.

Además, ¿de qué sirve eliminar secuencialmente cada elemento? ¿Por qué no simplemente llamar a alguna función que la borra de una vez (posiblemente simplemente creando una nueva)? Además, las estructuras de datos son más útiles cuando realmente contienen algo. Entonces, el caso de tener tantas eliminaciones como inserciones no es, en la práctica, muy común.

Cuando optimizas algo, quieres optimizar las cosas que hace más y que toman más tiempo. En uso normal, la eliminación de elementos de una estructura de datos ocurre con menos frecuencia que la inserción.

Rob Watts
fuente
44
Hay un caso de uso que puedo imaginar. Una estructura de datos que está preparada para la inserción inicial y luego el consumo individual. Por supuesto, es un caso raro, y no es muy interesante algorítmicamente, porque como usted dijo, tal operación no puede dominar la inserción de manera asintótica. Tal vez haya alguna esperanza, de hecho, de que la inserción por lotes puede tener un costo amortizado bastante bueno y ser rápida y simple para la eliminación, por lo que hubiera sido una inserción por lotes complicada pero práctica y eliminaciones individuales simples y rápidas. Ciertamente, una necesidad práctica muy poco común.
luk32
1
Ummm, creo que un ejemplo podría ser un vector de orden inverso. Puede agregar un lote kde elementos bastante rápido: invertir la entrada de clasificación y fusionarla con el vector existente O(k log k + n). Entonces tiene una estructura con una inserción bastante complicada, pero consumir uelementos superiores es trivial y rápido. Solo toma el último uy mueve el final del vector. Sin embargo, si alguien alguna vez necesita algo así, estaré condenado. Espero que esto al menos fortalezca su argumento.
luk32
¿No debería querer optimizar el patrón de uso promedio en lugar de lo que hace más?
Shiv
Una simple cola de trabajo FIFO generalmente intentará estar vacía la mayor parte del tiempo. Una cola bien diseñada estará bien optimizada (es decir, O (1)) tanto para inserciones como para eliminaciones (y una muy buena también admitirá operaciones concurrentes rápidas, pero ese es un problema diferente).
Kevin
6

No es mas dificil.

Con listas doblemente vinculadas, cuando inserte, estará asignando memoria, y luego se vinculará con el nodo principal o el nodo anterior, y con el nodo de cola o el siguiente. Cuando elimine, estará desvinculando exactamente de lo mismo y luego liberando memoria. Todas estas operaciones son simétricas.

Esto supone que en ambos casos tiene el nodo para insertar / eliminar. (Y en el caso de la inserción, que también tiene el nodo para insertar antes, por lo que, en cierto modo, la inserción podría considerarse un poco más complicada). Si está tratando de eliminar no tener el nodo para eliminar, sino la carga útil del nodo, entonces, por supuesto, primero tendrá que buscar en la lista la carga útil, pero eso no es un defecto de eliminación, ¿verdad?

Con árboles balanceados, lo mismo aplica: un árbol generalmente necesita balancearse inmediatamente después de una inserción y también inmediatamente después de una eliminación. Es una buena idea intentar tener una sola rutina de equilibrio y aplicarla después de cada operación, independientemente de si fue una inserción o una eliminación. Si está intentando implementar una inserción que siempre deja el árbol equilibrado, y también una eliminación que siempre deja el árbol equilibrado, sin que ambos compartan la misma rutina de equilibrio, está complicando innecesariamente su vida.

En resumen, no hay ninguna razón por la cual uno debería ser más difícil que el otro, y si descubres que es así, es posible que seas víctima de la tendencia (muy humana) de pensar que es más natural pensar de manera constructiva que sustractiva, lo que significa que podría estar implementando la eliminación de una manera que es más complicada de lo que debería ser. Pero eso es un problema humano. Desde un punto de vista matemático, no hay problema.

Mike Nakis
fuente
1
Tengo que estar en desacuerdo. El algoritmo de eliminación de AVL es más complejo que la inserción. Para ciertas eliminaciones de nodos, es posible que deba reequilibrar todo el árbol, lo que generalmente se hace de forma recursiva pero también se puede hacer de forma no recursiva. No tiene que hacer esto para la inserción. No estoy al tanto de los avances del algoritmo en los que se puede evitar el reequilibrio de todo el árbol en todos los casos.
Dennis
@Dennis: podría ser que los árboles AVL sigan la excepción en lugar de la regla.
outis
@outis IIRC, todos los árboles de búsqueda equilibrados tienen rutinas de eliminación más complicadas (que la inserción).
Rafael
¿Qué pasa con las tablas hash hash cerradas ? La inserción es (relativamente) sencilla, la eliminación es al menos más difícil de conceptualizar ya que hay que arreglar todo "lo que se suponía que estaba en el índice X está actualmente en el índice Y y tenemos que buscarlo y volver a colocarlo" cuestiones.
Kevin
3

En términos de tiempo de ejecución, mirando el comparación de la complejidad del tiempo de las operaciones de estructura de datos en Wikipedia, tenga en cuenta que las operaciones de inserción y eliminación tienen la misma complejidad. La operación de eliminación perfilada allí es una eliminación por índice, donde tiene una referencia al elemento de estructura que se va a eliminar; La inserción es por artículo. El mayor tiempo de ejecución para la eliminación en la práctica se debe a que generalmente tiene un elemento para eliminar y no su índice, por lo que también necesita una operación de búsqueda. La mayoría de las estructuras de datos en la tabla no requieren una búsqueda adicional para una inserción porque la posición de colocación no depende del elemento, o la posición se determina implícitamente durante la inserción.

En cuanto a la complejidad cognitiva, hay una respuesta en la pregunta: casos extremos. La eliminación puede tener más de ellos que la inserción (esto aún no se ha establecido en el caso general). Sin embargo, al menos algunos de estos casos extremos se pueden evitar en ciertos diseños (por ejemplo, tener un nodo centinela en una lista vinculada).

outis
fuente
2
"La mayoría de las estructuras de datos no requieren una búsqueda para una inserción". -- ¿como? Yo haría la afirmación opuesta, de hecho. (Usted "encuentra" la posición de inserción, que es tan costosa como encontrar el mismo elemento nuevamente más tarde.)
Raphael
@Raphael: esta respuesta debe leerse en el contexto de la tabla vinculada de complejidades de operación, que no incluye la operación de búsqueda como parte de la eliminación. En respuesta a su pregunta, clasifiqué la estructura por nombre común. De las matrices, listas, árboles, tablas hash, pilas, colas, montones y conjuntos, los árboles y conjuntos requieren una búsqueda para un inserto; los otros usan un índice no conectado al elemento (para pilas, colas y montones básicos, solo se expone 1 índice y no se admite la búsqueda) o lo calculan a partir del elemento. Los gráficos pueden ir en cualquier dirección, dependiendo de cómo se usen.
outis
... Los intentos podrían considerarse árboles; sin embargo, si se clasifica como su propia estructura, si hay un "hallazgo" durante la inserción es más una cuestión de debate, por lo que no lo incluyo. Tenga en cuenta que la lista de estructura de datos no tiene en cuenta la interfaz frente a la implementación. Además, la forma en que cuenta depende en gran medida de cómo se clasifica. Veré si puedo pensar en una declaración más objetiva.
outis
Admito que tenía en mente la interfaz diccionario / set (como es común en CS). De todos modos, esa tabla es engañosa e (iirc) incluso incorrecta en varios lugares: Wikipedia, el pozo de la desinformación de CS. : /
Raphael
0

Además de todos los problemas mencionados, hay una integridad referencial de datos involucrada. Para la estructura de datos más adecuada como bases de datos en SQL, la integridad referencial de Oracle es muy importante.
Para asegurarse de no destruirlo accidentalmente, se inventaron muchas cosas diferentes. Es por eso que la verificación de integridad referencial no le permitirá eliminar registros de la tabla primaria hasta que se limpien los registros de la tabla secundaria. Y es por eso que en la mayoría de las fuentes de datos es más difícil eliminar datos.
Por ejemplo, en cascada en la eliminación, que no solo elimina lo que intente eliminar, sino que también desencadena la limpieza de los datos relacionados.
Esta base de datos de limpieza de datos basura y mantiene intacta la integridad de los datos.
Por ejemplo, tiene tablas con padres y clases como registros relacionados en la segunda tabla.
Donde padre es la mesa principal. Si no tiene una integridad referencial reforzada, puede eliminar cualquier registro en cualquier tabla y más adelante no sabrá cómo obtener información familiar completa porque tiene datos en la tabla secundaria y nada en la tabla primaria.

Alex
fuente
Creo que la pregunta era sobre estructuras en memoria como listas enlazadas, tablas hash, etc. en lugar de bases de datos, pero la integridad referencial es un problema importante incluso con estructuras en memoria.
supercat