La respuesta a esta pregunta depende exactamente de lo que quieras aprender.
Python y Ruby
A menudo se sugieren lenguajes de alto nivel como Python y Ruby porque son de alto nivel y la sintaxis es bastante legible. Sin embargo, todos estos lenguajes tienen abstracciones para las estructuras de datos comunes. No hay nada que le impida implementar sus propias versiones como ejercicio de aprendizaje, pero es posible que descubra que está construyendo estructuras de datos de alto nivel sobre otras estructuras de datos de alto nivel, lo que no es necesariamente útil.
Además, Ruby y Python son lenguajes de tipado dinámico. Esto puede ser bueno, pero también puede ser confuso para el principiante y puede ser más difícil (inicialmente) detectar errores, ya que normalmente no serán evidentes hasta el tiempo de ejecución.
C
C está en el otro extremo. Es bueno si desea aprender detalles de muy bajo nivel, como cómo se administra la memoria, pero la administración de la memoria de repente es una consideración importante, como en el uso correcto de malloc () / free (). Eso puede distraer. Además, C no está orientado a objetos. Eso no es malo, pero simplemente vale la pena señalarlo.
C ++
Se ha mencionado C ++. Como dije en el comentario, creo que esta es una elección terrible . C ++ es horriblemente complicado incluso en un uso simple y tiene una cantidad ridícula de "trampas". Además, C ++ no tiene una clase base común. Esto es importante porque las estructuras de datos como las tablas hash dependen de que haya una clase base común. Podría implementar una versión para una clase base nominal, pero es un poco menos útil.
Java
También se ha mencionado Java. A muchas personas les gusta odiar Java y es cierto que el lenguaje es extremadamente detallado y carece de algunas de las características del lenguaje más modernas (por ejemplo, cierres), pero nada de eso realmente importa. Java tiene un tipo estático y tiene recolección de basura. Esto significa que el compilador de Java detectará muchos errores que los lenguajes tipados dinámicamente no (hasta el tiempo de ejecución) y no hay que lidiar con las fallas de segmentación (lo que no quiere decir que no pueda perder memoria en Java; obviamente, puede). Creo que Java es una buena elección.
C#
C # el lenguaje es como una versión más moderna de Java. Como Java, es un lenguaje compilado intermedio administrado (recolectado de basura) que se ejecuta en una máquina virtual. Todos los demás lenguajes enumerados aquí, aparte de C / C ++, también se ejecutan en una máquina virtual, pero Python, Ruby, etc. se interpretan directamente en lugar de compilarse en un código de bytes.
Básicamente, C # tiene los mismos pros y contras que Java.
Haskell (etc.)
Por último, tienes lenguajes funcionales: Haskell, OCaml, Scheme / Lisp, Clojure, F #, etc. Estos piensan en todos los problemas de una manera muy diferente y vale la pena aprenderlos en algún momento, pero nuevamente todo se reduce a lo que quieres aprender: programación funcional o estructuras de datos? Me limitaría a aprender una cosa a la vez en lugar de confundir el tema. Si aprende un lenguaje funcional en algún momento (lo que recomendaría), Haskell es una buena opción y segura.
Mi consejo
Elija Java o C #. Ambos tienen IDE excelentes y gratuitos (Eclipse, Netbeans e IntelliJ Community Edition para Java, Visual Studio Express para C #, Visual Studio community edition) que facilitan la escritura y ejecución de código. Si no usa una estructura de datos nativa más compleja que una matriz y cualquier objeto que usted mismo escriba, aprenderá básicamente lo mismo que en C / C ++ pero sin tener que administrar la memoria.
Permítanme explicar: una tabla hash extensible debe cambiar de tamaño si se agregan suficientes elementos. En cualquier implementación, eso significará hacer algo como duplicar el tamaño de la estructura de datos de respaldo (generalmente una matriz) y copiar los elementos existentes. La implementación es básicamente la misma en todos los lenguajes imperativos, pero en C / C ++ tienes que lidiar con fallas de segmentación cuando no asignas o desasignas algo correctamente.
Python o Ruby (realmente no importa cuál) sería mi próxima opción (y muy cerca de las otras dos) solo porque la escritura dinámica podría ser problemática al principio.