The answer to this question depends on what exactly you want to know.
Python and Ruby
Higher-level languages such as Python and Ruby are often offered because they are higher-level and the syntax is quite readable. However, all of these languages have abstractions for common data structures. Nothing prevents you from implementing your own versions as a training exercise, but you may find that you are building high-level data structures on top of other high-level data structures, which is not necessarily useful.
In addition, Ruby and Python are dynamically typed languages. This may be good, but it can also confuse the novice, and it can be harder (at the initial stage) to catch errors, because they usually will not be visible until runtime.
FROM
On the other end. It would be nice if you want to learn really low-level details, such as how to manage memory, but memory management suddenly becomes an important factor, for example, using malloc () / free () correctly. It can be distracting. In addition, C is not object oriented. This is not bad, but just worth noting.
C ++
C ++ has been mentioned. As I said in a comment, I think this is a terrible choice. C ++ is terribly complicated even with simple use and has a ridiculous amount of "errors". In addition, C ++ does not have a common base class. This is important because data structures, such as hash tables, rely on a common base class. You could implement a version for a nominal base class, but this is a little less useful.
Java
Java has also been mentioned. Many people love to hate Java, and it’s true that the language is extremely verbose and lacks some of the more modern language features (such as closures), but none of this matters. Java is statically typed and has a garbage collector. This means that the Java compiler will catch many errors that will not be dynamically typed languages (until runtime), and will not deal with segmentation errors (which does not mean that you cannot lose memory in Java; obviously, you can). I think Java is a good choice.
FROM#
C # language is similar to a more modern version of Java. Like Java, it is a managed (garbage collector) intermediate compiled language that runs on a virtual machine. Any other language specified here, except for C / C ++, also works in a virtual machine, but Python, Ruby, etc. Interpreted directly, not compiled into bytecode.
C # has the same pros and cons as Java, in principle.
Haskell (etc.)
Finally, you have functional languages: Haskell, OCaml, Scheme / Lisp, Clojure, F #, etc. They think about all the problems in a completely different way and at some point are worth studying, but again it all comes down to what you want to learn: functional programming or data structures? I stick to learning one thing at a time, and not confuse the problem. If at some point you learn a functional language (which I would recommend), Haskell is a safe and good choice.
My advice
Choose Java or C #. Both have excellent, free, integrated development environments (Eclipse, Netbeans, and IntelliJ Community Edition for Java, Visual Studio Express for C #, Visual Studio Community Edition) that make writing and running code easier. If you do not use your own data structure, which is more complex than an array and any object that you write yourself, you will learn basically the same thing as in C / C ++, but without the need to actually manage the memory.
Let me explain: the size of the extensible hash table needs to be changed if enough elements are added. In any implementation, this will mean doing something like doubling the size of the backup data structure (usually an array) and copying it into existing elements. The implementation is basically the same in all imperative languages, but in C / C ++ you have to deal with segmentation errors when you don't distribute or release something correctly.
Python or Ruby (it doesn't matter which) will be my next choice (and very close to the other two) just because dynamic typing can be problematic at first.