This book is for informational purposes only. Except when an external source is cited, everything in the book is the author’s opinion. The author makes no guarantee about the correctness or accuracy of any content in this book. Furthermore, you may disagree with and/or find certain content offensive.
Read at your own risk. Do not continue reading if you do not accept full responsibility for all actions you take as a result of reading this book. The author is not liable for any damages including, but not limited to, academic failures, career path mistakes, financial loss, feeling upset, and physical/mental injury.
Sophomore year is the time to branch out and explore. If you did the proper preparation during your freshman summer and figured out what areas of computer science seem interesting, then now is the time to explore them via coursework and internships. In this chapter, we’ll lay out best practices for your sophomore year of college.
In your freshman year, you should have taken your intro to programming course and an elementary data structures and/or algorithms course. Most universities offer a followup data structures and algorithms course that you should take early in your sophomore year. Take the course covering recursion, stacks, heaps, hashing, trees, sorting, and graph algorithms, among other topics.
Master your data structures and algorithms class as soon as possible because the knowledge is key to passing technical interviews and competing for the best internships. The most frequent concepts in interviews are recursion, dynamic programming, hashmaps, priority queues, and binary search. In particular, do extra practice with recursion and dynamic programming. Most people struggle with these two concepts and fail to learn the intuition. Professors often don’t teach it well and if yours doesn’t, I will be releasing a supplement on recursion and dynamic programming in the future.
For maximum efficiency, take your introductory computer architecture course in parallel with data structures and algorithms. Computer architecture is one of a few courses that don’t have data structures and algorithms as a prerequisite, so get that out of the way. Completing the computer architecture course also unlocks operating systems, which is another crucial course you should take before graduating.
Everyone has to take data structures and algorithms and computer architecture. These standard courses emphasize problem solving and engineering. If you followed the advice in chapter 15 and brushed up on these concepts before college, then you’re off to a great start. If not, your sophomore year courses will force you to pick up these skills.
For some people, problem solving comes naturally. If you fall into this category, then you’re set up for success. Other people struggle with problem solving. If you fall into this second category, then put in some extra work.
Problem solving applies to the exams where your only resource is your brain. Your ultimate goal is to understand the intuition behind the concepts in lecture. Can you explain to a freshman why a hashmap is important? Why are linear probing and separate chaining both useful and why do some hashmaps use one method over the other? Can you be the professor and teach the concepts yourself? Take the time to understand the intuition and you’ll hit every curveball on the exam.
Computer science material doesn’t come naturally for everyone and if you can’t dedicate the time and effort to learn the material well enough to be the teacher yourself, then you fall back to memorization and repetition. Instead of understanding why a hashmap is important, memorize the concepts: O(1) insertion, O(1) lookup if you have the key, O(1) deletion, the underlying data structure is typically an array, and so on. Solve tons of problems on Leetcode so that you can identify patterns between problems that hint at what data structure or algorithm to use. Before an exam, go through all the lecture slides and make flashcards to quiz yourself. You might fail some curveball problems involving the design and fundamentals, but you’ll hit the majority of questions simply asking you to apply a concept.
If memorization and repetition don’t work for you either, then you are not going to do well in the class, but you can still pass with around a C. Your sophomore year classes are not intended to fail the majority of students, and there will always be easy problems on the exams. At the very least, don’t make silly mistakes on easy problems. Furthermore, make sure you get as much credit on all the other components of your grade, such as homework, attendance, and programming projects.
While exams are passable with minimal effort, programming projects require a fair amount of dedication. Most programming projects involve an autograder that automatically runs your code against a large suite of tests and gives you credit for the tests that pass. Due to the scale of your code, these projects end up being less about problem solving and more about engineering and dedication to debugging your code.
First, if you don’t know what techniques to use for each part of the project, just chat with your classmates. Never share code because autograders compare your submission against other students’ code and flag you if there is too much similarity. Only discuss at a high level so that you understand the project requirements. It usually takes only a few hours to understand what the project is asking you to code.
Understanding the project and finding a high level solution is easy; the hard part is engineering your code to work. Always start early, preferably on the day the project is released. Follow decent coding standards: give your variables descriptive names, use functions and classes where appropriate, and write tests for your code. Finally, back up your code frequently with source control software such as git. That way, if you accidentally delete everything or mess up horribly, you can revert back to an older stable state.
Your development process should take the project in small increments. Identify a small, independent feature that you want to implement. Write the code for that feature. Next, write some tests for it. Once your tests are passing, submit to the autograder and see if you passed all the tests for that feature on the autograder. If the autograder reports incorrect output, then review your code and write more tests until you expose a bug. Use print statements to help you trace through your code, locate the bug and eradicate it. Once you have all the tests for your specific feature passing on correctness, make sure you backup your code in source control.
If the autograder flags your code as too slow or using too much memory, make the optimizations after you achieve the correct output. Optimizations usually just involve fixing silly mistakes. Are you unnecessarily copying data? Are you iterating through a two-dimensional array properly and taking advantage of cache locality? Did you remove your print statements, which can greatly slow down your program? Don’t be afraid to seek help from a teaching assistant if you’re having trouble optimizing your code. Finally, in the worst case, given that you’ve used the proper data structures and algorithms, you should only be docked a few points.
Once you receive full points for a particular feature or if you give up trying to figure out how to optimize something, you can move on to the next feature. When you move on to subsequent features, ensure that you never start failing a test you were passing earlier. If you do, that means the code you wrote since you last submitted to the autograder introduced a new bug. Review all the code you’ve written since and fix the bug before it gets lost among your giant codebase.
Repeat this process until you have all the features implemented. It usually takes weeks to finish a project. The key is to take small steps: start submitting to the autograder early and often. Stay dedicated and follow this process closely for a streamlined experience.
While you must do well in your freshman year computer science courses because those concepts are required for any full-time software job, it’s okay if you do poorly in data structures and algorithms or computer architecture. Computer architecture only applies to niche software jobs related to computer hardware so you’ll be fine if you avoid those roles. Data structures and algorithms are relevant to technical interviews and backend software development, so you can still work in a frontend role and/or get lucky and slip by in the interviews. Getting a B is solid, so don’t feel the need to stress yourself out and get all the remaining points on projects and exams. That said, getting an A in data structures and algorithms is best because it is correlated with doing well on technical interviews. Make sure you don’t hover around a C because if you slip lower, you risk failing and having to repeat a course.
Once you complete your data structures and algorithms and computer architecture classes, you unlock advanced electives. In your freshman summer, you should have researched software roles and companies that you found interesting. Find courses that prepare you for roles at those companies. For example, if there’s a full-stack developer role that you want at a particular tech firm, then then take a web development course. If you want to pursue a data science role at a particular startup, then take a data science course.
For every remaining semester, select two or three appropriate computer science electives to take. If necessary, revise the four-year course plan that you created your freshman year to account for any changes in course selection. Confirm that your revised course plan still meets the graduation requirements.
If you’re still undecided, here are popular courses you could try out:
Go into your courses with an open mind expecting to enjoy the material. If you discover you truly don’t enjoy something, you still have time to revise your plans for junior and senior year.
Most departments also have a difficulty ranking for courses. Make sure you don’t take more than two difficult electives in a single semester to avoid overloading yourself. On the other hand, although you may be tempted to take easier electives and coast, it’s better to get a B or C in an elective aligned with your career and job interests than to get an A in something you don’t care about. Your GPA has little impact on your software job prospects and if you’re paying for college, you might as well take the courses that interest you even if your grade ends up being lower. If you don’t care about what work you do after college so long as the pay is decent, then feel free to just coast on easy electives. Choosing electives is open-ended, so feel free to decide as you see fit.
In the first half of your sophomore year, focus on two standard requirements in the computer science major: data structures and algorithms, and computer architecture. Try your best, particularly in data structures and algorithms because that course applies to internship technical interviews. In the second half of your sophomore year and every semester thereafter, take two or three advanced electives pertinent to job roles you’re interested in. Use these electives to discover what you like and don’t like so that you can make informed decisions in your future job search.
Your sophomore summer internship is the prime opportunity to try out a job role and add to your resume. Getting a sophomore summer internship is essential for building up to your junior and senior year. Many of the same concepts from your freshman internship search still apply, but in this section, we’ll go further and discuss how to choose companies, apply to them effectively, and navigate their technical interviews.
While your freshman year internship search involved desperately applying to any company that would consider hiring you, you should be more selective about your sophomore year internship choices. Hopefully you did your freshman summer homework and came up with a list of companies with job roles that interest you. Ensure you have at least fifty potential companies. Review your list and apply to internships at those companies.
You should always apply to the large companies: Apple, Amazon, Facebook, Google, Microsoft, and IBM to list a few. They hire liberally and have all types of roles from web development, to mobile apps, to backend, to data science. They also let you express your preferences on the type of internship work you’d like to do and try their best to match you with an appropriate team. Large companies are an excellent place to explore as a sophomore, though competition for their roles is tough.
You should also apply to smaller, local companies, in the same municipality or state as your college. Smaller companies tend to recruit locally and local firms have precedent for hiring from your school. You’re more likely to get interview opportunities and offers from local connections than at other places. Check out if they have roles that interest you and apply. Applying to local companies is an efficient use of your time because you are more likely to succeed.
Once you’ve created your list, it’s time to use your networking skills and get those applications submitted. Review the networking techniques from chapter 17. Get your applications to large tech firms out as soon as the internship positions are posted because the spots fill quickly on a rolling basis. Find friends who have interned at large tech firms in the past and ask them if they can refer you or connect you with their recruiter. Or, try to get in touch with a recruiter on LinkedIn. Career fairs and online job portals are not a great way to connect with large tech companies because a million other students do that and you won’t stand out. With large tech firms, your first big hurdle is getting noticed.
For less-known, small, local companies, you don’t have to rush but it is better to apply early rather than late. As with large tech firms, getting a personal connection to the internship recruiter is the most effective way of getting noticed for an interview. Alternatively, you can connect with the recruiter at career fairs because companies that aren’t well-known tend to get minimal traffic and you easily stand out in the smaller crowd. Applying online also works because they don’t get swarmed with as many applicants, but only use that as a last resort. With smaller and less-known companies, it’s much easier to get noticed.
The second hurdle after getting noticed is passing technical interviews. Technical interviews make or break your internship offer. Most technical interviews follow a standard format: you speak with an engineer at the company you’re applying to and they ask you to write code that solves a self-contained problem. You have to write the correct code and explain your solution to pass. If you do well on all your technical interviews at a company, you will get an internship offer; conversely, if you don’t do well enough, you’ll be rejected.
The key to technical interviews is to learn your data structures and algorithms content well and to practice solving technical problems. Give your undivided attention in your data structures and algorithms class and aim for an A in the course. Being book-smart and doing well in class isn’t enough though; you also need to be capable of applying the concepts to problems. Just like how arithmetic is second-nature to you because you’ve done it so much, once you do enough technical problems, they become second-nature to you as well.
Leetcode is a popular site for practicing for technical interviews. Most technical interview questions are equivalent to Leetcode questions. If you master solving Leetcode problems, then you’ll easily answer technical interview questions.
Commit to spending one hour every evening on solving a Leetcode problem. If you can’t get your solution accepted within an hour, that’s okay. You can try again the next evening. If you still can’t solve it after two evenings, open the discussion tab, read someone else’s solution and spend time understanding it. Then write the code yourself without referencing the solution.
Next, I’ll discuss five of the most important concepts that appear in technical interviews. In class, pay extra attention to these topics. I’ve also listed a couple leetcode problems in each category and they are ordered from most frequent to least frequent.
Pretty much every interview question involves arrays or lists because that’s how the input will be provided. A lot of interview questions fall into the “ad hoc” algorithm category. Ad hoc problems don’t require any special data structures and algorithms knowledge and you simply have to look for a clever way of processing the input. Examples:
Stacks and queues are the next level above arrays and lists. Stacks and queues are a special way of organizing lists of data. When arrays or lists don’t seem to help, stacks and queues lead to clever ways of simplifying an otherwise complex problem. Examples:
Hashing lets you store keys and look them up in constant time. A hashset only tracks keys, while hashmaps also track a value that you can look up quickly given its key. Engineers use hashmaps a lot in industry and so this data structure comes up frequently in interviews. Examples:
Priority queues are a special type of queue where the minimum element in the queue is always at the front. This data structure is less common, but often helps reduce a part of your algorithm with linear complexity down to logarithmic runtime. Examples:
Recursion and dynamic programming usually apply to brute force problems where you have to check all possibilities. Many people struggle with brute force algorithms because their instincts always tell them to look for a clever solution and they pass over the dumb but simple brute force method. If you’re ever not sure how to solve something, then check for a brute force solution. Examples:
Most interviewers ask problems from the first four categories because the tricks aren’t difficult to comprehend. Practice solving lots of problems in these categories and you’ll be able to identify patterns and apply them to problems you haven’t seen before on interviews.
Mastering the fifth category is optional, but encouraged. Many interviewers themselves don’t understand recursion well and are incapable of asking from the fifth category. You can hope you don’t get paired with an interviewer who asks a recursion problem and usually you’ll get away with it. If you do have the time and energy though, practice solving problems in the fifth category to eliminate the variance in your interview results.
For practice, start with easy Leetcode problems. Once you can consistently solve easy problems in 45 minutes, then move up to medium, and then finally to hard. If you can consistently solve hard problems within 45 minutes, you will ace most technical interviews.
Finding a solution to a Leetcode problem boils down to two steps:
Figuring out the optimal runtime complexity helps you find the optimal solution. All solutions to Leetcode problems perform at worst on the order of approximately 10,000,000 computations. For example, if the problem has a worst case input size of N = 1,000 and you write an O(n^2) algorithm, then your solution does 1 million computations, falls within the 10 million limit, and will be accepted, provided you produce correct output. Here are some common input sizes and the likely complexities of their optimal solutions:
n > 10,000,000 | O(log(n)), O(1) is rare |
n <= 10,000,000 | O(n) |
n <= 1,000,000 | O(n) or O(n*log(n)) |
n <= 10,000 | O(n*log(n)^k), k > 1 is rare |
n <= 1,000 | O(n^2) |
n <= 500 | O(n^2*log(n)) |
n <= 100 | O(n^3) or O(n^3*log(n)) |
n <= 24 | O(2^n) |
n <= 10 | O(n!) |
n <= 8 | O(n^n) |
Next, try to match the worst-case complexity with a particular algorithm style. Here are some common styles and their complexities:
O(log(n)) | Binary search |
O(n), O(n^2), O(n^3), … | Nested loops potentially with hashmaps, stacks, and queues |
O(n*log(n)), O(n^2*log(n)), … | Nested loops potentially with hashmaps, stacks, and queues account for the n^k part. Binary search, priority queue, and/or sorting account for the log(n) |
O(2^n) | Brute force powerset search |
O(n!) | Brute force permutations or combinations |
O(n^n) | Brute force generic breadth-first or depth-first search algorithm |
Test out algorithms from your target worst-case complexity category and see if any approaches click with your problem.
Just as there is a runtime limit, there is also a memory usage limit. Try not to store more than 10 million pieces of data in memory. Before coding up a solution, make sure your algorithm adheres to both the runtime and memory constraints. Follow these rules and you avoid wasting time coding up solutions that have no chance of passing the Leetcode test cases.
Once you’ve gotten sufficient Leetcode practice, you’re capable of doing an actual technical interview. Technical interviews involve writing code on a whiteboard for an in-person interview, or typing in an online editor for a virtual interview. Your interviewer gives you a problem and by the end of the interview, you have to produce code that solves the problem.
Most interviews start with the interviewer introducing him/herself and then you introduce yourself. Memorize a short two-minute intro about yourself and recite that back. Your interviewer may ask a few questions about projects on your resume. Keep your responses brief. Don’t spend too long talking about yourself because that will cut down on the amount of time you have for the technical problem.
After all the formalities, your interviewer explains the technical problem to you. If anything is unclear, ask for clarification. I recommend explaining the sample inputs and outputs out loud and ask your interviewer to confirm that your understanding is correct. Clear up any misunderstandings to avoid wasting time coding a solution to the wrong problem.
Next, you have to come up with a solution to the interview problems. If you have no idea how to solve the problem, start by asking your interviewer if you can take a few minutes to think. Think about any Leetcode problems you’ve done that seem similar to your interview question. Interview questions apply the same patterns found in Leetcode problems and there’s a good chance they have similar solutions. If your Leetcode experience doesn’t help, then try applying every common data structure and algorithm in your mind. What can you do with a hashmap? Does a priority queue make any sense? Will a stack help? And never forget to consider a brute force search. See if anything clicks. Even if your idea results in a highly inefficient algorithm, having a solution is better than none.
When any particular method seems promising, talk about it with your interviewer. Your dialog goes along the lines of, “so what if I do X and then do Y? That seems like it could work.” A decent interviewer will nudge you further if you’re headed in the right direction or push back if your idea is incorrect. Silence is bad; if you and your interviewer aren’t talking back and forth for most of the interview, one of you is doing something wrong.
If you aren’t able to come up with anything at all after five minutes of thinking, it indicates you need more Leetcode practice. Be honest and admit you can’t think of anything and ask for a hint. You’re probably not going to pass the interview, but don’t get discouraged. Don’t squander the experience and give it your best.
Once you’ve found the correct solution or if you’ve only found a naive solution and are running low on time, ask your interviewer if it’s okay to start coding. Only start coding when your interviewer has confirmed it is okay to do so.
The coding part is straightforward: as you write your code on the whiteboard, explain what every line does and why you are doing it. As you speak, listen to yourself and ensure your words make sense. Communicate as if you were explaining your code to a friend who is struggling with the problem. Ensure your interviewer is always on the same page as you by stopping now and then and asking if everything makes sense. The most common mistake people make is not talking enough. If you don’t provide thorough explanations, the interviewer will assume you don’t communicate well and score you negatively. It never hurts to over-explain; your interviewer will tell you if you get too detailed.
After you’ve written and explained your code, your interviewer might ask you to fix some minor mistakes, step through your code, or ask some extension problems. Finally, they’ll agree that you’ve solved the problem. Make sure you’ve prepared questions to ask your interviewer in the remaining time.
The way to consistently pass technical interviews is to practice, practice, practice. Practice solving Leetcode-style interview problems until they become second nature to you. Ask friends to do mock interviews with you and practice explaining your solutions to them. You can have the most amazing personal projects and qualifications, but if you can’t pass the interview question, you aren’t getting the internship offer.
Your technical interviewing abilities will help not only for your sophomore year internship search, but for anytime you’re searching for a software engineering role in the future. Start practicing early and often because this investment pays huge dividends down the line.
If you don’t end up with any internship offers, fall back to summer research again. It is critical to have some sort of work experience your sophomore summer. Understand what went wrong and change your strategy accordingly. If you didn’t get any interviews, then get more referrals and apply to more companies. If you failed your technical interviews, then practice more Leetcode. These are the two primary reasons qualified sophomores fail to get an internship.
If you send out lots of internship applications and practice well for the technical interviews, you should wind up with an internship offer. If you received exactly one internship offer, then take it. If your search went extremely well, you may end up with multiple internship offers for your sophomore summer. Choosing between offers comes down to your personal preferences.
Ask yourself what you envision your full-time job looking like. Are you going to be a web developer? A mobile app developer? A data scientist? A database engineer? Something else? Choose an internship that allows you to develop the skills needed for your desired full-time job. All other things equal, pick the company with the highest reputation. Do not prioritize intern pay because a few thousand dollars now is insignificant in the long run. Select the internship you think will best help you get your ideal full-time job.
Your sophomore internship is also a convenient time to step outside your comfort zone and open the door for new opportunities. Perhaps you’d like to see what it’s like living in a new city or country? Or do you want to pass on a large tech company and join a startup instead because you think you’ll like the culture more? It’s okay to try something that you might not end up liking because you still have more time before graduation.
Take a risk, or don’t take a risk — whatever you’re comfortable with. Just make sure you understand why you are picking a company for a sophomore summer internship and how it aligns with your goals.
Outside of classes and the internship search, let’s review your mental health and physical health. During your freshman year, hopefully you found a good routine with regards to socializing, sleep, exercise, eating, and balancing all your activities. If things are working out, then continue doing what you’re doing.
If things are not working out well, don’t let setbacks deter you. Stay calm and take a step back to analyze the situation. The following are some common issues people face.
Feeling down about grades or internship rejections. Don’t kill yourself over low grades. What matters is if you put in your best effort. Seek additional help from professors or teaching assistants. You may need to adjust how you learn and the course staff can help. For internship rejections, don’t blame yourself for things outside your control. Keep on networking and practicing Leetcode; you’ll eventually land a position. Remember: a lot of things in your life are out of control; stay positive and don’t let the setbacks overwhelm.
Relationship breakups. Breakups are normal. If your partner ends your relationship, it means they decided things weren’t as compatible as they originally anticipated. It’s never a judgment about you — it’s your partner deciding that their preferences don’t align with yours. You have many more years ahead, many more amazing people to meet, and a college breakup is never the end of the world.
Struggling to meet people and make friends. Are you afraid to approach others? It’s all in your mind — people aren’t going to kill you when you approach them. Are you uptight or difficult to talk to? Relax more and read some books on how to be more humorous. Are you not finding shared interests with others? Keep an open mind and try something new — you might discover a new hobby and make new connections. Humans are all born social creatures. If you make yourself approachable, people will talk to you and befriend you.
Lack of energy. If you find yourself lacking energy and it’s not due to temporarily feeling down about something, then check your nutrition. Make sure you’re eating three balanced meals a day. Eat lots of vegetables of different colors to get all the vitamins and minerals you need. Consume fats and proteins to stay feeling full. Foods with healthy unsaturated fats include avocados, nuts, and olives. Healthy protein sources include tofu, beans, fish, chicken, and any non-red meat. Ditch sugary drinks and desserts because excessive sugar causes your energy levels to spike and then plummet. An improper diet is a common cause of low energy levels.
For anything that’s not working well, take a step back and determine what’s holding you back. If it helps, talk through your issues with someone you trust or consult books and online resources. Determine the source of the problem and then devise a solution and carry it out.
In subsequent chapters, we won’t cover much more on mental health and physical health because most people manage these dimensions successfully throughout college. From here on, we’ll be dedicating our focus to academics and jobs, which are the two areas in which many people make major mistakes that hurt their future job prospects.
Academics
Internships
Mental/Physical Health