KK's blog

每天积累多一些

0%

LeetCode

<div>

You have a queue of integers, you need to retrieve the first unique integer in the queue.

Implement the FirstUnique class:

  • FirstUnique(int[] nums) Initializes the object with the numbers in the queue.
  • int showFirstUnique() returns the value of the first unique integer of the queue, and returns -1 if there is no such integer.
  • void add(int value) insert value to the queue.

Example 1:

<pre>Input: ["FirstUnique","showFirstUnique","add","showFirstUnique","add","showFirstUnique","add","showFirstUnique"] [[[2,3,5]],[],[5],[],[2],[],[3],[]] Output: [null,2,null,2,null,3,null,-1] Explanation: FirstUnique firstUnique = new FirstUnique([2,3,5]); firstUnique.showFirstUnique(); // return 2 firstUnique.add(5); // the queue is now [2,3,5,5] firstUnique.showFirstUnique(); // return 2 firstUnique.add(2);            // the queue is now [2,3,5,5,2] firstUnique.showFirstUnique(); // return 3 firstUnique.add(3);            // the queue is now [2,3,5,5,2,3] firstUnique.showFirstUnique(); // return -1 </pre>

Example 2:

<pre>Input: ["FirstUnique","showFirstUnique","add","add","add","add","add","showFirstUnique"] [[[7,7,7,7,7,7]],[],[7],[3],[3],[7],[17],[]] Output: [null,-1,null,null,null,null,null,17] Explanation: FirstUnique firstUnique = new FirstUnique([7,7,7,7,7,7]); firstUnique.showFirstUnique(); // return -1 firstUnique.add(7); // the queue is now [7,7,7,7,7,7,7] firstUnique.add(3);            // the queue is now [7,7,7,7,7,7,7,3] firstUnique.add(3);            // the queue is now [7,7,7,7,7,7,7,3,3] firstUnique.add(7);            // the queue is now [7,7,7,7,7,7,7,3,3,7] firstUnique.add(17);           // the queue is now [7,7,7,7,7,7,7,3,3,7,17] firstUnique.showFirstUnique(); // return 17 </pre>

Example 3:

<pre>Input: ["FirstUnique","showFirstUnique","add","showFirstUnique"] [[[809]],[],[809],[]] Output: [null,809,null,-1] Explanation: FirstUnique firstUnique = new FirstUnique([809]); firstUnique.showFirstUnique(); // return 809 firstUnique.add(809); // the queue is now [809,809] firstUnique.showFirstUnique(); // return -1 </pre>

Constraints:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^8
  • 1 <= value <= 10^8
  • At most 50000 calls will be made to showFirstUnique and add.

</div>

算法思路:

Dict + LL (1次) + Set (2次)。此题非常类似于LRU, 相当于实习LinkedHashMap。

由易到难

题目 LL Map 其他数据结构
LRU 按时间顺序,头删尾入 值->LL节点 N/A
First Unique 按时间顺序,任意删尾入 值->LL节点 Set存两次以上
LFU 多个时间顺序的LL 值->LL节点 第二个Map存freq-> LL的首节点以及min_freq

注意事项:

  1. 注意两种情况,节点不在Map, 在Map中,对应的LL操作同样是add_to_tail和remove_node不过少了move_to_tail。
  2. 头尾dummy node,初始化要相连。
  3. 注意删除顺序,先删map中的entry再删Node。否则会出现NPE。新加入是顺序相反。删除节点要将prev和next赋None
  4. 区别还有ListNode不需要key,因为输入是单值,不是key-value对。数据结构多了set来记录出现两次及以上的元素,直接忽略

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
class FirstUnique(TestCases):

'''
2, 3, 5, 5
dict:
{2:1
3:1
5:2 (remove)
}
LinkedList: 2, 3, 5(r), 5(r)
dict:
{
2: Node(2)
3: Node(3)
5: Node(5)
}

'''

def __init__(self, nums: List[int]):
self.head = ListNode(0) # only store unique elements
self.tail = ListNode(0)
self.key_to_node = {} # only store unique elements
self.non_unique_set = set()
self.head.next, self.tail.prev = self.tail, self.head

for n in nums:
self.add(n)

def showFirstUnique(self) -> int:
if len(self.key_to_node) == 0:
return -1
return self.head.next.val
# 2, 3, 5, 5
def add(self, value: int) -> None:
if value in self.non_unique_set:
return

if value not in self.key_to_node:
new_node = ListNode(value)
self.add_to_tail(new_node)
self.key_to_node[value] = new_node
else:
to_be_removed_node = self.key_to_node[value]
self.key_to_node.pop(value)
self.remove_node(to_be_removed_node)
self.non_unique_set.add(value)

def add_to_tail(self, node):
predecessor = self.tail.prev
predecessor.next, node.prev = node, predecessor
node.next, self.tail.prev = self.tail, node

def remove_node(self, node):
predecessor, successor = node.prev, node.next
predecessor.next, successor.prev = successor, predecessor
node.prev, node.next = None, None


class ListNode:

def __init__(self, val, next = None, prev = None):
self.val = val
self.next = next
self.prev = prev

算法分析:

每个操作时间复杂度为O(1),空间复杂度O(n).

LeetCode

<div>

You are given an array of k linked-lists lists, each linked-list is sorted in ascending order.

Merge all the linked-lists into one sorted linked-list and return it.

Example 1:

<pre>Input: lists = [[1,4,5],[1,3,4],[2,6]] Output: [1,1,2,3,4,4,5,6] Explanation: The linked-lists are: [ 1->4->5, 1->3->4, 2->6 ] merging them into one sorted list: 1->1->2->3->4->4->5->6 </pre>

Example 2:

<pre>Input: lists = [] Output: [] </pre>

Example 3:

<pre>Input: lists = [[]] Output: [] </pre>

Constraints:

  • k == lists.length
  • 0 <= k <= 10^4
  • 0 <= lists[i].length <= 500
  • -10^4 <= lists[i][j] <= 10^4
  • lists[i] is sorted in ascending order.
  • The sum of lists[i].length won't exceed 10^4.

</div>

算法思路:

N/A

注意事项:

  1. heap不能比较ListNode大小,要实现__lt__函数或者将(node.val, node)对加入到heap中(此法Leetcode有编译错误但PyCharm可过)
  2. 调用heappush时,记得查节点是否None,无论循环里还是初始化
  3. 出堆后node的next赋None不需要,因为每个节点next都会重复赋值,而最后一个节点本来也没有next

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
ListNode.__lt__ = lambda x, y: x.val < y.val
class Solution:
def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
heap = []
fake_head = ListNode(0)
for head in lists:
if head:
heappush(heap, head)
it = fake_head
while heap:
node = heappop(heap)
if node.next:
heappush(heap, node.next)
# node.next = None
it.next = node
it = it.next
return fake_head.next

算法分析:

时间复杂度为O(nlogk),空间复杂度O(k).


算法II解题思路:

Devide and Conquer

注意事项:

  1. k.next = None因为删除节点,所以赋None,这句不加也行,但作为良好习惯建议加,且不能在i = i.next前加,否则i会变空。
  2. 查lists是否为空

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
def mergeKLists2(self, lists: List[List['ListNode']]) -> 'ListNode':
if not lists:
return None
return self.merge_sort(lists, 0, len(lists) - 1)

def merge_sort(self, lists, start, end):
if start >= end:
return lists[start]
mid = start + (end - start) // 2
li = self.merge_sort(lists, start, mid)
li2 = self.merge_sort(lists, mid + 1, end)
return self.merge_two_lists(li, li2)

def merge_two_lists(self, li, li2):
i, j, res = li, li2, ListNode(0)
k = res
while i and j:
if i.val < j.val:
k.next = i
i = i.next
k = k.next
k.next = None
else:
k.next = j
j = j.next
k = k.next
k.next = None
if i:
k.next = i
if j:
k.next = j
return res.next

算法分析:

不管多少次递归,每次递归的一层总的节点数为n,而对k做二分,所以递归数为logk, 时间复杂度为O(nlogk),空间复杂度O(1).


算法III解题思路:

算法二的迭代法

注意事项:

  1. 输入大小的奇偶处理
  2. 返回值是lists[0]而不是lists

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def mergeKLists3(self, lists: List[List['ListNode']]) -> 'ListNode':
if not lists:
return None
while len(lists) > 1:
tmp = []
if len(lists) % 2 == 1:
lists.append([])
for i in range(0, len(lists), 2):
tmp.append(self.merge_two_lists(lists[i], lists[i + 1]))
lists = tmp
return lists[0]

算法分析:

同算法二.

LeetCode

<div>

There is a robot on an m x n grid. The robot is initially located at the top-left corner (i.e., grid[0][0]). The robot tries to move to the bottom-right corner (i.e., grid[m - 1][n - 1]). The robot can only move either down or right at any point in time.

Given the two integers m and n, return the number of possible unique paths that the robot can take to reach the bottom-right corner.

The test cases are generated so that the answer will be less than or equal to 2 * 10<sup>9</sup>.

Example 1:

<pre>Input: m = 3, n = 7 Output: 28 </pre>

Example 2:

<pre>Input: m = 3, n = 2 Output: 3 Explanation: From the top-left corner, there are a total of 3 ways to reach the bottom-right corner: 1. Right -> Down -> Down 2. Down -> Down -> Right 3. Down -> Right -> Down </pre>

Constraints:

  • 1 <= m, n <= 100

</div>

题目大意:

求矩阵路径总数

解题思路:

求个数用DP,递归式:

1
dp[i][j] = dp[i-1][j] + dp[i][j-1]

解题步骤:

N/A

注意事项:

  1. 初始值dp[1] = 1而不是dp[0] = 1因为第二行的第一格不能加左边的虚拟格=1
  2. range(m)不是range(len(m))
  3. 优化空间用一维

Python代码:

1
2
3
4
5
6
7
def uniquePaths(self, m: int, n: int) -> int:
dp = [0] * (n + 1)
dp[1] = 1 # remember not dp[0] = 1
for i in range(m): # remember no len(m)
for j in range(1, len(dp)):
dp[j] += dp[j - 1]
return dp[-1]

算法分析:

时间复杂度为<code>O(n<sup>2</sup>)</code>,空间复杂度<code>O(n<sup>2</sup>)</code>

LeetCode

<div>

Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right, which minimizes the sum of all numbers along its path.

Note: You can only move either down or right at any point in time.

Example 1:

<pre>Input: grid = [[1,3,1],[1,5,1],[4,2,1]] Output: 7 Explanation: Because the path 1 → 3 → 1 → 1 → 1 minimizes the sum. </pre>

Example 2:

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

Constraints:

  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 200
  • 0 <= grid[i][j] <= 100

</div>

题目大意:

求矩阵最短路径和。只能向下向右走。

解题思路:

递归式:

1
dp[i][j] = min{dp[i-1][j], dp[i][j-1]} + grid[i - 1][j - 1]

解题步骤:

N/A

注意事项:

  1. 初始值为最大值,dp[0][1] = dp[1][0] = 0确保左上格正确。
  2. 模板四点注意事项

Python代码:

1
2
3
4
5
6
7
8
# dp[i][j] = min{dp[i-1][j], dp[i][j-1]} + grid[i - 1][j - 1]
def minPathSum(self, grid: List[List[int]]) -> int:
dp = [[float('inf') for _ in range(len(grid[0]) + 1)] for _ in range(len(grid) + 1)]
dp[0][1] = dp[1][0] = 0
for i in range(1, len(dp)):
for j in range(1, len(dp[0])):
dp[i][j] = min(dp[i - 1][j], dp[i][j - 1]) + grid[i - 1][j - 1]
return dp[-1][-1]

算法分析:

时间复杂度为<code>O(n<sup>2</sup>)</code>,空间复杂度<code>O(n<sup>2</sup>)</code>

LeetCode

<div>

A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below).

The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below).

Now consider if some obstacles are added to the grids. How many unique paths would there be?

An obstacle and space is marked as 1 and 0 respectively in the grid.

Example 1:

<pre>Input: obstacleGrid = [[0,0,0],[0,1,0],[0,0,0]] Output: 2 Explanation: There is one obstacle in the middle of the 3x3 grid above. There are two ways to reach the bottom-right corner: 1. Right -> Right -> Down -> Down 2. Down -> Down -> Right -> Right </pre>

Example 2:

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

Constraints:

  • m == obstacleGrid.length
  • n == obstacleGrid[i].length
  • 1 <= m, n <= 100
  • obstacleGrid[i][j] is 0 or 1.

</div>

题目大意:

求矩阵路径总数。有障碍

解题思路:

求个数用DP,递归式:

1
2
dp[i][j] = dp[i-1][j] + dp[i][j-1] if obstacle[i][j-1] == 0
= 0 if obstacle[i][j-1] == 1

解题步骤:

N/A

注意事项:

  1. obstacleGrid[i][j-1], j-1因为dp从1开始, 但i不是,因为dp不含i。

Python代码:

1
2
3
4
5
6
7
8
9
10
def uniquePathsWithObstacles(self, obstacleGrid: List[List[int]]) -> int:
dp = [0] * (len(obstacleGrid[0]) + 1)
dp[1] = 1
for i in range(len(obstacleGrid)):
for j in range(1, len(dp)):
if obstacleGrid[i][j - 1] == 0:
dp[j] += dp[j - 1]
else:
dp[j] = 0
return dp[-1]

算法分析:

时间复杂度为<code>O(n<sup>2</sup>)</code>,空间复杂度<code>O(n<sup>2</sup>)</code>

Free mock interview