diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/6.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/6.md new file mode 100644 index 00000000..9e22cba4 --- /dev/null +++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/6.md @@ -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): +``` \ No newline at end of file diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/7.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/7.md new file mode 100644 index 00000000..398a5a85 --- /dev/null +++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/7.md @@ -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)th and jth element of our original input array1. 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 points2 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! + +
+ + 1 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 + + 2 credit to https://leetcode.com/articles/shortest-subarray-with-sum-atleast-k/ for the rest of the explanation given in this document \ No newline at end of file diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/8.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/8.md new file mode 100644 index 00000000..9737ef5e --- /dev/null +++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/8.md @@ -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. + + + + 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. \ No newline at end of file diff --git a/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/9.md b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/9.md new file mode 100644 index 00000000..c7599e24 --- /dev/null +++ b/Data-Structures-and-Algos-Topic/Module1-Intro-to-Data-Structures-and-Algos/Activity3_Queues/cards/9.md @@ -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() + ``` \ No newline at end of file diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/Images/BFS_maze.jpg b/Module4.1_Intro_to_Data_Structures_and_Algos/Images/BFS_maze.jpg new file mode 100644 index 00000000..574b04d9 Binary files /dev/null and b/Module4.1_Intro_to_Data_Structures_and_Algos/Images/BFS_maze.jpg differ diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-1.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-1.md new file mode 100644 index 00000000..6154af74 --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-1.md @@ -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)? \ No newline at end of file diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-2.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-2.md new file mode 100644 index 00000000..d82e85f4 --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-2.md @@ -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. \ No newline at end of file diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-3.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-3.md new file mode 100644 index 00000000..5b00418b --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-3.md @@ -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. + + + +## 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. \ No newline at end of file diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-4.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-4.md new file mode 100644 index 00000000..a6cf51bc --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-4.md @@ -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. \ No newline at end of file diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-5.md b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-5.md new file mode 100644 index 00000000..8d03447a --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Interview-Question-5.md @@ -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! + diff --git a/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Shortest_Maze_Path_Interview_Ans.py b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Shortest_Maze_Path_Interview_Ans.py new file mode 100644 index 00000000..9d1b557b --- /dev/null +++ b/Module4.1_Intro_to_Data_Structures_and_Algos/activities/Act3_Queues/Shortest_Maze_Path_Interview_Ans.py @@ -0,0 +1,114 @@ +# Credit to https://www.techiedelight.com/lee-algorithm-shortest-path-in-a-maze/ + +# 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() + + + + + + + +