KK's blog

每天积累多一些

0%

LeetCode

<div>

A car travels from a starting position to a destination which is target miles east of the starting position.

There are gas stations along the way. The gas stations are represented as an array stations where stations[i] = [position<sub>i</sub>, fuel<sub>i</sub>] indicates that the i<sup>th</sup> gas station is position<sub>i</sub> miles east of the starting position and has fuel<sub>i</sub> liters of gas.

The car starts with an infinite tank of gas, which initially has startFuel liters of fuel in it. It uses one liter of gas per one mile that it drives. When the car reaches a gas station, it may stop and refuel, transferring all the gas from the station into the car.

Return the minimum number of refueling stops the car must make in order to reach its destination. If it cannot reach the destination, return -1.

Note that if the car reaches a gas station with 0 fuel left, the car can still refuel there. If the car reaches the destination with 0 fuel left, it is still considered to have arrived.

Example 1:

<pre>Input: target = 1, startFuel = 1, stations = [] Output: 0 Explanation: We can reach the target without refueling. </pre>

Example 2:

<pre>Input: target = 100, startFuel = 1, stations = [[10,100]] Output: -1 Explanation: We can not reach the target (or even the first gas station). </pre>

Example 3:

<pre>Input: target = 100, startFuel = 10, stations = [[10,60],[20,30],[30,30],[60,40]] Output: 2 Explanation: We start with 10 liters of fuel. We drive to position 10, expending 10 liters of fuel. We refuel from 0 liters to 60 liters of gas. Then, we drive from position 10 to position 60 (expending 50 liters of fuel), and refuel from 10 liters to 50 liters of gas. We then drive to and reach the target. We made 2 refueling stops along the way, so we return 2. </pre>

Constraints:

  • 1 <= target, startFuel <= 10<sup>9</sup>
  • 0 <= stations.length <= 500
  • 0 <= position<sub>i</sub> <= position<sub>i+1</sub> < target
  • 1 <= fuel<sub>i</sub> < 10<sup>9</sup>

</div>

题目大意:

其最小加油次数使得能到达目标

Heap解题思路(推荐):

由于是重叠区间题且贪婪法加找最大油加油站,考虑用heap。求最小值,所以用最大堆。heap存的油数。

注意事项:

  1. 每到一个加油站,先将油预存到heap中。startFuel为到达某个站后的剩余油数,若startFuel为负,从heap中取油,且累计加油次数。
  2. 用heap模板,遍历数组也就是加油站。
  3. 若加完油后,仍为负数,返回-1。
  4. 因为要计算target是否能达到,所以不妨将target加入到stations中,这样startFuel的计算可以包括target

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
heap, res, prev_pos = [], 0, 0
stations.append([target, 0])
for pos, fuel in stations:
startFuel -= pos - prev_pos
while heap and startFuel < 0:
startFuel += -heapq.heappop(heap)
res += 1
if startFuel < 0:
return -1
heapq.heappush(heap, -fuel)
prev_pos = pos
return res

算法分析:

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


DP算法II解题思路:

一开始考虑用jump game,但此题可以在同一层加多次油。比如start fuel有100 mi,而加油站有3个,所以同一层可以加3次油。所以层数和加油次数不是一个概念。
既然是最值考虑另一种方法DP。这题有两个难点:
第一个难点是DP式: dp不采用题目的最小加油次数,考虑jump game的分析,转化成dp[i]为停i个站加油能达到的最远距离。或者这样思考,若定义走到第n个站需要最小加油次数,这个n颗粒度不够细,可以换成miles,不如将下标和数值互换。
第二个难点是递归式。首先知道假设dp[2]能到达的范围内有一个加油站,加油后dp[3] = dp[2] + 该油站的油数。递归式为:

1
dp[i] = max{dp[i-1] + stations[i-1][1]}, dp[i-1] >= stations[i-1][0], stations[i..n]

有个前提条件是dp[2]必须能达到当前的加油站。比如要更新dp[3]从任意两个加油站dp[2] + 加油站[i]可能获得。还可能是从dp[2] + 加油站[i+1]获得,如此类推,要试完stations[i..n]。
dp值从后往前更新,因为当前加油站在后方。

解题步骤:

N/A

注意事项:

  1. dp定义和递归式

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
# dp[i] = max{dp[i-1] + stations[i-1][1]}, dp[i-1] >= stations[i-1][0], stations[i..n]
def minRefuelStops(self, target: int, startFuel: int, stations: List[List[int]]) -> int:
dp = [startFuel] + [0] * len(stations)
for i, (pos, fuel) in enumerate(stations):
for j in range(i, -1, -1):
if dp[j] >= pos:
dp[j + 1] = max(dp[j + 1], dp[j] + fuel)

for i, miles in enumerate(dp):
if miles >= target:
return i
return -1

算法分析:

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

LeetCode

<div>

Given an array, rotate the array to the right by k steps, where k is non-negative.

Example 1:

<pre>Input: nums = [1,2,3,4,5,6,7], k = 3 Output: [5,6,7,1,2,3,4] Explanation: rotate 1 steps to the right: [7,1,2,3,4,5,6] rotate 2 steps to the right: [6,7,1,2,3,4,5] rotate 3 steps to the right: [5,6,7,1,2,3,4] </pre>

Example 2:

<pre>Input: nums = [-1,-100,3,99], k = 2 Output: [3,99,-1,-100] Explanation: rotate 1 steps to the right: [99,-1,-100,3] rotate 2 steps to the right: [3,99,-1,-100] </pre>

Constraints:

  • 1 <= nums.length <= 10<sup>5</sup>
  • -2<sup>31</sup> <= nums[i] <= 2<sup>31</sup> - 1
  • 0 <= k <= 10<sup>5</sup>

Follow up:

  • Try to come up with as many solutions as you can. There are at least three different ways to solve this problem.
  • Could you do it in-place with O(1) extra space?

</div>

题目大意:

数组原地向右旋转k位

解题思路:

证明如上图,比如[1,2,3,4,5,6,7]中,A = [1,2,3,4], B = [5,6,7]先整体reverse再分别reverse。

解题步骤:

N/A

注意事项:

  1. k会大于数组大小,所以取mod
  2. Python中reverse一个sublist,方法先取sublist再倒转

Python代码:

1
2
3
4
5
def rotate(self, nums: List[int], k: int) -> None:
k = k % len(nums) # remember
nums[:] = nums[::-1]
nums[:k] = nums[:k][::-1] # remember how to reverse sublist
nums[k:] = nums[k:][::-1]

算法分析:

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

LeetCode

<div>

Given the head of a singly linked list, reverse the list, and return the reversed list.

Example 1:

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

Example 2:

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

Example 3:

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

Constraints:

  • The number of nodes in the list is the range [0, 5000].
  • -5000 <= Node.val <= 5000

Follow up: A linked list can be reversed either iteratively or recursively. Could you implement both?

</div>

题目大意:

反转LL

解题思路:

简单题,但是经典题。循环体为,左方一个单独节点,右方为一个LL,将LL的首节点指向单独节点

LeetCode 206 Reverse Linked List 反转整个LL LeetCode 092 Reverse Linked List II 反转部分LL,此题更加一般化

模板: 不断将end直接后面的节点加到start直接后面
start(group n) -> NodeA (新状态) -> ... -> end(group n+1) -> NodeA (前状态) -> ...

  1. 找出start和end,start为反转部分的前一个,end为反转部分的首个节点
  2. 循环删除end直接后,再加入到start直接

Python代码:

1
2
3
4
5
6
start, end = fake_head, head
while <反转链表长度>:
# delete the node
moved_node, end.next = end.next, end.next.next
# insert the moved_node
start.next, moved_node.next = moved_node, start.next

解题步骤:

N/A

注意事项:

  1. 利用模板,由于首节点会变,所以引入fake_node
  2. 空节点的处理

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
def reverseList(self, head: ListNode) -> ListNode:
if not head:
return None
fake_head = ListNode(0)
fake_head.next = head
start, end = fake_head, head
while end.next:
# delete the node
moved_node, end.next = end.next, end.next.next
# insert the moved_node
start.next, moved_node.next = moved_node, start.next
return fake_head.next

算法分析:

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

LeetCode

<div>

Given an integer n, return the number of prime numbers that are strictly less than n.

Example 1:

<pre>Input: n = 10 Output: 4 Explanation: There are 4 prime numbers less than 10, they are 2, 3, 5, 7. </pre>

Example 2:

<pre>Input: n = 0 Output: 0 </pre>

Example 3:

<pre>Input: n = 1 Output: 0 </pre>

Constraints:

  • 0 <= n <= 5 * 10<sup>6</sup>

</div>

题目大意:

求n内的素数个数

解题思路:

排除法:知道一个素数后删除它的倍数,剩下的就是下一个素数

解题步骤:

N/A

初始方法

Python代码:

1
2
3
4
5
6
7
8
9
10
def countPrimes(self, n: int) -> int:
if n < 2:
return 0
primes = [True] * n # remember less than n
primes[0] = primes[1] = False
for i in range(2, int(math.sqrt(n)) + 1):
if primes[i]:
for j in range(i * i, n, i): # starting from i rather than 2
primes[j] = False
return sum(primes)

注意事项:

  1. 开一个prime大小数组,初始值为True表示是素数。这样可以遍历到n开方+1. 是最主要的优化步骤,可以优化90%。
  2. 提高效率:i遍历到n开方+1,删除的数不能超过n(一开始写没有break导致TLE), 最后用sum统计比for循环效率高点
  3. 删除数字从i * i开始而不是i * 2,因为很多重复计算如2x3与3x2。此时第二个优化步骤。优化15%
  4. 用数组记录primes而不是set,第三个优化步骤。优化2%
  5. 题目要求素数小于n,所以不含n

Python代码:

1
2
3
4
5
6
7
8
9
10
def countPrimes(self, n: int) -> int:
if n < 2:
return 0
primes = [True] * n # remember less than n
primes[0] = primes[1] = False
for i in range(2, int(math.sqrt(n)) + 1):
if primes[i]:
for j in range(i * i, n, i): # starting from i rather than 2
primes[j] = False
return sum(primes)

算法分析:

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

LeetCode

<div>

A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Given the locations and heights of all the buildings, return the skyline formed by these buildings collectively.

The geometric information of each building is given in the array buildings where buildings[i] = [left<sub>i</sub>, right<sub>i</sub>, height<sub>i</sub>]:

  • left<sub>i</sub> is the x coordinate of the left edge of the i<sup>th</sup> building.
  • right<sub>i</sub> is the x coordinate of the right edge of the i<sup>th</sup> building.
  • height<sub>i</sub> is the height of the i<sup>th</sup> building.

You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0.

The skyline should be represented as a list of "key points" sorted by their x-coordinate in the form [[x<sub>1</sub>,y<sub>1</sub>],[x<sub>2</sub>,y<sub>2</sub>],...]. Each key point is the left endpoint of some horizontal segment in the skyline except the last point in the list, which always has a y-coordinate 0 and is used to mark the skyline's termination where the rightmost building ends. Any ground between the leftmost and rightmost buildings should be part of the skyline's contour.

Note: There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...,[2 3],[4 5],[7 5],[11 5],[12 7],...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...,[2 3],[4 5],[12 7],...]

Example 1:

<pre>Input: buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]] Output: [[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]] Explanation: Figure A shows the buildings of the input. Figure B shows the skyline formed by those buildings. The red points in figure B represent the key points in the output list. </pre>

Example 2:

<pre>Input: buildings = [[0,2,3],[2,5,3]] Output: [[0,3],[5,0]] </pre>

Constraints:

  • 1 <= buildings.length <= 10<sup>4</sup>
  • 0 <= left<sub>i</sub> < right<sub>i</sub> <= 2<sup>31</sup> - 1
  • 1 <= height<sub>i</sub> <= 2<sup>31</sup> - 1
  • buildings is sorted by left<sub>i</sub> in non-decreasing order.

</div>

题目大意:

N/A

解题思路:

Heap(高度最大堆) + 端点排序法(先端点再高度逆序) 不是高频题,但思路值得学习
本质和meeting rooms II一致,因为都是求重合,但这题是二维,后者是一维,区别是

  1. 具体线段有关,所以用高度堆而不是时间堆。高度起主要作用。高度存在起点和终点,它们是高度的时间合法性。
  2. 高度时间合法性同样用end time来验证,这里放到heap的第二元素中。
  3. end time无法自动trigger让高度出堆,所以只能设计让end time也作为遍历的一部分去强行出堆。也就是start和end time作为新数组。

Heap(高度最大堆): LeetCode 253 Meeting Rooms II方法一,终点的最小堆
端点排序法(先端点再高度逆序): LeetCode 253 Meeting Rooms II方法二
meeting room是新线段的start逼栈顶终点出堆,此题也是同样,但用高度的最大堆维护当前最高大厦,这与题意符合。

  • 为什么要加入结束点? 两种情况,第一种情况没有问题,但第二种情况就会漏掉第一栋大厦的结束点。原因是出堆的点没有被处理,但出堆的点可能有多个而且若没有新大厦它不能出堆,所以结束点逼它出堆。

  • 为什么高度逆序? 第一种情况在坐标2这个位置有两节点(2, 0, 0)第一栋大厦结束点, (2, 5, 3)第二栋大厦开始点,若不按高度排序,第一栋结束点会逼第一栋开始点出堆,产生天际线。若按高度逆序,后者先入堆,第一栋开始点出堆也不会产生天际线。类似于heapq.heapreplace先加入再删除或者LeetCode 354 Russian Doll Envelopes的排序方式

解题步骤:

N/A

注意事项:

  1. 先顺序排序端点再逆序高度,因为当结束点和始点重合时,让高度大的先入堆可以确保不会产生矮的天际线,否则这些矮的天际线实际被包含在高的大厦里。
  2. 结束点也要加入循环但不入堆。这样产生两点:
    1. start >= heap[0][1]要取等号,否则不能让这栋大厦结束点出堆。
    2. 结束点不入堆,因为它只用于产生结束点从而加入到结果集,它不产生高度,只有产生高度的点才会被加入到堆
  3. 与前高度不同,也就是高度发生变化就入堆
  4. 确保res[-1][1] != -heap[0][0]。用只有一栋大厦作为test case。
    1. 因为用到了res[-1][1],所以res初始化加入[-float('inf'), 0],第一个值不会用到所以无所谓不妨去负无穷,高度为0;
    2. 最后结果要排除这个点,取res[1:]
    3. 因为要用到heap[0][0],也就是heap要永远有节点。初始化加入[0, float('inf')],高度为0,用于产生在地平线的点的高度,结束点为无穷大,确保不会被逼出堆。 总结加入res中的为起始点,加入heap中的为结束点,它们高度均为0,但端点对称分别为负正无穷。

大厦题,首尾加入节点
LeetCode 084 Largest Rectangle in Histogram LeetCode 218 The Skyline Problem

实现上有点似:
LeetCode 239 Sliding Window Maximum 模板,计算,排除 LeetCode 218 The Skyline Problem 模板,加入,计算

Python代码:

1
2
3
4
5
6
7
8
9
10
11
def getSkyline(self, buildings: List[List[int]]) -> List[List[int]]:
events = sorted(buildings + [[end, 0, 0] for _, end, _ in buildings], key=lambda x: (x[0], -x[2]))
heap, res = [(0, float('inf'))], [[-float('inf'), 0]]
for start, end, height in events:
while heap and start >= heap[0][1]:
heapq.heappop(heap)
if height > 0: # don't push ends into the heap
heapq.heappush(heap, (-height, end))
if res[-1][1] != -heap[0][0]:
res.append([start, -heap[0][0]])
return res[1:]

算法分析:

时间复杂度为O(nlogn),空间复杂度O(k), k为重合天际线个数,此复杂度跟Meeting Rooms II一致

Free mock interview