KK's blog

每天积累多一些

0%

LeetCode



Given an array of points where points[i] = [x<sub>i</sub>, y<sub>i</sub>] represents a point on the X-Y plane and an integer k, return the k closest points to the origin (0, 0).

The distance between two points on the X-Y plane is the Euclidean distance (i.e., √(x<sub>1</sub> - x<sub>2</sub>)<sup>2</sup> + (y<sub>1</sub> - y<sub>2</sub>)<sup>2</sup>).

You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in).

Example 1:



Input: points = [[1,3],[-2,2]], k = 1
Output: [[-2,2]]
Explanation:
The distance between (1, 3) and the origin is sqrt(10).
The distance between (-2, 2) and the origin is sqrt(8).
Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin.
We only want the closest k = 1 points from the origin, so the answer is just [[-2,2]].


Example 2:

Input: points = [[3,3],[5,-1],[-2,4]], k = 2
Output: [[3,3],[-2,4]]
Explanation: The answer [[-2,4],[3,3]] would also be accepted.


Constraints:

1 <= k <= points.length <= 10<sup>4</sup> -10<sup>4</sup> < x<sub>i</sub>, y<sub>i</sub> < 10<sup>4</sup>

算法思路:

最大堆

注意事项:

  1. 求最小距离用最大堆,距离的相反数入堆
  2. 与堆顶比较,跟模板一样仍然是大于号

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
if not points or not points[0]:
return 0
heap, res = [], []
for i in range(len(points)):
x, y = points[i][0], points[i][1] # x=-2, y=2
if i < k: # i=1, k=1
heapq.heappush(heap, (-(x * x + y * y), x, y)) # -10, 1, 3
elif -(x * x + y * y) > heap[0][0]: # -8 > -10
heapq.heapreplace(heap, (-(x * x + y * y), x, y)) # -8, -2, -2

while heap:
(dis, x, y) = heapq.heappop(heap) # -8, -2, -2
res.append([x, y])
return res

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
public int[][] kClosest(int[][] points, int K) {
PriorityQueue<Point> maxHeap = new PriorityQueue<Point>(K,
new Comparator<Point>() {
@Override
public int compare(Point o1, Point o2) {
return distance(o2, o1); // descending
}
}
);

for(int i = 0; i < points.length; i++) {
Point p = new Point(points[i][0], points[i][1]);
if(maxHeap.size() < K)
maxHeap.offer(p);
else if(distance(p, maxHeap.peek()) < 0) {
maxHeap.poll();
maxHeap.offer(p);
}
}
int[][] res = new int[K][2];
int j = 0;
while(!maxHeap.isEmpty()) {
Point p = maxHeap.poll();
res[j][0] = p.x;
res[j][1] = p.y;
j++;
}
return res;
}

int distance(Point o1, Point o2) {
return (o1.x * o1.x + o1.y * o1.y) - (o2.x * o2.x + o2.y * o2.y);
}

class Point {
int x;
int y;
Point() { x = 0; y = 0; }
Point(int a, int b) { x = a; y = b; }
}

算法分析:

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

LeetCode 054 Spiral Matrix

Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order.

For example,
Given the following matrix:

[
 [ 1, 2, 3 ],
 [ 4, 5, 6 ],
 [ 7, 8, 9 ]
]

You should return [1,2,3,6,9,8,7,4,5].

题目大意:

给定一个mxn的矩阵(m行 n列),以螺旋状返回矩阵中的所有元素。

解题思路:

打印方法主要是以下两种:第一种四边对称打印,实现起来边际情况很多,不推荐。因为要不断向内遍历,所以对称打印不合适。第二种方法是每条边比上一条少一个,
用四个指针,top, bottom, left, right来记录四个边界,每打印完一条边该边界向内扩展。注意有些回路不是完整比如[1,2]或上面例子中5就不是完整回路,此情况,
注意判断top和bottom以及left和right关系即可。四指针法可以进一步升级到两指针法甚至一个指针法,其实都是大同小异。

注意事项:

  1. 注意不是所有矩阵都有完整回路。所以后两个for循环要加if语句
  2. 右边和左边,遍历矩阵用matrix[i][right],而不是matrix[right][i]
  3. Python中从后往前遍历要注意始点-1,range(right, left - 1, -1):

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
if not matrix or not matrix[0]:
return []
res = []
top, bottom, left, right = 0, len(matrix) - 1, 0, len(matrix[0]) - 1
while top <= bottom and left <= right:
for i in range(left, right + 1):
res.append(matrix[top][i])
top += 1
for i in range(top, bottom + 1):
res.append(matrix[i][right]) # remember [i[[right] not [right][i]
right -= 1
if top <= bottom:
for i in range(right, left - 1, -1):
res.append(matrix[bottom][i])
bottom -= 1
if left <= right:
for i in range(bottom, top - 1, -1):
res.append(matrix[i][left])
left += 1
return res

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
public void spiral2(int[][] a){
int rowTop = 0, rowBottom = a.length-1, colLeft = 0, colRight = a[0].length-1;

while(rowTop<=rowBottom && colLeft<=colRight){
//topRow
for(int i=colLeft;i<=colRight;i++)
System.out.print(a[rowTop][i]+" ");
rowTop++;
//rightCol
for(int i=rowTop;i<=rowBottom;i++)
System.out.print(a[i][colRight]+" ");
colRight--;
if(rowTop<=rowBottom){
for(int i=colRight;i>=colLeft;i--)
System.out.print(a[rowBottom][i]+" ");
rowBottom--;
}
if(colLeft<=colRight){
for(int i=rowBottom;i>=rowTop;i--)
System.out.print(a[i][colLeft]+" ");
colLeft++;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void spiral3(int[][] a){
int rowTop = 0, colLeft = 0;

while(rowTop<=a.length-rowTop-1 && colLeft<=a[0].length-colLeft-1){
//topRow
for(int i=colLeft;i<=a[0].length-colLeft-1;i++)
System.out.print(a[rowTop][i]+" ");
rowTop++;
//rightCol
for(int i=rowTop;i<=a.length-rowTop;i++)
System.out.print(a[i][a[0].length-colLeft-1]+" ");
//colRight--;
if(rowTop<=a.length-rowTop){
for(int i=a[0].length-colLeft-2;i>=colLeft;i--)
System.out.print(a[a.length-rowTop][i]+" ");
}
if(colLeft<=a[0].length-colLeft-2){
for(int i=a.length-rowTop-1;i>=rowTop;i--)
System.out.print(a[i][colLeft]+" ");
colLeft++;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void spiral4(int[][] a){
int num = Math.min(a.length, a[0].length);
for(int st=0;st<(num+1)/2;st++){
//complete edge(top)
for(int i=st;i<a[0].length-st;i++)
System.out.print(a[st][i]+" ");
//complete edge-top (right)
for(int i=st+1;i<a.length-st;i++)
System.out.print(a[i][a[0].length-1-st]+" ");
if(a[0].length-2-st>st)
for(int i=a[0].length-2-st;i>=st;i--)
System.out.print(a[a.length-st-1][i]+" ");
if(a.length-2-st>st+1)
for(int i=a.length-2-st;i>=st+1;i--)
System.out.print(a[i][st]+" ");
}
}

算法分析:

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

LeetCode 121 Best Time to Buy and Sell Stock

Say you have an array for which the ith element is the price of a given stock on day i.

If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit.

Example 1:

Input: [7, 1, 5, 3, 6, 4]
Output: 5

max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price)

Example 2:

Input: [7, 6, 4, 3, 1]
Output: 0

In this case, no transaction is done, i.e. max profit = 0.

题目大意:

如果你只能进行一次交易(比如购买或者销售一个股票),设计一个算法来获取最大利润。

解题思路:

利润=当前价格-买入价,利润作为第一个变量求其最大值。由于买入价越低,利润可能会越大,所以第二个变量就要不断更新买入价
(最小值)。本题核心思路就是维护两个变量:最低价,利润。为什么不用最高价而选择利润呢?因为最低价和最高价没有顺序,最高价
必须在最低价后面,这样的利润才可实现,但如果是最低价和利润,就能确保这个顺序了,因为利润一定是在最低价后,否则这个利
润为负,不能为最大值。另一种稍麻烦的方法是凡是min更新,最高价就reset为0,大原则就是保持顺序。

注意事项:

  1. 数组为空

Python代码:

1
2
3
4
5
6
7
def maxProfit(self, prices: List[int]) -> int:
min_buy_idx, max_profit = 0, 0
for i in range(len(prices)):
max_profit = max(max_profit, prices[i] - prices[min_buy_idx])
if prices[i] < prices[min_buy_idx]:
min_buy_idx = i
return max_profit

Java代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public int maxProfit(int[] prices) {
if(prices.length==0)
return 0;
int min = prices[0];
int curProfit = 0;
for(int i=1;i<prices.length;i++){
int todayProfit = prices[i]-min;
if(todayProfit>curProfit)
curProfit = todayProfit;
if(min>prices[i])
min = prices[i];
}
return curProfit;
}

算法分析:

时间复杂度为O(n),n为字符串长度,空间复杂度O(1)

相关题目:

LeetCode 121 Best Time to Buy and Sell Stock
LeetCode 122 Best Time to Buy and Sell Stock II
LeetCode 309 Best Time to Buy and Sell Stock with Cooldown
LeetCode 123 Best Time to Buy and Sell Stock III

LeetCode 056 Merge Intervals

Given a collection of intervals, merge all overlapping intervals.

For example,
Given [1,3],[2,6],[8,10],[15,18],
return [1,6],[8,10],[15,18].

题目大意:

给定几个区间,要求合并重叠区间,返回结果.

解题思路:

A公司的考题。这条题难点在于判断是否合并,怎么合并,新区间合并多个区间。

  1. 按start排序。
  2. 定义API:如何合并两个区间(两情况),两个区间是否可以合并
  3. 遍历每个区间,产生新区间并将其带入到下一轮循环。这是难点,公式为 新区间=新区间+输入区间[i],这也分为两种情况,可合并和不可合并
    不可合并时,前状态的新区间成为结果,公式为新区间=输入区间[i]。
  4. 若不想特别处理循环边界,可加入空区间到末尾(见Java实现,它把新区间=输入区间[i]放入了下一轮)。若不如此做,可将空区间放入开头。

注意事项:

  1. 先按左节点排序
  2. 区间的右端与另一个区间的左端一样,也算重叠,如[1,2],[2,3]。
  3. 原输入加入首节点的左边界fake区间。避免for循环的特殊处理。2. 最后一个区间的情况。
  4. 合并后生成新区间,要与下一个继续尝试合并。

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def merge(self, intervals: List[List[int]]) -> List[List[int]]:
intervals.sort(key=lambda x: x[0])
new_interval = [intervals[0][0], intervals[0][0]]
res = []
for interval in intervals:
if self.can_merge(new_interval, interval):
new_interval = self.merge_two_intervals(new_interval, interval)
else:
res.append(new_interval)
new_interval = interval
res.append(new_interval)
return res

def can_merge(self, interval, interval2):
return interval[1] >= interval2[0]

def merge_two_intervals(self, interval, interval2):
return [interval[0], max(interval[1], interval2[1])]

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
public List<Interval> merge(List<Interval> intervals) {
Collections.sort(intervals, new Comparator<Interval>(){
public int compare(Interval v1, Interval v2){
return v1.start - v2.start;
}
});
intervals.add(new Interval(Integer.MAX_VALUE, Integer.MAX_VALUE));
List<Interval> re = new ArrayList<Interval>();
Interval newInterval = null;
for(int i=1;i<intervals.size();i++){
if(newInterval==null)
newInterval = intervals.get(i-1);
if(canMerge(newInterval,intervals.get(i))){
newInterval = mergeIntervals(newInterval,intervals.get(i));
}
else{
re.add(newInterval);
newInterval = null;
}
}
return re;
}


//假设in与in2按start排序,所以只有两情况:
/*
* In -------
* In2 ---
* In2 --------
*/
public boolean canMerge(Interval in, Interval in2){
if(in2.start == Integer.MAX_VALUE)
return false;
return in.end>=in2.start;
}

public Interval mergeIntervals(Interval in, Interval in2){
return new Interval(in.start, Math.max(in.end, in2.end));
}

算法分析:

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

LeetCode 1197 Minimum Knight Moves

In an infinite chess board with coordinates from -infinity to +infinity, you have a knight at square [0, 0].

A knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction.

Return the minimum number of steps needed to move the knight to the square [x, y]. It is guaranteed the answer exists.

Example 1:

Input: x = 2, y = 1
Output: 1
Explanation: [0, 0] → [2, 1]

Example 2:

Input: x = 5, y = 5
Output: 4
Explanation: [0, 0] → [2, 1] → [4, 2] → [3, 4] → [5, 5]

Constraints:

  • |x| + |y| <= 300

Because x and y are constrained to be in range[-300, 300], we can use BFS to find the minimum steps needed to reach target(x, y). Furthermore, we can only consider the case that x >=0 && y >=0 since the chess board is symmetric. The bfs implementation is pretty straightforward. There are two important points you need to be careful with.

  1. Pruning. We can limit the search dimension within 310 * 310. Any moves that lead to a position that is outside this box will not yield an optimal result.

2. Initially, you used a Set of type int[] to track visited positions. This caused TLE because you didn’t overwrite the hashCode and equals methods for int[]. As a result, Set uses the default hashCode and equals method when checking if an element is already in the set. For equals(), The default implementation provided by the JDK is based on memory location — two objects are equal if and only if they are stored in the same memory address. For a comprehensive reading, refer to https://dzone.com/articles/working-with-hashcode-and-equals-in-java

O(x * y) runtime and space

题目大意:

象棋一样,走日字到达目标点的最小次数。

解题思路:

这题是最短路径题,第一时间想到BFS。

解题步骤:

这题是最短路径题,第一时间想到BFS。这是一条典型的单源最短路径问题。用Distance BFS模板

  1. 建距离map。
  2. BFS访问。

注意事项:

  1. 有边界限制

Python代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def minKnightMoves(self, x: int, y: int) -> int:
return self.bfs(0, 0, x, y)

def bfs(self, start_x, start_y, target_x, target_y):
directions = {(2, 1), (1, 2), (-1, 2), (-2, 1), (-2, -1), (-1, -2), (1, -2), (2, -1)}
queue = deque([(start_x, start_y)])
visited = {(start_x, start_y)}
distance = {(start_x, start_y): 0}
while queue:
node = queue.popleft()
if node == (target_x, target_y):
return distance[node]

for direction in directions:
neighbor = (node[0] + direction[0], node[1] + direction[1])
if neighbor in visited:
continue
queue.append(neighbor)
visited.add(neighbor)
distance[neighbor] = distance[node] + 1

注意事项:

  1. 用map记录距离一定要将首节点加入到map中,否则求距离时候会NPE。
  2. visited我一开始实现用HashSet但因为没有实现equals导致LTE,改成矩阵即可。

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
int[] directX = new int[]{1, 1,-1,-1,2,2,-2,-2};
int[] directY = new int[]{2,-2,2,-2,1,-1,1,-1};

public int shortestPath(boolean[][] grid, Point source, Point destination) {
if(grid == null || grid.length == 0 || grid[0].length == 0)
return -1;

Queue<Point> q = new LinkedList<>();
Map<Point, Integer> map = new HashMap<>();
map.put(source, 0); // remember
boolean[][] visited = new boolean[grid.length][grid[0].length];
q.offer(source);
visited[source.x][source.y] = true; // use hashSet is wrong.
while(!q.isEmpty()) {
Point p = q.poll();
if(p.x == destination.x && p.y == destination.y)
return map.get(p);
for(Point neighbor : getNeighbors(p)) {
if(!isValid(grid, neighbor) || visited[neighbor.x][neighbor.y])
continue;
q.offer(neighbor);
visited[neighbor.x][neighbor.y] = true;
map.put(neighbor, map.get(p) + 1);
}
}

return -1;
}

List<Point> getNeighbors(Point point) {
List<Point> result = new ArrayList<>();
for(int i = 0; i < 8; i++) {
result.add(new Point(point.x + directX[i], point.y + directY[i]));
}
return result;
}

boolean isValid(boolean[][] grid, Point point) {
if(point.x >= 0 && point.x < grid.length && point.y >= 0 && point.y < grid[0].length
&& !grid[point.x][point.y])
return true;
else return false;
}

算法分析:

时间复杂度为棋盘大小O(n*m),空间复杂度O(n)

Free mock interview