When learning how to code, all is well as you go through countless tutorials.
Tutorials guide your way. Holding your hand each step of the way. Telling you what you exactly need to do, and when. But after doing the tutorial, you are on your own. To attempt solving problems all on your own. Jumping into the deep end.
When you attempt to solve a problem, you realize that the knowledge you gathered from tutorials does not map one-to-one to the solution of that problem. Because it will need a different set of skills.
I, too, had the same challenge when I learned how to code. But after working on many problems, I realized that there is a particular pattern I follow to come up with a solution.
In this article, I will show you the exact steps you need to follow to solve computational problems. Do this to increase your chances of getting a solution.
TL;DR
Here are the steps I follow when solving coding problems. I invite you to adapt the steps to suit your needs. (Depending on how far you are in your journey to learning how to code).
Understand the problem well [rephrase it if possible in your own words]
Gather the requirements of the program [Inputs, processes, outputs]
Break down the problem into simpler tasks
Think of possible test cases [edge cases, normal cases, weird cases]
Inspect your toolbox [language, methods, data structures, common algorithms]
Write pseudocode [a rough idea of a possible algorithm]
Write code [use the toolbox, IDE, code editor, minimum viable program]
Run your code [with the test items you had come up with, think of more edge cases]
Debug your code [Fix problems arising from testing your code]
Celebrate.
That is it. Whenever you are working on a coding problem, or attempting coding challenges on sites like LeetCode or CodeWars or you were given a take-home coding challenge after bagging an interview (massive congratulations on that one btw), following these steps will lead you to a solution.
We will look at each step in detail in the rest of this guide.
But, this guide will only give you a path to follow to come up with a working solution. In no way do I want to claim that the solution you get is optimal. Usually, but not always, once you have a working solution, optimizing is much easier. What with the confidence that comes with knowing that your idea works!
Exercises
Note that this is a practical guide and you will need to do some work along the way. Grab a pen and a notebook and follow along.
We shall tackle two exercises in this article. We shall use the first exercise to practice working out coding problems following the method I use. The second exercise will be left as a take-home practice exercise. You will need to work that one out on your own.
Once you are done with these exercises, you can check out my proposed solutions from this GitHub repo.
Exercise One
Write a program that receives an alphanumeric string and prints the sum of the numeric digits in the string.
>>> numericSum("h3l1o w07ld!")
>>> 11
Exercise Two
Given a list of a pair of points on a Cartesian plane, write a program that returns true if the points are on a straight line, and false otherwise.
>>> straight([(1,2), (2,3), (3,4), (4,5)])
>>> True
>>> straight([(1,2), (2,3), (3,4), (4,8)])
>>> False
Let's look at the steps involved when solving a coding problem.
Understand the problem
Over the years, I have realized I think more clearly when I scribble something down, on paper. Not typing. If I had read the question in advance and had a rough idea of what is required, then I am not at my desk either.
All the best if you attempt to solve a problem that you have not clearly understood.
Read the question, word for word, line by line, and, in between the lines. And understand what is required. If you are having trouble understanding the problem, try rephrasing it in your own words. You will discover what it is you could be missing.
Gather the requirements of the program
As you read through the question be sure to get the following details:
What are the inputs to the program?
What is the output?
What processes take place as the program runs?
Look out for nouns and verbs.
Nouns indicate the inputs and the output. They give you an idea of the variables you need. Verbs hint at the processes. Do you need a name for your function or method? Check the verbs.
Take a minute and look at our sample problem, again.
Can you identify the nouns? The verbs?
Before you think of a possible solution, can you tell at least one variable that you will need? Do you see a potential name for a function?
When you get to the point of identifying the inputs, outputs, and processes of a program, you can be confident that you understand the problem you are solving.
In a simple problem, the requirements are obvious. You can see the inputs right there: "an alphanumeric string". The output is stark naked, right there: "a sum of integers". And, it is clear what is going on, the process. Summing the integers in an alphanumeric string. Sum. Simple.
In a more complicated problem, it might not be obvious what is going on in the program. You will have to dig deeper to unravel these things.
Break down the problem into simpler tasks
How do you dig deeper if the problem is more involved?
Try breaking the problem into simpler tasks. Each task you identify should be simple enough that you know what its input is, what process will take place, and, what it will produce. But do not limit yourself. Think of as many ways you can break the problem into as you can.
Do not worry if you cannot think of all possible "simpler tasks". The idea is to get started and get going. You will figure out more as you try to solve each task.
Think of possible test cases
It is important to think in advance how to tell that your program is doing what you expect.
How will you know that your program is running as expected once you have implemented it? That your simple tasks are accomplishing your goals? It is important that you think about this in advance. Well before you think of a possible algorithm.
These will form your test cases. A few may be given. (Like is the case for the problem we are working on.) But you need to think of other possible cases to test your program thoroughly. Think of different inputs that you can run your program with to satisfy all the defined requirements. Think of edge cases, weird cases that your clients may want to test your program with. An empty string. A string with all alphabets. A number. And so on. I try to aim for a minimum of 3 test cases. More doesn't hurt.
Take an example of the program we are working with.
What are the different inputs the program could be subjected to?
Here is a sample:
>>> numericSum("") # Empty string
>>> numericSum("3") # String with a single digit
>>> numericSum("k") # String with a single character
>>> numericSum("h3l10") # String with a mix of alphanumeric characters
>>> numericSum("2023") # String with all numeric digits
As an exercise:
What is the output of our program for each test case?
What other tests could we add?
Inspect your toolbox
You are almost ready to start writing your program. But not so fast. The actual coding is only a small portion of problem-solving, of turning a problem into code. The least of the work. Most of the work is done before you open your favorite IDE or code editor.
How so? Once you have your program planned out in advance, once you have an idea of what needs to be done, the rest is simple. Get your toolbox, take out the right tools, and get to work.
Toolbox. The programming language of choice. Common algorithms you are familiar with. Data structures. That is your toolbox.
Are there tools in the toolbox?
The language syntax. Do you know how to declare variables? Do you know different string methods? Looping techniques. Exception handling. How do you sum integers? These are tools. The axe you will need to chop down a tree.
But not to worry. All these things are there in the language documentation. Or a Google search away. You do not need to have them all at your fingertips to solve a problem (unless you are in college and writing an examination). What you should have is the knowledge that these things can be done in your language of choice, then search for how to do them. That is simplifying a problem.
You do not need to remember how to split a string in Python. You can check that out. But I guess you need to be aware that a string can be split.
By inspecting your toolbox, about the problem at hand, you realize that you know enough. And you can get very far in coming up with a solution, without reading another tutorial. Without wasting time. By getting things done.
And if not so, you pinpoint the knowledge gaps to fill. Which makes your search for knowledge simpler. Your search for how to extract digits from an alphanumeric string becomes "how to loop through a string in Python", followed by "how to tell that a character is an integer, not a letter of the alphabet ..."
You get better results.
You also know when you need to ask for help. And how to ask for it. Which is also an important skill.
Let's take our example further, and, see if we have the required tools in our toolbox.
The input to our program is a string. So we shall need string methods in our toolbox. Next, we shall be summing integers. We shall work with integers, we need that in our toolbox. And print the result. How do you print the output in Python? Or in Go?
Write Pseudocode
By the time you get to this stage, you know enough about the problem you are solving that you almost have the solution.
In some cases, you will need to write pseudocode for the proposed solution. Pseudocode will guide your way as you turn your solution into code. It will help make your code easier to follow.
This stage helps clarify any doubts. What was not clear becomes apparent as you write an algorithm that solves your problem. Wrong assumptions stare at you in the face.
You could try running the code, by pen and paper, as you work out the algorithm in pseudocode to clarify some areas. To see if you overlooked some detail.
Unless the problem is so easy you know the solution by just reading the description, I would suggest you do not skip this stage. It will make the next step easier to go through.
If you have written pseudocode before, try it for this exercise. See what you come up with. Don't worry if you have not done it before. Look for a simple guide on how to write pseudocode (yet another tutorial, LOL!) like this one then try writing an algorithm for the problem we are working on.
If you are not able to complete this stage, it might be because you have not captured finer details about the problem you are solving. Are there details you missed out on? Could your toolbox be ... empty? (That is not possible! Might just be missing the right tools. Time to refresh your knowledge? Or choose a more familiar language.)
Or choose a different path: take a short break and come back to it later. You will be amazed by this simple act.
Write the code
Seriously. Take a break. Go for a short walk. Look at the distance. Grab another coffee. Whatever. (But do not read another tutorial ... What for?)
Because this is the final part and it is the simplest. Simple if you took all the other steps. Flipping your laptop open, starting your favorite code editor (welcome to NeoVim btw), and, typing away. This is the tree you sharpened your axe to chop.
It will feel as if nothing is going on. No breaking a sweat. No butterflies. Do you know why? You prepared for this all that time. So have fun. Do your best using the tools you have in your toolbox to do an amazing job.
(Be careful using the axe. It is so sharp.)
A few keystrokes in and you have your full code. Ready to be battle tested.
Imagine that.
Go ahead. I will wait. Try turning the pseudocode in step 6 into a program, in your favorite language.
Test your program
You now have a full program for solving your problem. Look out for any glaring issues that could make your algorithm fail to run, or give inappropriate output. Your IDE could be complaining that you used a variable before declaring it. Or that you re-declared a constant. Go fix those.
There are no issues? None at all? Good. You have done so well. This is how planning out your code in advance does. The code you write is near perfect. Even before you test it, you just want to take another break. Look at the distance. Catch some fresh air.
You are confident the code will work. With the first attempt to test it.
Let's confirm that.
(Do take that break, come back and run your tests.)
Run your program with each of the test inputs that you identified in Step 4 and see what happens. If there are issues, it will be easy to fix them.
Although, sometimes debugging could turn out to be another challenge. But with much of the work done, it will end well. Hopefully.
When you are done with this step, go celebrate one more win!
In the end
Problem-solving is a learned skill. Learned by doing more and more of these problems. Cracking each of them. So, go improve your problem-solving skills.
And, I wish you all the best.
Practice
Practice makes perfect. It is only fair that you practice the skills you have learned today.
Follow the steps in this article to solve Exercise Two.
The solution to both exercises can be found in this GitHub repo: https://github.com/JosephMainaDev/solving-coding-problems.
Please work out the problems first before you look at my solutions. That way, you learn better.
PS:
When you have a running minimum viable program, you can think of optimizing it for better run time. Or to use fewer resources. Or to use a better algorithm. Refactoring comes after you test your algorithm and are confident your way of doing things works. Not before.
If you are stuck, at any of the stages, remember to ask for help. Early. Seek clarification when necessary. But do not ask for obvious things. The things that you can work out yourself. You should learn how to search for a solution. How to Google and read the documentation. How to unstuck yourself when you are stuck.
Happy Coding!