Learning Go by Solving LeetCode Problems — TwoSums

Jonathan Duran
7 min readJul 6, 2022

I want to learn Go, however, I don’t have the energy or time for anything super structured (i.e. courses, books, etc.). I thought it’d be fun to learn Go by jumping into the easiest Leetcode problem I could find.

Here’s my plan…

  • Come up with a logical plan for the problem.
  • Take it one step at a time.
  • Research Go stuff (conditionals, syntax, etc.) relevant to that step.
  • Implement Go stuff.
  • Iterate.

Solving problems locally.

I chose to develop my solution locally using VSCode for the following reasons…

  • I’m forced to learn how to create and structure a Go program (as opposed to jumping into the Leetcode environment where everything is already set up for you).
  • I’ll be able to figure out how to set up functions.
  • I’m extremely curious. Testing/debugging the “what-if-i-do-this-instead?” is easier when working in a local environment.

Firing up the project structure 🚀.

After reading the official docs, structuring my project was pretty straightforward.

I’m not trying to become a Go master after this first exercise so I avoided going too deep into any single concept. Basically, we have the following key points about structuring a project…

  • Once you create a directory for your project, run go mod init <module_path> to initialize a module within your project directory. <module_path> can be anything and just serves as a prefix to the paths of any packages in your module.
  • Modules are a collection of packages and packages are a collection of source files that share the same package directory.
  • Any executable commands must be in a package named main, with a source file containing a main function that serves as the entry point to your program.
  • All source files must begin with the package <PACKAGE_NAME> declaration.

If that was confusing, here’s the project structure I’m running with for this exercise.

--| leetcode-go (project root directory)
--| main.go (source file that will contain our solution)
--| go.mod (automatically created when running go mod init)

That’s it! No need to go crazy learning about packages, imports, external dependencies, or anything that is unrelated to the goal of this article.

Before moving, let’s peek into the contents of the go.mod file that was automatically created after running go mod init leetcode-go (btw, the module path does not have to match the name of the project).

Awesome! a version number and a module path!! On to the code 🤷🏼‍♂️.

📚 Project structure resources

Creating a skeleton for main.go 💀.

So, the “main” name for the file isn’t required. I could’ve named this file leetcode.go . However, a main package with a main function is necessary for your program to run.

🚨 The main function takes no arguments and returns nothing.

main.go

How to do functions?

The Leetcode problem involves inputs and outputs. The function in the Leetcode environment takes in a slice called nums and an integer called target , and returns a slice. Arguments and return values are not allowed with the main function so I’ll create a helper function to handle the inputs/outputs as well as the solution’s logic.

The syntax of a function and its signature is as follows…

  • func funcName(param1 dataType, param2 dataType, ...) returnValueDataType {}

📚 Function resources.

Constants, Variables, Arrays, and Slices.

We need to define the inputs somewhere in our code. For this, we must first learn about declaring variables and constants and also look into the differences between arrays and slices.

Constants

  • Constants need to be declared with the const constName = constValue syntax. They can be assigned numeric values, strings, and booleans.
  • As their names suggest, constants cannot be changed.

Variables

  • Declaration without assignment — var varName dataType
  • Declaration with assignment — var varName dataType = varValue
  • Data type inference — var varName = varValue . Go will infer the data type from the varValue .
  • Short variable declaration — varName := varValue . Go will figure things out based on the varValue .

Arrays

  • Can be declared with var arrayName [numElements]dataType = [numElements]dataType{a, b, c, ...} (not the only way!).
  • Arrays have a fixed length. You cannot add or delete elements.
  • Elements can be changed using arrayName[index] = newValue assuming the index is within bounds.

Slices

  • Can be declared with var sliceName []dataType = []dataType{a, b, c, ...} (notice that there is no numElements in the datatype).
  • Slices are references to an underlying array (in the above bullet point, Go creates an array having the same number of elements as you declared in {a, b, c, …} and uses that as a reference to your slice).
  • Using a slice to change the value of the underlying array affects all other slices that reference the same array.
  • You can add and drop values of a slice but re-slicing or using the append function.

Since we aren’t changing any of the elements in nums and according to Leetcode, we must return two integer values, we can go ahead and change our slices into arrays of the desired length.

HashMaps 🗺

As we iterate over the nums array, we’ll need to compute the complement of the current number in nums and the target (complement = target — current_num ). After every iteration, the current number, along with its index, should be added to a hashmap and used as a potential complement during later iterations.

Time to tackle hashmaps!

  • Like slices, maps are reference types (they reference some underlying map that the Go runtime handles).
  • There are various ways of declaring a map. For my solution, I chose mapName = map[keyDataType]valueDataType{} to declare an empty map.

📚 Hashmap resources

Conditionals 🚦

As we iterate over nums we check the map for the complement target — current_num, if it exists then we return the indices of the current number and complement, if not, we add the current number and its index to the map and continue iterating.

As a reminder, we don’t want to learn everything there is to know about loops. We only care about implementing a loop that iterates over an array and gives us access to its elements and index values. We actually only care about the index since array[index] returns its value.

The simple solution is for i := 0; i < len(array); i++ { someLogic } .

As for maps, we need a way to check if a key exists as well as how to add new data. Adding data to a map is as simple as map[newKey] = newValue . However, checking for a key was a bit weird. Trying to access a value in a map returns two pieces of information — 1. the value if it exists and 2. a boolean stating whether the value exists.

For example…

someMap := map[string]int{"hi":1}
val, exist := someMap["hi"] // val = "hi", exist = true
val, exist = someMap["hello"] // val = 0, exist = false

🚨 We don’t need a second short variable declaration in the example above since we’ve already declared val, exist to be a value in someMap .

Few things…

  • Go wants you to use every variable you declare. If you don’t plan on doing that (like me on line 17) you should substitute the variable name with an underscore.
  • Unlike Python, If we tell Go that we are returning a 2-length array, then we must return a 2-length array. Leetcode guarantees a solution exists for this problem so In the impossible case where there is no complement, I return a 2-length array of zeros.
  • I’ve imported the fmt package that comes with Go’s standard library. This will help us validate our solution by printing the results to the terminal.

📚 Conditional resources

Solution 💪🏼

Running the main.go locally using the go run main.go command gives us the correct answer [0, 1] .

Before copying the code inside of the twoSums function over to Leetcode, make sure you change all the arrays to slices (e.g. [2]int -> []int ) and you can return a nil instead of the 2-length array of zeros (e.g. return [2]int{0,0} -> return nil .

Leetcode

The point of this exercise was to give an alternative way of learning something new. I was definitely more motivated to learn Go using Leetcode as a guide compared to opening up a book or going through some YouTube video. Admittedly, I’m already hooked on Go and plan on continuing this form of learning. Stay tuned for the next article and thanks for reading!

If you enjoyed this article go ahead and follow me on Twitter!

--

--