I Got 99 Problems, and Pyramid’s Just One

Anh
6 min readOct 5, 2020

Hello, fellow soon-to-be software developer. If you are preparing for upcoming interviews like me, you’re most likely (or will about to be) grinding away on loads of algorithm problems. In this post, I’d like to share a walkthrough of a problem I recently solved, in hope of better understanding it myself, as well as helping anyone who might be struggling with similar problems.

For this particular bad boy, we’re tasked with writing a function that prints out a two-sided pyramid of n levels that follows a couple of simple rules. I figured out two ways of tackling this (one I much prefer over the other). Let’s jump in!

Two-Sided Pyramid, Explained

Specific instruction for this problem is as follows:

Write a function that accepts a positive number n. The function should console log a pyramid shape with n levels using the # character. Make sure the pyramid has spaces on both the left *and* the right-hand sides.

Examples:

Side note: This problem was taken from Stephen Grider’s Udemy course on Algorithms + Data Structures, which I highly recommend.

Solution #1

I first looked at the examples given to identify common patterns shared and jotted down these observations. Here is how I broke down the patterns using pyramid(4) as a case example.

  • First, I related my observations to the stable argument n. The pyramid consists in this case of 4 or n rows.
  • Next, I went row-by-row, and again wrote down my observations regarding how many spaces are on either sides of the #. From this, I deduced that spaceCount or the number of spaces on either side starts at n — 1 (or 4 — 1, which is 3). For each new row, spaceCount decreases by 1 on either side, until you reach the final row, in which case the final spaceCount is 0.
  • On the other hand, the hashCount or number of #’s in the middle of each row, starts at 1 and increments by 2 each time you move onto a new row.
  • With these observations in mind, I started pseudo-coding out the solution, which looks like this:
  1. Create a variable called spaceCount initially set to n — 1.
  2. Create another variable called hashCount initially set to 1.
  3. While spaceCount hasn’t reached below0, console.log out spaceCount number of spaces, followed by hashCount number of #’s, followed again by spaceCount number of spaces. I used spaceCount as the variable whose value determines when I will be “done”, as I know that we will have finished printing the pyramid once we reach the row with only #'s and no spaces.
  4. After each iteration, we set the new spaceCount to be one less than what it was, and we set the new hashCount to be two more than what it was.
  • Then I coded out my solution:

Within my outer while loop, I created two variables, called spaces and hashes which will store the actual string fragments; initially they are empty strings. I created two inner while loops that add to said empty strings either spaces or #'s according to the current spaceCount and hashCount. Next, I concatenated the string parts and console.log’ed them out. And lastly, I decrement spaceCount by 1 and increment hashCount by 2 for each iteration of the outer while loop.

And voila, completed! I like this first solution as it followed exactly what I observed without much math (beyond counting) involved. The next solution is a bit complicated, in my opinion, but I find that learning multiple ways to solve the same problem always ends up teaching me something new.

Solution #2

This solution involves seeing the pyramid in terms of rows and columns. This way, you can identify each position in the pyramid by specifying the row number it belongs to and the column number it belongs to in a battleship-esque way. Using this matrix, we can determine at which position to add # and at which to add space. I drew out a diagram, labeled rowIdx’s and columnIdx’s and identified some observations:

There are some “stable” variables we can take note of here. First is that all rows share a common “midpoint” at columnIdx 3, we can use this to figure out the “range” of columnIdx’s where we will be adding #'s rather than spaces. Second, we notice that the length of each row is consistently 7. This is useful to know so that when we are constructing out each row, we know when to stop.

We will make use of two for loops: an inner loop that adds either # or a space to a particular row, and an outer loop that will print out each row. Let’s solve this one layer at a time, by first constructing the outer loop:

  • In line two, we specify that we will be creating rows from index 0 to the index right before n. Given the 0-based indexing that is happening here, the condition rowIdx < n will make sure that we end the loop with n number of rows printed out. rowIdx++ is the iteration part of the for loop, which ensures that we increment the index by one with each iteration.
  • Notice here that row has not yet been defined. Next, let’s talk through the logic regarding how we will flesh out each row given the observations we made, and then code accordingly.

First I wrote out how to calculate the stable length of each row from n since n is the only argument passed into the function; hence, we need to find ways to deduce everything else in relation to n. To get from n = 4 to rowLength = 7, we can use the formula: rowLength = n * 2 — 1. Knowing the length is useful for our inner for loop, as we can set it as the condition, where as long the columnIdx is less than the rowLength, we can continue adding characters (either # or space).

Next, I wrote out how I can calculate the midpoint, in the case above, it is at columnIdx 3. And this is the formula: midpoint = Math.floor(rowLength / 2). Since rowLength in this case is 7, 7 / 2 = 3.5. Using Math.floor(), we round the result down to 3, hence arriving at columnIdx 3.

Next, I deduced the pattern of spaces and #'s for each row in relation to the midpoint and rowIdx, like so:

  • Looking at the pattern above, we can say that the columnIdx’s where we will be adding #'s are from the startIdx of midpoint — rowIdx to the endIdx of midpoint + rowIdx. Refer to the drawn diagram above for a visual of the row and column matrix.

Given all this information, we can write the logic to construct each row as follows:

  1. Create a variable called row that is initially an empty string
  2. Define rowLength
  3. Define midpoint
  4. Loop from columnIdx to the index just before rowLength
  5. The range of indices where we are to place the #'s are from startIdx of midpoint — rowIdx to the endIdx of midpoint + rowIdx, as stated above
  6. If the current columnIdx belongs in that range, then add a # to the row string, if not, add a space instead

And finally, the code:

Again, I prefer the first solution, as it involves less calculation and is much more straightforward. However, this solution allows for a different view of the same problem. There is a third solution that involves recursion, but I won’t be going into that here.

Skills I practiced from solving this problem include: 1) articulating in human terms step-by-step the patterns I observed in the examples given and 2) relating said observations back to the “stable” variables, especially the argument(s) provided to the function.

I hope you find this helpful. And I wish you the best on your next coding interview!

--

--