Calvin Goodale, Developer - Blog
c g o o d a a l e . . c o m

3D Surface Area HackerRank

Challenge:

Given a 2D array representing a square of stacks of blocks, calculate the total surface area of the blocks. 

https://www.hackerrank.com/challenges/3d-surface-area/problem

This was a fun problem that sounds more challenging than it actually is. My thought process was that we need to first calculate the height of the stack of blocks at each index as if it had no neighbors, which is 2 + (height of stack x 4), and then subtract the height of any of its neighboring stacks (or just subtract it's own height if the neighboring stack is greater than the current stack). We also need to account for edges and corners, which is handled by making sure the index of each neighboring index is greater than 0 and less than the height/width.

My solution:

def surfaceArea(A):
    total_area = 0
    for index, row in enumerate(A):
        for index2, column in enumerate(row):
            column_area = 2 + (column * 4)

            if index2 - 1 >= 0:
                left = A[index][index2 - 1]
                if left > column:
                    column_area -= column
                else:
                    column_area -= left

            if index2 + 1 <= len(A[0]) - 1:
                right = A[index][index2 + 1]
                if right > column:
                    column_area -= column
                else:
                    column_area -= right

            if index + 1 <= len(A) - 1:
                down = A[index + 1][index2]
                if down > column:
                    column_area -= column
                else:
                    column_area -= down

            if index - 1 >= 0:
                up = A[index - 1][index2]
                if up > column:
                    column_area -= column
                else:
                    column_area -= up

            total_area += column_area

    return total_area


0 Comments

HackerRank: Matrix Layer Rotation (Part 3)

I figured out that the reasons my code was failing due to Run Time Errors were (1.) I was creating a new empty matrix with the same dimensions as the given matrix and then appending the rotated arrays to that, instead of simply replacing the elements in the given matrix with the elements from the rotated arrays, and (2.) I was deleting each section from the flat array after adding it to the new matrix, which I found easier at the time, since I wouldn't have to calculate the new index of the next section. This fix was pretty simple, since I just needed to keep an incremental index going while getting the correct slices from the rotated flat array.

It took me a little while to determine the best way to handle the first part, but I finally passed 12 out of 12 test cases with the following code:

def matrixRotation(matrix, r):
    # Number of rows in the matrix
    rows = len(matrix)
    # Number of columns in the matrix
    columns = len(matrix[0])

    # New list that will contain each slice of the matrix in a new list
    matrices = []
    # The number of slices in the matrix
    matrix_num = int(min(rows, columns) / 2)
    i = 0
    while i < matrix_num:
        single_matrix = [matrix[i][i:columns-i],
                         [matrix[row][columns-1-i] for row in range(i+1, rows-i-1)],
                         matrix[rows-i-1][i:columns-i][::-1],
                         [matrix[row][i] for row in range(i+1, rows-i-1)[::-1]]]

        i += 1
        # Turn the single_matrix list of lists into a single list
        single_matrix = [mat for mat in single_matrix if mat != []]
        # Turn into a flat list
        single_matrix = list(itertools.chain(*single_matrix))
        # Rotate this flat list counter clockwise by r
        # If r is more than the length of the matrix it's more efficient to use r % len
        new_r = r % len(single_matrix)
        # Rotate the list
        single_matrix = single_matrix[new_r:] + single_matrix[:new_r]
        # Append the rotated list to the matrices list
        matrices.append(single_matrix)
    flat_matrices = list(itertools.chain(*matrices))
    m = 0
    rs = rows - 2
    cs = columns
    index = 0
    # Create new matrix from rotated matrix
    while m < matrix_num:
        # Create new top
        matrix[m][m:m+cs] = flat_matrices[index:][:cs]
        index += cs
        # Create new right side
        for k in range(rs):
            matrix[k + 1 + m][-1-m] = flat_matrices[index]
            index += 1

        # Create new bottom
        matrix[rows - 1 - m][m:m+cs] = flat_matrices[index:][:cs][::-1]
        index += cs
        # Create new left side
        for k in range(rs):
            matrix[rs - k + m][m] = flat_matrices[index]
            index += 1
        m += 1
        rs -= 2
        cs -= 2
    # Print each row as a string joined by a space
    for row in matrix:
        print(' '.join(str(num) for num in row))

It took me a bit longer than I expected to solve this problem, but as it was my first "Hard" level HackerRank problem, I'm just glad I was able to complete it. There are definitely ways of rotating each layer in place rather than creating flat arrays from each matrix layer and adding them back in, but I'm happy with my solution.


0 Comments

HackerRank: Matrix Layer Rotation (Part 2)

Continued from my last post. It looks like I'll need at least one more Blog post related to this problem, since although I found a solution, it is not optimized enough to pass all of the test cases. I'm getting timeouts on 3 of the 10 test cases in HackerRank, and I'm not quite sure where my bottlenecks are yet.

My first try had even more timeouts, but I realized that I can use mod against the number of rotations (rotations % len(array)), since a number of rotations that is more than the length of the array being rotated can be reduced. I implemented this, but am still getting timeouts.

I suspect that one of my bottlenecks is the fact that I'm creating an empty 2d array of the same number of rows and columns before adding my new rows of data to it, so one possible solution would be for me to figure out how to add the data directly to the given matrix, rather than creating a new empty matrix first.

Current (non-optimized) solution:

def matrixRotation(matrix, r):
    # Number of rows in the matrix
    rows = len(matrix)
    # Number of columns in the matrix
    columns = len(matrix[0])

    # New list that will contain each slice of the matrix in a new list
    matrices = []
    # The number of slices in the matrix
    matrix_num = int(min(rows, columns) / 2)
    i = 0
    while i < matrix_num:
        single_matrix = []
        # The top (left to right) of the matrix slice
        top = matrix[i][i:columns-i]
        # The right side (top to bottom) of the matrix slice
        right = [matrix[row][columns-1-i] for row in range(i+1, rows-i-1)]
        # The bottom (right to left) of the matrix slice
        bottom = matrix[rows-i-1][i:columns-i]
        bottom.reverse()
        # The left side (bottom to top) of the matrix slice
        left = [matrix[row][i] for row in range(i+1, rows-i-1)]
        left.reverse()

        i += 1

        # Append each side of the matrix slice to the single_matrix list, with each side in its own list
        single_matrix.extend([top, right, bottom, left])
        # Turn the single_matrix list of lists into a single list
        single_matrix = [x for x in single_matrix if x != []]
        # Turn into a flat list
        single_matrix = list(itertools.chain(*single_matrix))
        # Rotate this flat list counter clockwise by r
        # If r is more than the length of the matrix it's more efficient to use r % len
        new_r = r % len(single_matrix)
        for _ in range(new_r):
            single_matrix.append(single_matrix.pop(0))
        # Append the rotated list to the matrices list
        matrices.append(single_matrix)

    flat_matrices = list(itertools.chain(*matrices))
    new_matrix = [[] for i in range(rows)]
    i = 0
    rs = rows
    cs = columns
    # Create new matrix from rotated matrix
    while i <= matrix_num:
        # Create new top
        new_matrix[i][i:i] = flat_matrices[:cs]
        del flat_matrices[:cs]
        # Create new right side
        for k in range(rs - 2):
            new_matrix[k + 1 + i].insert(i, flat_matrices[k])
        del flat_matrices[:rs - 2]
        # Create new bottom
        new_matrix[rows - 1 - i][i:i] = flat_matrices[:cs][::-1]
        del flat_matrices[:cs]
        # Create new left side
        for k in range(rs - 2):
            new_matrix[rs - 2 - k + i].insert(i, flat_matrices[k])
        del flat_matrices[:rs - 2]
        i += 1
        rs -= 2
        cs -= 2

    # Print each row as a string joined by a space
    for row in new_matrix:
        print(' '.join(str(x) for x in row))

Here is the problem I'm working on, if anybody is interested:
https://www.hackerrank.com/challenges/matrix-rotation-algo/problem

Hopefully I will have a Part 3 with an optimized solution soon.


0 Comments

HackerRank: Matrix Layer Rotation (Part 1)

I've been doing a few HackerRank problems each week since I started studying Python (sometimes other things such as take-home challenges for interviews take precedence) to get better at problem solving. I'm at the point now where the problems rated as 'Easy' are very doable for me in less than an hour, and the 'Medium' problems I can usually solve within a few sessions of work. This is my first 'Hard' rated problem attempt, and I thought it would be a good one to work on since the problem is easily understandable for me:

Given a matrix (2d list of lists in this case) and a number of rotations, rotate the entire matrix counter clockwise by the number of rotations and then return the new matrix.

The first thing I did in order to begin solving this problem is to get each matrix 'slice' (with each slice being a rectangular 'box' of elements, for example the outermost box, then the box smaller by 1 on each side, etc) into a string, in order around the box starting from the top left, so I could easily 'rotate' this slice the appropriate number of times:

import itertools

matrix = [[1, 2, 3, 4, 5, 6],
          [7, 8, 9, 10, 11, 12],
          [13, 14, 15, 16, 17, 18],
          [19, 20, 21, 22, 23, 24],
          [25, 26, 27, 28, 29, 30],
          [31, 32, 33, 34, 35, 36]]
r = 2


def matrixRotation(matrix, r):
    # Number of rows in the matrix
    rows = len(matrix)
    # Number of columns in the matrix
    columns = len(matrix[0])

    # New list that will contain each slice of the matrix in a new list
    matrices = []
    # The number of slices in the matrix
    matrix_num = min(rows, columns) / 2
    i = 0
    while i < matrix_num:
        single_matrix = []
        # The top (left to right) of the matrix slice
        top = matrix[i][i:rows-i]
        # The right side (top to bottom) of the matrix slice
        right = [matrix[row][rows-1-i] for row in range(i+1, rows-i-1)]
        # The bottom (right to left) of the matrix slice
        bottom = matrix[rows-i-1][i:columns-i]
        bottom.reverse()
        # The left side (bottom to top) of the matrix slice
        left = [matrix[row][i] for row in range(i+1, rows-i-1)]
        left.reverse()
        
        i += 1

        # Append each side of the matrix slice to the single_matrix list, with each side in its own list
        single_matrix.extend([top, right, bottom, left])
        # Turn the single_matrix list of lists into a single list
        single_matrix = [x for x in single_matrix if x != []]
        # Turn into a flat list
        single_matrix = list(itertools.chain(*single_matrix))
        # Rotate this flat list counter clockwise by r
        single_matrix = single_matrix[r:] + single_matrix[:r]
        # Append the rotated list to the matrices list
        matrices.append(single_matrix)

    return matrices

Now I have each slice of the matrix in a flat list and rotated the correct number of times. My next steps will be to turn this flat list back into a matrix of the same number of rows and columns as the original matrix, which for some reason feels more difficult than the steps I took get each slice into a list. My next blog post will (hopefully) detail how I achieved this.

To be continued...


0 Comments