¿Qué estructura de datos debo usar para completar esta tarea?

Las listas enlazadas son terribles y no deben usarse excepto en casos muy especiales. Usted toma una gran penalización de rendimiento porque los nodos generalmente no se almacenan consecutivamente en la memoria, lo que destruye el rendimiento de su caché. En teoría, la inserción y eliminación son rápidas, pero en la práctica generalmente tiene que buscar la ubicación donde ocurrirá la inserción o eliminación, ¡y tal búsqueda lleva tiempo lineal! Entonces, a menos que tenga una situación especial (por ejemplo, puede conocer la ubicación de inserción / eliminación sin buscarla), las listas vinculadas son una muy mala opción.

¿Sabes qué más tiene tiempo de inserción y eliminación lineal? Vectores Sin embargo, a diferencia de una lista vinculada, tienen un excelente rendimiento de caché, lo que significa que en la práctica sus inserciones y eliminaciones son generalmente más rápidas. Además, puedes acceder a ellos al azar. Son fáciles de ordenar, y cuando se ordenan, puede usar la búsqueda binaria para buscar elementos. La mayoría de las veces, el código escrito con una lista vinculada funcionaría mejor y sería más fácil de escribir si utilizara un vector.

Una vez que el conjunto de datos es lo suficientemente grande como para la inserción y eliminación lineal para matar el rendimiento, debe pasar a un árbol binario equilibrado. Son difíciles de escribir, y la implementación adecuada de una puede llevar una semana o más, especialmente si no lo ha hecho antes. Tienen excelentes características de rendimiento, aunque no tan buenas como un vector para búsquedas o recorridos (debido tanto a la complejidad como al rendimiento de la memoria caché).

Si desea búsquedas rápidas utilizando múltiples criterios, debe considerar mantener los “datos” en un lugar y un “índice” en una estructura de datos diferente. Cada elemento en el índice se refiere a una entrada en la estructura de datos principal (con un puntero, referencia, iterador o índice, dependiendo de cómo almacene los datos principales; si es un vector C ++, los índices son el único método seguro). Luego puede ordenar cada índice de una manera diferente; por ejemplo, uno de ellos podría ordenarse por el nombre de sus datos de host, otro podría clasificarse por edad, otro por dirección, etc. Esto le permite obtener tiempos de búsqueda de inicio de sesión para múltiples tipos de búsqueda .

Tenga en cuenta que el uso de un índice también significa que su almacenamiento de datos centrales no necesita ser ordenado en absoluto, lo que hace que sea más fácil almacenar sus datos principales en un vector. Puede que tenga que ser creativo para permitir eliminaciones rápidas de este vector; considere cambiar el elemento para eliminarlo con la entrada final y luego abrir la parte posterior; si mantener la posición igual es importante (para preservar la validez del índice), considere agregar una lista de elementos “eliminados” que estén disponibles para su reutilización, y permita que el vector tenga “agujeros” en él.

Sobre el tema del vector frente al árbol binario, es bastante obvio que el vector ganará en pequeñas cantidades de datos (porque es muy simple y el rendimiento de la memoria caché tan bueno), pero el árbol ganará si tiene grandes cantidades de datos. Mi experiencia con C ++ es que el punto de equilibrio es de aproximadamente 5 kilobytes; más que eso, y deberías usar un árbol. Menos, y el vector normalmente será más rápido . Si el número de estudiantes en el índice será de alrededor de 1300 o menos, y si necesita 4 bytes en cada entrada de índice para referirse nuevamente al registro de datos principal, entonces mantener el índice como un vector superará a un árbol binario . Si tiene significativamente más estudiantes que eso, es probable que deba cambiar a un árbol para obtener el máximo rendimiento.

Primero veamos tu problema
Tú quieres

1. Mantenga una colección de registros de Alumno, con cada registro de Alumno que contenga datos como Id, Nombre, Apellido, Clase, etc. (Llamemos a cada dato como un atributo del registro del Alumno)
2. Agregue o elimine el registro del estudiante en la colección existente, también modifique cualquier registro del estudiante existente
3. Capaz de ordenar (o mostrar en orden) los registros en función de cualquier atributo del registro del estudiante
4. Capaz de buscar en la colección un registro de estudiante específico (supongo que sobre la base de cualquier atributo)

Necesitas
1. Implemente el registro individual del estudiante como estructuras simples o clase
2. Para cada uno de los atributos, en los que se requiere la clasificación / búsqueda, debe mantener un árbol binario equilibrado (BBT) o Hash-Table (HT)
3. La clave en su BBT / HT será su valor de atributo, los datos satelitales serán un puntero a su registro. Por ejemplo, un BBT / HT contendrá solo ID, con punteros a los registros, otro BBT / HT contendrá solo LastNames, con punteros a los registros.
4. Al agregar / eliminar registros, deberá agregar / eliminar entradas en todos los BBT / HT
5. Al editar un valor de atributo, deberá eliminar y volver a agregar el elemento en el BBT / HT del atributo editado

Si está utilizando C ++ o Java, las estructuras de datos de la biblioteca estándar como Map, TreeMap, HashMap ya están disponibles y pueden usarse para BBT / HT (aunque dudo que a su profesor le guste). Si está utilizando C, tendrá que construirlo desde cero (no es una tarea fácil)

PD: Creo que esto es aproximadamente cómo se implementan realmente las cosas en DBMS
PPS: Creo que sus profesores tienen su cerebro / corazón en el lugar correcto.

Lo que está buscando es una estructura de datos de árbol de búsqueda binaria.
Un árbol de búsqueda binario básico proporciona operaciones de inserción, búsqueda y eliminación, cada una con una complejidad de O (logN). Sin embargo, en el peor de los casos, podría ir a O (N) y, por lo tanto, debe preferir un árbol Negro rojo o un árbol AVL que garantice un rendimiento de O (logN) en el peor de los casos.
Si está utilizando C ++, entonces tiene un mapa listo y establece las clases STL, que harán su trabajo.

Otra estructura de datos que puede usar es una tabla hash (C ++ unordered_map), que realiza las tareas anteriores en un tiempo promedio constante, sin embargo, en el peor de los casos, puede funcionar mal (O (N)).
Para la mayoría de las tareas “no tan pesadas”, un árbol negro rojo funciona bien porque logN es un número bastante pequeño (considere tomar un registro de la población de la tierra).

Puede considerar bst como una opción que admite la eliminación de inserción y la búsqueda en tiempo O (log), la mayor parte del hash también se realiza a través de bst, como el mapa en C ++ se implementa a través de bst internamente, por lo que puede ser una buena opción

Sugeriría algo muy parecido a lo que Shubham Garg ha sugerido. Busque hash a través de árboles AVL si utiliza con frecuencia la búsqueda o hash a través de árboles Rojo-Negros si agrega o elimina registros con frecuencia. Se sabe que los árboles de búsqueda binarios se inclinan hacia un lado, lo que los hace tan buenos como una lista. Los árboles AVL o los árboles rojo-negros funcionarían mejor que BST.

Haga todos los registros de los estudiantes, luego, para cada campo, inserte a todos los estudiantes en un árbol rojo negro separado. Esto es extremadamente simple de hacer una vez que haya implementado el árbol.

Si no necesita ordenar, puede usar un hashmap, pero no hay una desventaja real de poner todo en un árbol.

Use Hashmap donde id es clave y el valor es Student object / struct. Y cree un árbol de índice para cada propiedad del alumno que desee usar para filtrar, ordenar. Los nodos de estos árboles solo tienen ID, puede obtener ID de estos y obtener otras propiedades utilizando ID de Hashmap con complejidad O (1).