Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Let's take a look at an example problem to demonstrate a use for queues (credit to https://leetcode.com/problems/shortest-subarray-with-sum-at-least-k/ for the problem statement).

Return the **length** of the shortest, non-empty, contiguous subarray of `A` with sum at least `K`.

If there is no non-empty subarray with sum at least `K`, return `-1`.
**Example 1:**
```
Input: A = [1], K = 1
Output: 1
```
**Example 2:**
```
Input: A = [1,2], K = 4
Output: -1
```
**Example 3:**
```
Input: A = [2,-1,2], K = 3
Output: 3
```
The function header should be defined as follows.
```python
def shortestSubarray(A, K):
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Shortest Subarray with Sum at Least K - Conceptual Solution

First, we should create an array (call it `dp`) which holds the sum of the first `i` elements, where `i` is the index of our array. That is, if our input array is `[1, 2, 3, 4]`, `dp` should be `[0, 1, 3, 6, 10]`. Notice that `dp[1]` is 1, `dp[2]` is 1 + 2 = 3, `dp[3]` is 1 + 2 + 3 = 6 and so on.

What else do you notice about the relationship between the elements of `dp`? Notice that if you take `dp[y] - dp[x]`, where `y` and `x` are indices and `y > x`, you obtain the sum of **all** of the elements between the (x+1)<sup>th</sup> and j<sup>th</sup> element of our original input array<sup>1</sup>. Essentially, our goal has transformed from finding the shortest subarray greater than or equal to k in our input array to an equivalent problem of finding the smallest value of `y` - `x` such that `dp[y] - dp[x] >= k`.

There are a few important points<sup>2</sup> about the `dp` array and our potential solutions:

* Take the case where `x'` < `x''`, BUT `dp[x'']` < `dp[x']`. This basically means there is an inversion in the `dp` array. We can conclude that if `dp[y] - dp[x'] >= K` holds, then `dp[y] - dp[x''] >= K` will also hold. However, the distance between `y` and `x''` is smaller than the distance between `y` and `x'`, so the former is a better solution for our problem.
* After going through all the indices we need when trying to find the smallest distance between a left index (`x`) and a certain `y` such that `dp[y] - dp[x] >= k`, once this smallest distance is obtained, this left index (`x`) is useless to factor into any other distance calculation. This is because if we take a greater right index (`y`), the distance between our left index (`x`) and right index (`y`) will only increase.

In order to code this, we are going to need a queue. We should declare an instance of a `deque`. It is important, as we will later see, that this is a `deque` and not a `Queue` from our custom class as we want to do operations at both the beginning and the end of the queue. This deque will store the indices of `dp` , ensuring that the values in `dp` associated with each index (`dp[index]`) are increasing left to right.

How would you apply the above two bullet points to the deque?

* We want our indices in the deque ordered so that their respective values in `dp` are increasing. So, when we append (to the right) a right index (`y`) to the deque, we should remove all the indices for which their associated `dp` value is greater than `dp[y]`.
* When we find a left index (`x'`) for which `dp[y] - dp[x'] >= K` , we can safely ignore `x'` for future calculations. This means we can remove `x'` from our deque!

<hr/>

<sup>1 </sup> credit to https://www.youtube.com/watch?v=_JDpJXzTGbs for pointing out this relationship and for much of the explanation in this paragraph and above

<sup>2</sup> credit to https://leetcode.com/articles/shortest-subarray-with-sum-atleast-k/ for the rest of the explanation given in this document
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Find the Shortest Path in a Maze

Given a maze of the form of a rectangular matrix, develop an algorithm to find the length of the shortest path from a given source to a given destination.

The path can only be constructed out of cells having a value of `1` and, at any given moment, we can only move one step in one of the four directions. The valid moves are:

* Go Up: (x, y) --> (x - 1, y)
* Go Left: (x, y) --> (x, y - 1)
* Go Down: (x, y) --> (x + 1, y)
* Go Right: (x, y) --> (x, y + 1)

For example, consider the below matrix. If source = (0, 0) and destination = (7, 5), the shortest path from source to destination has length 12.

<img src = "../../Images/BFS_Maze.jpg" width = "400px">

Hints:

* BFS will be a useful algorithm to familiarize yourself with

* Initialize the following two arrays in your code

* ```python
row = [-1, 0, 0, 1]
col = [0, -1, 1, 0]
```

* Creating `row` and `col` as such allows us to represent the four possible moves (up, left, down, and right) that one can make.

* `row[0]` and `col[0]` taken together represent moving up.
* `row[1]` and `col[1]` taken together represent moving left.
* `row[2]` and `col[2]` taken together represent moving right.
* `row[3]` and `col[3]` taken together represent moving down.
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
### Solution:

```python
# Credit to https://www.techiedelight.com/lee-algorithm-shortest-path-in-a-maze/ for solution and problem.

# import the deque library
from collections import deque
import sys

# Global variables
# M x N matrix
M = 10;
N = 10;

# Below arrays details all 4 possible movements from a cell
row = [-1, 0, 0, 1]
col = [0, -1, 1, 0]

# queue node used in BFS
class Node:
# (x, y) represents matrix cell coordinates
# dist represents its minimum distance from the source
def __init__(self, x, y, dist):
self.x = x
self.y = y
self.dist = dist


# Function to check if it is possible to go to position (row, col)
# from current position. The function returns false if (row, col)
# is not a valid position or has value 0 or it is already visited
def isValid(mat, visited, row, col):
return ((row >= 0) and (row < M) and (col >= 0) and (col < N) and (mat[row][col]) and (not visited[row][col]))


# Find Shortest Possible Route in a matrix mat from source
# cell (i, j) to destination cell (x, y)
def BFS(mat, i, j, x, y):
# // construct a matrix to keep track of visited cells
# see https://www.geeksforgeeks.org/python-using-2d-arrays-lists-the-right-way/ for more details on how to initialize a 2D list in Python
# make sure to read above article to all the way to the end. it is possible a reader may be confused and get unanticipated
# results with their 2D list initialization if they do not read all the way to the end
# note that visited = [[False] * N] * M] does NOT work as intended (see article for more details)
visited = [[False for n in range(N)] for m in range(M)]

# create an empty queue
q = deque()

# mark source cell as visited and enqueue the source node
visited[i][j] = True
q.append(Node(i, j, 0))

# stores length of longest path from source to destination
min_dist = sys.maxsize

# run till queue is not empty
while q:
# pop front (left) node from queue and process it
node = q.popleft()

# (i, j) represents current cell and dist stores its
# minimum distance from the source
i = node.x
j = node.y
dist = node.dist


# if destination is found, update min_dist and stop
if i == x and j == y:
min_dist = dist
break

# check for all 4 possible movements from current cell
# and enqueue each valid movement
for k in range(4):
# check if it is possible to go to position
# (i + row[k], j + col[k]) from current position
if (isValid(mat, visited, i + row[k], j + col[k])):
visited[i + row[k]][j + col[k]] = True
q.append(Node(i + row[k], j + col[k], dist + 1))

if min_dist != sys.maxsize:
print("The shortest path from source to destination has length", min_dist)
else:
print("destination can't be reached from given source")

# Shortest path in a Maze
def main():

# input maze of size M x N
mat = [
[ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1 ],
[ 0, 1, 1, 1, 1, 1, 0, 1, 0, 1 ],
[ 0, 0, 1, 0, 1, 1, 1, 0, 0, 1 ],
[ 1, 0, 1, 1, 1, 0, 1, 1, 0, 1 ],
[ 0, 0, 0, 1, 0, 0, 0, 1, 0, 1 ],
[ 1, 0, 1, 1, 1, 0, 0, 1, 1, 0 ],
[ 0, 0, 0, 0, 1, 0, 0, 1, 0, 1 ],
[ 0, 1, 1, 1, 1, 1, 1, 1, 0, 0 ],
[ 1, 1, 1, 1, 1, 0, 0, 1, 1, 1 ],
[ 0, 0, 1, 0, 0, 1, 1, 0, 0, 1 ],
]

# Find shortest path from source (0, 0) to
# destination (7, 5)
BFS(mat, 0, 0, 7, 5)


main()
```
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Find the Shortest Path in a Maze

Rather than presenting the problem statement all together, we will present this problem in significant chunks, so that we can break down and analyze each part of the question.

## Problem Statement - Part 1

Given a maze of the form of a rectangular matrix, develop an algorithm to find the length of the shortest path from a given source to a given destination.

## Analysis

Here, we are given the general goal of our problem, without any of the specific implementation details that will be presented later. We should use this information to ensure that we have a foundational understanding of what we are required to do.

Let's break down the information line-by-line.

* "Given a maze of the form of a **rectangular matrix**"
* This is an indicates that the medium of our maze will be a 2D list. Immediately, you should ask yourself
* How does this impact the way we move through the maze? Can we move diagonally? Is there any restriction on where we start from and where we end?
* "develop an algorithm to find the length of the **shortest path** from a given **source to a given destination**."
* A light bulb should go off when you see the phrase "shortest path" You should ask yourself:
* What well-known algorithms have I seen in the past that use "shortest path" in their problem statement (hint: famous graph algorithms). What data structures do those algorithms use and will they be any use to me here (they may or may not)?
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Find the Shortest Path in a Maze

## Problem Statement - Part 2

The path can only be constructed out of cells having a value of `1` and, at any given moment, we can only move one step in one of the four directions. The valid moves are:

* Go Up: (x, y) --> (x - 1, y)
* Go Left: (x, y) --> (x, y - 1)
* Go Down: (x, y) --> (x + 1, y)
* Go Right: (x, y) --> (x, y + 1)

## Analysis

We are provided with a **lot** of useful information here. Most importantly, we now know how our matrix will be formatted and in what directions we can move.

At this point, you should ponder about what each of the coordinates for each of the respective moves represents. More specifically,

* Why do "Go Up" subtract 1 from the x-coordinate and "Go Down" add one to the x-coordinate. Why does neither impact the y-coordinate instead?
* Why do "Go Left" subtract 1 from the y-coordinate and "Go Right" add one to the y-coordinate. Why does neither impact the x-coordinate instead?

The above points should make you consider alternatives for storing the (x, y) coordinates. Should you store them as tuples? Should you have different arrays for the x coordinates and y coordinates? Does it matter?

This is a good example of when drawing a diagram of the problem setup (2D list) is helpful in visualizing the problem and seeing why certain properties hold. Although the next section will provide you with a diagram, you should get into the habit of making diagrams, especially considering that problem statements usually only provide the most basic diagrams and test cases.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Find the Shortest Path in a Maze

## Problem Statement - Part 3

For example, consider the below matrix. If source = (0, 0) and destination = (7, 5), the shortest path from source to destination has length 12.

<img src = "../../Images/BFS_maze.jpg" width = "400px">

## Analysis

Here, the problem statement provides us with an example input. For good practice, we should make our own test cases as well to see how our high-level ideas (NOT CODE - we have not gotten there yet!) for solutions to the problem work. Running your ideas on multiple test cases helps in finding flaws in your current thinking and helps in stimulating new ways of thinking about the problem.

Here is another example of a test case that you could come up with on your own.

```
Source = (1, 1) and destination = (4, 0) Path length = 6

[0 0 1 0]
[0 1 1 0]
[0 0 1 1]
[0 1 1 0]
[1 1 1 1]
```

Notice how this test case differs from the first one. This one has a rectangular (not square) matrix and the source is not (0, 0). You should try to come up with as many distinct test cases as possible.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Find the Shortest Path in a Maze

## Before We Start to Code

Now that we have broken down the question, seen an example test case, and come up with our own, we are ready to come up with a code-specific algorithm.

You should realize that

* For whatever move we make, we are going to have to check if its valid. We don't want to be allowed to go up when we are in the first row, we don't want to go left if in the first column, etc. This means we should have a `isValid()` function.
* We have to store which portions of the maze we have already visited. We also have to be able to fully "explore" a possible path. This should remind you of a certain, well-known graph algorithm.

Here are some specific hints about how to approach this problem:

* BFS will be a useful algorithm to familiarize yourself with

* Initialize the following two arrays in your code

* ```python
row = [-1, 0, 0, 1]
col = [0, -1, 1, 0]
```

* Creating `row` and `col` as such allows us to represent the four possible moves (up, left, down, and right) that one can make.

* `row[0]` and `col[0]` taken together represent moving up.
* `row[1]` and `col[1]` taken together represent moving left.
* `row[2]` and `col[2]` taken together represent moving right.
* `row[3]` and `col[3]` taken together represent moving down.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Find the Shortest Path in a Maze

## Code Analysis

After coding the problem yourself (or seeing the solution provided), it is important to do an analysis of your solution for time and space complexity.

### Time Complexity

The `isValid()` function is `O(1)`, so the time complexity is driven by our `BFS()` function. What is the worst case that this function can undergo? Well, we would require the most processing every single element of our 2D list.

Now that we have identified our worst case scenario, how would the processing of this scenerio change with the input size (as our input gets larger and larger)? Well, if we are enqueuing and processing every element of our matrix and the number of elements is always (`# of rows * # of columns`), that means our worst case processing should always be (`# of rows * # of columns`). That is, our worst case time complexity is `O(RC)`, where `R = # of rows` and `C = # of columns`.

### Space Complexity

For space complexity, we have to identify our data structure that requires the most space/memory. In this case, the matrix we create requires the most space and is of size `R*C`. Thus, our worst case space complexity is `O(RC)` as well!

Loading