This studying guide is a compilation of the best techniques I've found for studying technical courses but it could not have been created without the efforts of Josh Hug and Andrew Huang.

Optimizing course resources

Here's an ideal learning workflow.

  1. Before attending lecture, study the lecture slides and code, following along with the animations. Pay special attention to the precise terminology, for example, the differences between "operand", "argument", and "parameter". More importantly, when presented with an example, try to predict each next step that will happen before moving on. Then, once you've settled on your answer, verify that the answer is correct. If the answer isn't correct, try to learn why it was not correct. Was there a misunderstanding of the definitions? Was there a misconception in how you applied the definition? Basically, our goal is to identify our weak spots and places where we need to spend more time with a concept.
  2. Consult the readings, watch past webcasted lectures, try things out in the interpreter, or ask your friends to help answer the questions and misconceptions you identified.
  3. In lecture, do not copy down the slides. Instead, spend the lecture focusing on the points that were confusing and pay close attention to the details that matter to you. Try to answer the questions you came up with since there are usually a couple more details presented in live lecture. This is also a good opportunity to ask your questions directly since it's likely that many other students have the very same question.
  4. Participate in discussion, lab, and group mentoring. These are all opportunities for a staff member to address your questions, oftentimes without you even needing to ask it! Focus on coming up with generalizable strategies to solving problems. What are the steps that are needed to solve a problem like this from scratch? How does your section leader break things down? From which direction do they start approaching the problem? The goal is to further develop and hone your intuition about these problems.
  5. If it still doesn't make sense, attend office hours and one-on-one tutoring.

Since we're all busy people, here's a more realistic schedule combining steps 1, 2, and 3 from the ideal workflow. We won't have time to prepare before lecture, so instead, try to anticipate each next step during lecture. Because we're busy thinking, we don't spend time taking detailed notes. Instead, record questions or misconceptions that you came up with. Compare your thought process with the walkthroughs presented by the instructor. Where do you diverge? Take note of that and, if it's still confusing, remember to address it later. Then, participate in all other course activities following the same steps above.

Tackling code-writing questions

  1. Read the question. Read it again. Really read it again. Identify the domain and range and remember it for later. It can even help to physically write it out: "The input is a number and a string and the output is a number."
  2. Study the doctests carefully. Figure out what the exam is trying to tell you about the problem: big hints are always given away in the doctest. The doctests often expose a detail in the structure of how the problem is meant to be solved.
  3. Come up with better examples. Even though they provide many hints, the doctests are not exhaustive and they usually don't show the most important cases. Try to come up with example cases that cover the following situations at least:

    • What's the smallest or simplest possible input I could give to this function?
    • Is there a similar small input that is invalid for this problem? How is it related to or different from the earlier case?
    • Can we come up with any larger inputs to the program that are related to or rely on smaller cases? The idea is to come up with some of the subproblems we might have to solve with recursion or other techniques.
  4. Spend some time solving some of these simple cases by manipulating each specific input without worrying about trying to find the perfect final answer. The answer you get might be "hard-coded", or written without referencing names in the problem. That's fine, we just want to get a sense of what tools we'll need to solve this problem and get our mind actively engaged with the problem.
  5. Come up with a general idea for solving the problem using the skills you've learned. Does this problem look similar to something you've seen before? (Hopefully, the answer is, "Yes.") Armed with your experience from homework, lab, and discussion plus the analysis you completed earlier, develop a general idea of how to solve the problem. Apply the problem solving techniques you've learned: with recursion, for example, it helps to try to follow the steps of finding a base case, identifying the recursive calls, and then combining the results.
  6. Write down a rough draft of your code. It might not even be close to working but, after having spent some time thinking about this incomplete solution, you'll have learned a great deal more about the problems and the limitations of your approach. Make any adjustments as necessary and walk through your program on various inputs. Do I seem to be on the right track? Or should I reconsider my strategy?
  7. Debug until your code is correct! The code won't be right on your first try, but that's entirely normal. To improve our code, we just need to ask ourselves the right questions. What input would break your program? Think like Python: break down each expression and scrutinize it exactly the way Python executes your function. We have examples of what the output should look like, so make sure the actual result matches your expectations. If it doesn't match, where is it going wrong? Fix it, and repeat this step until the program is bug-free.

Notice that we spent a lot of time just on analyzing the problem and getting a feel for the mechanics and the tools we'll need before even looking at the skeleton code.

This isn't a one-size-fits-all strategy but it's something worth trying if the approach isn't immediately clear.

Exam studying guide

Courtesy of Josh Hug, with slight modifications for CS 61A.

Studying for an exam is about gaining a level of familiarity with the material such that you can solve interesting problems that aren't just repetitions of things you've already seen. Some general tips:

  1. Start early and spread things out over time. Sleep does some sort of magic where things sink in better. More shorter study days is way preferably to a few short ones. Indeed, one of the reasons we have homework with deadlines is to exploit this fact.
  2. Be as active in your studying as possible. Working through problems and developing cheat sheets are "active". Rewatching lectures and rereading something you've read is not.
  3. The often missing component: Reflect on your own problem solving process. This is much easier through discussion with others, particularly other students (see suggested workflow below).

For those who feel behind

If there are some lectures that you feel very shaky on, watch the video and/or do the reading ASAP. Then, immediately afterwards, try to work through some of the problems on the mentoring and discussion handouts. Each handout is designed to ramp up in difficulty the further you get through the worksheet. Try to answer at least the first few questions in each section. As you work, add the seemingly most important items to your cheat sheet.

Once you feel up to date

Once you know the basics, doing the readings, reading the lectures, and rewatching the lectures is unlikely to do much good (in my opinion).

A better approach is to work through problems from old exams, as well as problems from mentoring and discussion handouts.

An even better approach is to follow up your attempt to solve a problem with careful reflection on your solution process. This is much easier to do with other people around.

My suggested approach for studying for the exam is as follows:

  1. Form a group of 2-5 people.
  2. Meet regularly, agreeing on a set of problems that everyone should attempt before each meeting.
  3. At each meeting, have each person lead the group through their own solution to one of the problems. Everyone else should interject with their thoughts. It can be rather enlightening to see what shortcuts or inefficiencies you'll uncover. Repeat until you've covered all problems and everyone has had a chance to talk.

I did this for my obscenely difficult graduate probability and statistics class in grad school, and I really wished at the time that I'd discovered this process sooner. Your mileage may vary.

Ideally, this group would meet throughout the semester, and not just in the days leading up to an exam.