How to study for the Algorithm courses
Designing algorithms that are fast enough is probably a function of experience. There are strategies (recursion, divide-and-conquer, dynamic programming...) that can be used. Often a problem can be approached with multiple strategies but one is more efficient than the other. The more of these strategies you know, the better you are equipped for using them in your algorithms. Experience will also give you a feel for what is an appropriate approach; you may intuitively notice which problems are recursive in nature, which can be expressed as operations on a graph etc. Then you can use what you know to knead the challenge into a familiar shape. Do a Google search for "algorithm strategies" to find useful resources which list generic strategies. Learn about these and then consider for algorithms you encounter how they are instances of these generic approaches (or combinations thereof).

Edge cases tend to occur where assumptions are made, and for some situations those assumptions are incorrect. An algorithm attempts to resolve something for some structure which seems consistent; it breaks down where the structure suddenly differs or becomes inconsistent. Edge cases are the sort of place in a program where you need to test assumptions and where decisions branch.

For example, suppose I have some algorithm that takes a list of elements as input and performs a computation over it. You begin your design by stating that for each element in the list, in order, some action is performed. That's an assumption right there. "Each element in the list" assumes there are some. How will it behave for an empty list?

An example of this I encountered in some programming contest a while ago. You were given a list of numbers and had to find out how many ways there were to divide this into three lists, maintaining the original order of the numbers, in some way that satisfied some condition (the sums for the three sub-lists had to be equal). I got started but soon noticed some test inputs failed. It turned out that empty sub-lists were also valid. I had assumed that each sub-list had to have at least one element. And then, of course, I'd need to consider whether my fixed program would handle inputs of 0, 1 or 2 numbers correctly as well.