When software is developed, it is designed to take advantage of other components that are already available. We can use an analogy to build a house - if you own a piece of land and want to build a house, would you first build the factories that produce bricks, windows, doors, glasses, carpets, etc. or would you purchase them from the market? This is the same in building software. Suppose that you are writing a download manager. Rather than writing the download code totally from scratch, you can use the graphical components, dialogs, buttons that have been written in advance and focus yourself on the downloading process, just as you would focus on your house's design rather than producing bricks.
Software works the same way. Developers do not need to reinvent the wheel every time they write a program. For example, what if program A depends on components X, Y, Z to run while component X depends on components U and V, component Y depends on components S and T to run, and component U depends on component T to run? The following diagram shows how the components would depend on one another.
To install program A properly, you have to install T first, then U and V, then S, then X, Y and Z and finally A. This is the only way that each component's dependencies would be satisfied. Unless this is the case, the program will produce an error and will not be installed correctly. This is what causes "dependency hell". Don't think dependency hell is a Linux-only concept: it is called “Dependency Hell" in the Linux/UNIX world, “DLL Hell" in the Windows world and “Jar Hell" in the Java World.