KK's blog

每天积累多一些

0%

LeetCode 329 Longest Increasing Path in a Matrix

Given an integer matrix, find the length of the longest increasing path.

From each cell, you can either move to four directions: left, right, up or down. You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed).

Example 1:

<pre>nums = [ [<font color="red">9</font>,9,4], [<font color="red">6</font>,6,8], [<font color="red">2</font>,<font color="red">1</font>,1] ] </pre>

Return 4
The longest increasing path is [1, 2, 6, 9].

Example 2:

<pre>nums = [ [<font color="red">3</font>,<font color="red">4</font>,<font color="red">5</font>], [3,2,<font color="red">6</font>], [2,2,1] ] </pre>

Return 4
The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed.

题目大意:

给定一个整数矩阵,计算其最长递增路径的长度。 从每一个单元格出发,你可以向四个方向移动:左右上下。你不可以沿着对角线移动也不能移出边界。(亦即,环绕是不允许的)。

Floyd解题思路:

这是经典题,类似于LIS题,只要将以每个点为终点的最长路径长度存起来,就可以类推它的邻近点的最长长度(DP)。f(x,y)=max{f(x,y),4个邻近点的f+1} 由于这是求最长路径题且为矩阵,可以考虑按步长计算,就是Floyd的思路,就是先计算步长为1,2一直到所以最长路径长度矩阵长度不再更新为止。
另一个思路也是用矩阵存起每个点最长路径长度,但用DFS搜索,直至这个点的值不为初始值为止,详见书影博客。

注意事项:

  1. 矩阵为空或长度为0
  2. 当任何值没有更新时,Floyd停止计算

Java代码:

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
37
38
39
40
41
42
43
44
45
public int longestIncreasingPath(int[][] matrix) {
if (matrix==null || matrix.length==0)
return 0;
int[][] path = new int[matrix.length][matrix[0].length];
for(int i=0;i<matrix.length;i++)
for(int j=0;j<matrix[0].length;j++)
path[i][j] = 1;

boolean nextPath = true;
while(nextPath){
nextPath = false;
for(int i=0;i<matrix.length;i++)
for(int j=0;j<matrix[0].length;j++){
if(changeCell(matrix, path, i, j))
nextPath = true;
}
}

int max = 1;
for(int i=0;i<matrix.length;i++)
for(int j=0;j<matrix[0].length;j++)
if(path[i][j]>max)
max = path[i][j];
return max;
}

public boolean changeCell(int[][] matrix, int[][] path, int i, int j){
int pre = path[i][j];

if(i-1>=0 && matrix[i-1][j]<matrix[i][j] && path[i-1][j]+1>path[i][j])
path[i][j] = path[i-1][j]+1;

if(i+1<matrix.length && matrix[i+1][j]<matrix[i][j] && path[i+1][j]+1>path[i][j])
path[i][j] = path[i+1][j]+1;

if(j-1>=0 && matrix[i][j-1]<matrix[i][j] && path[i][j-1]+1>path[i][j])
path[i][j] = path[i][j-1]+1;

if(j+1<matrix[0].length && matrix[i][j+1]<matrix[i][j] && path[i][j+1]+1>path[i][j])
path[i][j] = path[i][j+1]+1;

if(pre!=path[i][j])
return true;
else return false;
}

算法分析:

k为最长路径长度,时间复杂度为<code>O(k_n_<sup>2</sup>)</code>,空间复杂度<code>O(n<sup>2</sup>)</code>。


DP算法II解题思路(推荐):

求最值,考虑用DP,但DP的应用条件为有序,所以不妨将所有值排序。
先对所有数的值排序作为新的输入数组,按这个顺序,计算DP,每个点的往前4个方向的DP值+1。
如题第一个例子, 元祖里第一个为值,后两个为xy坐标
[(1, 2, 1), (1, 2, 2)...] 递归公式:

1
dp[x][y] = dp[x + dx][y + dy] + 1, ifmatrix[x][y] > matrix[x + dx][y + dy]:
最后计算max(dp)

注意事项:

  1. 当递增时,才更新DP,Line 13

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def longestIncreasingPath(self, matrix: List[List[int]]) -> int:
ary = []
for i in range(len(matrix)):
for j in range(len(matrix[0])):
ary.append((matrix[i][j], i, j))
ary.sort()
dp = [[1 for _ in range(len(matrix[0]))] for _ in range(len(matrix))]
for val, _x, _y in ary:
for _dx, _dy in OFFSETS:
x, y = _x + _dx, _y + _dy
if x < 0 or x >= len(matrix) or y < 0 or y >= len(matrix[0]):
continue
if matrix[x][y] > matrix[_x][_y]:
dp[x][y] = max(dp[x][y], dp[_x][_y] + 1)
return max(map(max, dp))

算法分析:

排序是n^2logn^2 = n^2logn, dp计算是n^2 所以时间复杂度为<code>O(n<sup>2</sup>logn)</code>,空间复杂度<code>O(n<sup>2</sup>)</code>


算法III解题思路:

这题也可以额用记忆性搜索DFS来解。

LeetCode

<div>

You are given an m x n grid grid of values 0, 1, or 2, where:

  • each 0 marks an empty land that you can pass by freely,
  • each 1 marks a building that you cannot pass through, and
  • each 2 marks an obstacle that you cannot pass through.

You want to build a house on an empty land that reaches all buildings in the shortest total travel distance. You can only move up, down, left, and right.

Return the shortest travel distance for such a house. If it is not possible to build such a house according to the above rules, return -1.

The total travel distance is the sum of the distances between the houses of the friends and the meeting point.

The distance is calculated using Manhattan Distance, where distance(p1, p2) = |p2.x - p1.x| + |p2.y - p1.y|.

Example 1:

<pre>Input: grid = [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]] Output: 7 Explanation: Given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2). The point (1,2) is an ideal empty land to build a house, as the total travel distance of 3+3+1=7 is minimal. So return 7. </pre>

Example 2:

<pre>Input: grid = [[1,0]] Output: 1 </pre>

Example 3:

<pre>Input: grid = [[1]] Output: -1 </pre>

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 50
  • grid[i][j] is either 0, 1, or 2.
  • There will be at least one building in the grid.

</div>

题目大意:

找到所有大厦最短距离的点,这个点不能是大厦也不能是障碍

解题思路:

一开始觉得类似于LeetCode 296 Best Meeting Point,但由于有障碍,所以不能用贪婪法。最值考虑用BFS。属于对所有节点BFS。类似于LeetCode 200 Number of Islands,
从每一栋大厦开始做BFS,计算每个点到此大厦距离。然后对累计到这个点的总距离矩阵dis中。最后求距离矩阵的最小值。

此题难点在于-1的情况,也就是一个点不能到达其中一个building或者是这个building不能到达的点。所以要再用一个矩阵house_count来记录每一个点能到达的大厦数。

解题步骤:

N/A

注意事项:

  1. -1的情况,用一个矩阵house_count来记录每一个点能到达的大厦数。
  2. 用常数记录矩阵长和宽,不用x >= len(grid) or y >= len(grid[0]), 否则会TLE

Python代码:

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
OFFSETS = [(-1, 0), (1, 0), (0, -1), (0, 1)]
def shortestDistance(self, grid: List[List[int]]) -> int:
dis = [[0 for _ in range(len(grid[0]))] for _ in range(len(grid))]
house_count = [[0 for _ in range(len(grid[0]))] for _ in range(len(grid))]
total_houses = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == 1:
self.bfs(grid, i, j, dis, house_count)
total_houses += 1
min_dis = float('inf') # remember
for i in range(len(dis)):
for j in range(len(dis[0])):
if dis[i][j] > 0 and house_count[i][j] == total_houses:
min_dis = min(min_dis, dis[i][j])
return -1 if min_dis == float('inf') else min_dis # remember

def bfs(self, grid, start_x, start_y, dis, house_count):
h = len(grid)
w = len(grid[0]) # remember otherwise TLE
queue = collections.deque([(start_x, start_y, 0)])
visited = [[False for _ in range(len(grid[0]))] for _ in range(len(grid))]
visited[start_x][start_y] = True
while queue:
node = queue.popleft()
for _dx, _dy in OFFSETS:
x, y = node[0] + _dx, node[1] + _dy
if x < 0 or x >= h or y < 0 or y >= w or grid[x][y] != 0 or visited[x][y]:
continue
queue.append((x, y, node[2] + 1))
visited[x][y] = True
dis[x][y] += node[2] + 1
house_count[x][y] += 1

算法分析:

时间复杂度为O(nm * nm),空间复杂度O(nm)

LeetCode

<div>

Given an integer array nums and an integer k, return the maximum length of a subarray that sums to k. If there is not one, return 0 instead.

Example 1:

<pre>Input: nums = [1,-1,5,-2,3], k = 3 Output: 4 Explanation: The subarray [1, -1, 5, -2] sums to 3 and is the longest. </pre>

Example 2:

<pre>Input: nums = [-2,-1,2,1], k = 1 Output: 2 Explanation: The subarray [-1, 2] sums to 1 and is the longest. </pre>

Constraints:

  • 1 <= nums.length <= 2 * 10<sup>5</sup>
  • -10<sup>4</sup> <= nums[i] <= 10<sup>4</sup>
  • -10<sup>9</sup> <= k <= 10<sup>9</sup>

</div>

题目大意:

最长子数组和等于k,求长度

解题思路:

一开始以为类似于LeetCode 209 Minimum Size Subarray Sum用同向双指针。但这题不是连续,因为此题含负数,不会连续大于等于target。考虑用presum的two sum法。

解题步骤:

N/A

注意事项:

  1. 加入presum[0] = 0,因为这样才可以得到以首元素开始的子数组和
  2. 若presum已经在hashmap中了,不要加入,因为要保证最长数组,如 [-1, 1], target = 0, index可以为0, 2
  3. max_len答案只要初始化为0,不用最小值,因为最大长度必为非负

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
# presum[i] - presum[j] = k
def maxSubArrayLen(self, nums: List[int], k: int) -> int:
max_len, presum = 0, 0 # not float('-inf')
sum_to_idx = collections.defaultdict(int)
sum_to_idx[0] = 0
for i in range(len(nums)):
presum += nums[i]
if presum - k in sum_to_idx:
max_len = max(max_len, i - sum_to_idx[presum - k] + 1)
if presum not in sum_to_idx: # remember
sum_to_idx[presum] = i + 1
return max_len

算法分析:

时间复杂度为O(n),空间复杂度O(n)

LeetCode

<div>

Given the head of a singly linked list, group all the nodes with odd indices together followed by the nodes with even indices, and return the reordered list.

The first node is considered odd, and the second node is even, and so on.

Note that the relative order inside both the even and odd groups should remain as it was in the input.

You must solve the problem in O(1) extra space complexity and O(n) time complexity.

Example 1:

<pre>Input: head = [1,2,3,4,5] Output: [1,3,5,2,4] </pre>

Example 2:

<pre>Input: head = [2,1,3,5,6,4,7] Output: [2,3,6,7,1,5,4] </pre>

Constraints:

  • n ==number of nodes in the linked list
  • 0 <= n <= 10<sup>4</sup>
  • -10<sup>6</sup> <= Node.val <= 10<sup>6</sup>

</div>

题目大意:

重排LL, 先偶位再奇位

解题思路:

N/A

解题步骤:

N/A

注意事项:

  1. LL四点注意事项: 删除节点.next = None
  2. 空输入特别处理

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def oddEvenList(self, head: ListNode) -> ListNode:
if not head: # remember
return None
odd_head = ListNode(0)
it, it_odd = head, odd_head
while it.next:
it_odd.next = it.next
it.next = it.next.next
if it.next:
it = it.next
it_odd = it_odd.next
it_odd.next = None # remember
it.next = odd_head.next
return head

算法分析:

时间复杂度为O(n),空间复杂度O(1)

LeetCode

<div>

Assume the following rules are for the tic-tac-toe game on an n x n board between two players:

  1. A move is guaranteed to be valid and is placed on an empty block.
  2. Once a winning condition is reached, no more moves are allowed.
  3. A player who succeeds in placing n of their marks in a horizontal, vertical, or diagonal row wins the game.

Implement the TicTacToe class:

  • TicTacToe(int n) Initializes the object the size of the board n.
  • int move(int row, int col, int player) Indicates that the player with id player plays at the cell (row, col) of the board. The move is guaranteed to be a valid move.

Example 1:

<pre>Input ["TicTacToe", "move", "move", "move", "move", "move", "move", "move"] [[3], [0, 0, 1], [0, 2, 2], [2, 2, 1], [1, 1, 2], [2, 0, 1], [1, 0, 2], [2, 1, 1]] Output [null, 0, 0, 0, 0, 0, 0, 1]

Explanation TicTacToe ticTacToe = new TicTacToe(3); Assume that player 1 is "X" and player 2 is "O" in the board. ticTacToe.move(0, 0, 1); // return 0 (no one wins) |X| | | | | | | // Player 1 makes a move at (0, 0). | | | |

ticTacToe.move(0, 2, 2); // return 0 (no one wins) |X| |O| | | | | // Player 2 makes a move at (0, 2). | | | |

ticTacToe.move(2, 2, 1); // return 0 (no one wins) |X| |O| | | | | // Player 1 makes a move at (2, 2). | | |X|

ticTacToe.move(1, 1, 2); // return 0 (no one wins) |X| |O| | |O| | // Player 2 makes a move at (1, 1). | | |X|

ticTacToe.move(2, 0, 1); // return 0 (no one wins) |X| |O| | |O| | // Player 1 makes a move at (2, 0). |X| |X|

ticTacToe.move(1, 0, 2); // return 0 (no one wins) |X| |O| |O|O| | // Player 2 makes a move at (1, 0). |X| |X|

ticTacToe.move(2, 1, 1); // return 1 (player 1 wins) |X| |O| |O|O| | // Player 1 makes a move at (2, 1). |X|X|X| </pre>

Constraints:

  • 2 <= n <= 100
  • player is 1 or 2.
  • 0 <= row, col < n
  • (row, col) are unique for each different call to move.
  • At most n<sup>2</sup> calls will be made to move.

Follow-up: Could you do better than O(n<sup>2</sup>) per move() operation?

</div>

题目大意:

设计井字过三关

解题思路:

游戏题。最重要是是数据结构,类似于LeetCode 051 N-Queens和LeetCode 037 Sudoku Solver用matrix记录每行,每列,对角线和反对角线的和。这样验证时候只需要O(1).

解题步骤:

N/A

注意事项:

  1. 对角线和反对角线只有一条,所以要先判断move的这个点是否在对角线上。
  2. 由于用-1来代表某一个player,所以判断和时候,用abs

Python代码:

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
class TicTacToe(TestCases):

def __init__(self, n: int):
self.board_len = n
self.row = [0] * n
self.col = [0] * n
self.diag = 0
self.anti_diag = 0

def move(self, row: int, col: int, player: int) -> int:
if player == 2:
player = -1
self.row[row] += player
self.col[col] += player
if row == col: # remember
self.diag += player
if row == self.board_len - 1 - col:
self.anti_diag += player
does_win = abs(self.row[row]) == self.board_len or abs(self.col[col]) == self.board_len or \
abs(self.diag) == self.board_len or abs(self.anti_diag) == self.board_len # remember abs
if does_win:
if player == -1:
return 2
else:
return player
return 0

算法分析:

move时间复杂度为O(1),空间复杂度O(n)

Free mock interview