leetcode.com/contest/weekly-contest-212
leetcode.com/problems/slowest-key/
class Solution:
def slowestKey(self, releaseTimes: List[int], keysPressed: str) -> str:
#Time complexity = O(n)
#Space complexity = O(1)
maxletter = keysPressed[0]
maxvalue = releaseTimes[0]
for i in range(1, len(releaseTimes)):
value = releaseTimes[i] - releaseTimes[i-1]
if value > maxvalue:
maxvalue = value
maxletter = keysPressed[i]
elif value == maxvalue:
maxletter = max(maxletter, keysPressed[i])
return maxletter
leetcode.com/problems/arithmetic-subarrays/
sort 이용하지말고 정수론 이용해서 짜야할듯
O(m x NlogN) -> O(MN)
leetcode.com/problems/arithmetic-subarrays/discuss/909120/python3-using-set-no-sort-O(MN)
def checkArithmeticSubarrays(self, nums: List[int], l: List[int], r: List[int]) -> List[bool]:
def isArith(n):
st = set(n)
if len(n) != len(st): return len(st) == 1 # take care of duplicates
mx, mn, = max(n), min(n)
if (mx - mn)%(len(n)-1) != 0: return False
step = (mx - mn)//(len(n)-1)
for i in range(mn, mx, step):
if i not in st:
return False
return True
return [isArith(nums[start:stop+1]) for start, stop in zip(l,r)]
leetcode.com/problems/path-with-minimum-effort/
구글기출
이 문제를 처음 봤을때 생각난 백트랙킹.. TLE ..
Consider the grid as a graph, where adjacent cells have an edge with cost of the difference between the cells.
Hide Hint 2
If you are given threshold k, check if it is possible to go from (0, 0) to (n-1, m-1) using only edges of ≤ k cost.
Show Hint 3
Binary search the k value.
The problem is that given a 2D matrix \text{heights}heights of size \text{row} \cdot \text{col}row⋅col representing the height of each cell, we have travel from top right corner (0, 0) to bottom right corner (row-1, col-1) of the matrix and find a path with minimum effort. The effort to move from one cell to another is the absolute difference in the heights of those cells.
The following example illustrates the effort required to travel from cell A to all the neighboring cells B,C, D, and F. The first matrix represents the heights of each cell and the second represents the absolute difference from cell A to all the adjacent cells. The path with minimum effort is from cell A to cell E.
Let's understand the different approaches to implement the solution in detail.
Intuition
The brute force approach would be to traverse all the possible paths from the source cell to the destination cell and track the path with minimum efforts. To try all possible paths, the first thing that comes in our mind is Backtracking. Backtracking incrementally builds the candidates for a solution using depth first search and discards the candidates (backtrack) if it doesn't satisfy the condition.
The backtracking algorithms consists of the following steps,
To make the algorithm more efficient, once we find any path from source to destination, we track the maximum absolute difference of all adjacent cells in that path in a variable \text{maxSoFar}maxSoFar. With this, we can avoid going into other paths in the future where effort is greater than or equal to \text{maxSoFar}maxSoFar.
In other words, if we have already found a path to reach the destination cell with \text{maxSoFar}maxSoFar, then, we would only explore other paths if it takes efforts less than \text{maxSoFar}maxSoFar.
Algorithm
We must begin the Depth First Search traversal from source cell (x = 0 and y = 0). Using the intuition discussed above, we must explore all the potential paths using the following steps,
Thus, for each cell, we recursively calculate the effort required to reach the destination cell from all the adjacent cells and find the minimum effort.
Note
It must be noted that we mark the current cell as visited by setting the height of the current cell (x,y) as 0. We must update the height back to the previous value once we backtrack from the current path. This is necessary because the cell must be visited again for other paths.
Implementation
class Solution:
def minimumEffortPath(self, heights: List[List[int]]) -> int:
r = len(heights)
c = len(heights[0])
self.max_so_far = math.inf
def dfs(x, y, max_diff):
if x == r-1 and y == c-1:
self.max_so_far = min(self.max_so_far, max_diff)
return max_diff
curr_height = heights[x][y]
min_effort = math.inf
heights[x][y] = -1
for dx, dy in [[0, 1], [1, 0], [0, -1], [-1, 0]]:
next_x = x + dx
next_y = y + dy
if 0 <= next_x < r and 0 <= next_y < c and heights[next_x][next_y] != -1:
curr_diff = max(max_diff, abs(heights[next_x][next_y] - curr_height))
if curr_diff < self.max_so_far:
result = dfs(next_x, next_y, curr_diff)
min_effort = min(min_effort, result)
heights[x][y] = curr_height
return min_effort
return dfs(0, 0, 0)
Complexity Analysis
Let mm be the number of rows and nn be the number of columns in the matrix \text{heights}heights.
The time complexity is exponential, hence this approach is exhaustive and results in Time Limit Exceeded (TLE).
Intuition
The previous approach is exhaustive as it traverses all the paths. If we observe, the problem is similar to finding the shortest path from a source cell to a destination cell. Here, the shortest path is the one with minimum absolute difference between every adjacent cells in that path. Also, since there is height associated with each cell, simple BFS traversal won't be sufficient.
The absolute difference between adjacent cells A and B can be perceived as the weight of an edge from cell A to cell B. Thus, we could use Dijkstra's Algorithm which is used to find the shortest path in a weighted graph with a slight modification of criteria for the shortest path.
Let's look at the algorithm in detail.
Algorithm
We use a differenceMatrix of size \text{row} \cdot \text{col}row⋅col where each cell represents the minimum effort required to reach that cell from all the possible paths. Also, initialize we all the cells in the differenceMatrix to infinity \text{(MAX\_INT)}(MAX_INT) since none of the cells are reachable initially.
As we start visiting each cell, all the adjacent cells are now reachable. We update the absolute difference between the current cell and adjacent cells in the differenceMatrix. At the same time, we also push all the adjacent cells in a priority queue. The priority queue holds all the reachable cells sorted by its value in differenceMatrix, i.e the cell with minimum absolute difference with its adjacent cells would be at the top of the queue.
We begin by adding the source cell (x=0, y=0) in the queue. Now, until we have visited the destination cell or the queue is not empty, we visit each cell in the queue sorted in the order of priority. The less is the difference value(absolute difference with adjacent cell) of a cell, the higher is its priority.
Get the cell from the top of the queue curr and visit the current cell.
For each of the 4 cells adjacent to the current cell, calculate the maxDifference which is the maximum absolute difference to reach the adjacent cell (adjacentX, adjacentY) from current cell (curr.x, curr.y).
If the current value of the adjacent cell (adjacentX, adjacentY) in the difference matrix is greater than the maxDifference, we must update that value with maxDifference. In other words, we have found that the path from the current cell to the adjacent cell takes lesser efforts than the other paths that have reached the adjacent cell so far. Also, we must add this updated difference value in the queue.
Ideally, for updating the priority queue, we must delete the old value and reinsert with the new maxDifference value. But, as we know that the updated maximum value is always lesser than the old value and would be popped from the queue and visited before the old value, we could save time and avoid removing the old value from the queue.
1 / 8
Implementation
Complexity Analysis
Time Complexity : \mathcal{O}(m \cdot n \log (m \cdot n))O(m⋅nlog(m⋅n)), where mm is the number of rows and nn is the number of columns in matrix \text{heights}heights. It will take \mathcal{O}(m \cdot n)O(m⋅n) time to visit every cell in the matrix. The priority queue will contain at most m \cdot nm⋅n cells, so it will take \mathcal{O}(\log (m \cdot n))O(log(m⋅n)) time to re-sort the queue after every adjacent cell is added to the queue. This given as total time complexiy as \mathcal{O}(m \cdot n \log(m \cdot n))O(m⋅nlog(m⋅n)).
Space Complexity: \mathcal{O}(m \cdot n)O(m⋅n), where mm is the number of rows and nn is the number of columns in matrix \text{heights}heights. The maximum queue size is equal to the total number of cells in the matrix \text{height}height which is given by m \cdot nm⋅n. Also, we use a difference matrix of size m \cdot nm⋅n. This gives as time complexity as \mathcal{O}(m \cdot n + m \cdot n)O(m⋅n+m⋅n) = \mathcal{O}(m \cdot n)O(m⋅n)
Intuition
Using Disjoint Set is another intuitive way to solve the problem. Each cell in the matrix is a single node/component in a graph. The path from the current cell to adjacent cells is an edge connecting the 2 cells. Using this intuition, we could use Union Find algorithm to form a connected component from the source cell to the destination cell.
Initially, every cell is a disconnected component and we aim to form a single connected component that connects the source cell to the destination cell. Each connected component connects multiple cells and is identified by a parent. We must continue connecting components until the source cell and destination cell shares the same parent.
The union find algorithm performs 2 operations,
Find(x): Returns the parent of the connected component to which x belongs.
Union(x, y): Merges the two disconnected components to which x and y belongs.
To efficiently implement the above operations, we could use Union By Rank and Path Compression strategy.
Algorithm
Start iterating over the sorted edge list and connect each edge to form a connected component using Union Find Algorithm.
After every union, check if the source cell (0) and destination cell (row * col - 1) are connected. If yes, the absolute difference between the current edge is our result. Since we access the edges in increasing order of difference, and the current edge connected the source and destination cell, we are sure that the current difference is the maximum absolute difference in our path with minimum efforts.
Implementation
Complexity Analysis
Let mm be the number of rows and nn be the number of columns of the matrix \text{height}height.
For every edge, we find the parent of each cell and perform the union (Union Find). For nn elements, the time complexity of Union Find is \log nlogn. (Refer Proof Of Time Complexity Of Union Find). Thus for m \cdot nm⋅n cells, the time taken to perform Union Find would be \log m \cdot nlogm⋅n. This gives us total time complexity as, \mathcal{O}(m\cdot n(\log(m\cdot n)))O(m⋅n(log(m⋅n))).
Intuition
Our aim to find the minimum effort required to travel from source cell to destination cell. We know from the given constraints that the maximum height could be 10^6 (1000000)106(1000000). So we know that our required absolute difference values would between 00 and 10^6106. We could use Binary Search and reduce our search space by half.
Given the lower bound as 00 and upper bound as 10^6106, we could repeatedly calculate the middle value. Let this middle value be mid. We could divide our search space based on the following condition,
To find if there exists a path from the source cell to the destination cell for a given mid value, we could use simple graph traversal. In this approach, we use Breath First Search traversal.
Algorithm
Intialize the lower bound left as 00 and upper bound right as 10^6106. Calculate the middle value mid of the left and right value.
Using Breath First Search, check if there exists a path from source cell (x=0, y=0) to destination cell (x=row-1, y=column-1) with effort less than or equal to mid using method canReachDestination which returns a boolean value.
If there exists a path from the source cell to the destination cell, we must update the result value as a minimum of the current result and mid and continue the search in the range between left and mid-1. For this, we must update the value of right to mid-1.
Otherwise, we must search in the range between mid+1 and right and update left to mid+1.
Implementation
Complexity Analysis
Let mm be the number of rows and nn be the number of columns for the matrix \text{height}height.
Binary Search:To perform Binary search on numbers in range (0.. 10^{6})(0..106), the time taken would be \mathcal{O}(\log 10^{6})O(log106).
Breath First Search: The time complexity for the Breadth First Search for vertices V and edges E is \mathcal{O}(V+E)O(V+E) (Refer) Thus, in the matrix of size m \cdot nm⋅n, with m \cdot nm⋅n vertices and m \cdot nm⋅n edges (Refer time complexity of Approach 3), the time complexity to perform Breath First Search would be \mathcal{O}(m \cdot n + m \cdot n)O(m⋅n+m⋅n) = \mathcal{O}(m \cdot n)O(m⋅n).
This gives us total time complexity as \mathcal{O}(\log10^{6}\cdot(m \cdot n))O(log106⋅(m⋅n)) which is equivalent to \mathcal{O}(m \cdot n)O(m⋅n).
Intuition and Algorithm
The solution is similar to Approach 4. Except that, here, we use a Depth First Search traversal to find if there exists a path from the source cell to destination cell for a given value middle value mid.
Implementation
Complexity Analysis
Time Complexity : \mathcal{O}(m \cdot n)O(m⋅n). As in Approach 4. The only difference is that we are using Depth First Search instead of Breadth First Search and have similar time complexity.
Space Complexity: \mathcal{O}(m \cdot n)O(m⋅n), As in Approach 4. In Depth First Search, we use the internal call stack (instead of the queue in Breadth First Search).
관련문제 미디움
leetcode.com/problems/path-with-maximum-minimum-value/
leetcode.com/problems/rank-transform-of-a-matrix/
구글옛날기출
관련문제 이지
leetcode.com/problems/rank-transform-of-an-array/
def arrayRankTransform(self, A):
rank = {}
for a in sorted(A):
rank.setdefault(a, len(rank) + 1)
return map(rank.get, A)
def arrayRankTransform(self, A):
return map({a: i + 1 for i, a in enumerate(sorted(set(A)))}.get, A)