题目
标题和出处
标题:行星碰撞
出处:735. 行星碰撞
难度
5 级
题目描述
要求
给定一个整数数组 asteroids \texttt{asteroids} asteroids,表示在同一行的行星。
对于数组中的每一个元素,其绝对值表示行星的大小,正负表示行星的移动方向(正表示向右移动,负表示向左移动)。每一颗行星以相同的速度移动。
找出碰撞后剩下的所有行星。碰撞规则:两颗行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。
示例
示例 1:
输入:
asteroids
=
[5,10,-5]
\texttt{asteroids = [5,10,-5]}
asteroids = [5,10,-5]
输出:
[5,10]
\texttt{[5,10]}
[5,10]
解释:
10
\texttt{10}
10 和
-5
\texttt{-5}
-5 碰撞后只剩下
10
\texttt{10}
10。
5
\texttt{5}
5 和
10
\texttt{10}
10 永远不会发生碰撞。
示例 2:
输入:
asteroids
=
[8,-8]
\texttt{asteroids = [8,-8]}
asteroids = [8,-8]
输出:
[]
\texttt{[]}
[]
解释:
8
\texttt{8}
8 和
-8
\texttt{-8}
-8 碰撞后,两者都发生爆炸。
示例 3:
输入:
asteroids
=
[10,2,-5]
\texttt{asteroids = [10,2,-5]}
asteroids = [10,2,-5]
输出:
[10]
\texttt{[10]}
[10]
解释:
2
\texttt{2}
2 和
-5
\texttt{-5}
-5 发生碰撞后剩下
-5
\texttt{-5}
-5。
10
\texttt{10}
10 和
-5
\texttt{-5}
-5 发生碰撞后剩下
10
\texttt{10}
10。
示例 4:
输入:
asteroids
=
[-2,-1,1,2]
\texttt{asteroids = [-2,-1,1,2]}
asteroids = [-2,-1,1,2]
输出:
[-2,-1,1,2]
\texttt{[-2,-1,1,2]}
[-2,-1,1,2]
解释:
-2
\texttt{-2}
-2 和
-1
\texttt{-1}
-1 向左移动,而
1
\texttt{1}
1 和
2
\texttt{2}
2 向右移动。由于移动方向相同的行星不会发生碰撞,所以最终没有行星发生碰撞。
数据范围
- 2 ≤ asteroids.length ≤ 10 4 \texttt{2} \le \texttt{asteroids.length} \le \texttt{10}^\texttt{4} 2≤asteroids.length≤104
- -1000 ≤ asteroids[i] ≤ 1000 \texttt{-1000} \le \texttt{asteroids[i]} \le \texttt{1000} -1000≤asteroids[i]≤1000
- asteroids[i] ≠ 0 \texttt{asteroids[i]} \ne \texttt{0} asteroids[i]=0
解法
思路和算法
如果两颗行星的移动方向相同,则不会发生碰撞。如果两颗行星的移动方向相反,其中左边的行星向左移动,右边的行星向右移动,也不会发生碰撞。
只有当两颗行星相向移动时,左边的行星向右移动,右边的行星向左移动,才会发生碰撞。
由于碰撞只会发生在相邻的行星之间(行星爆炸之后,其左右的行星从不相邻变成相邻),因此可以使用栈存储未爆炸的行星。
从左到右遍历数组 asteroids \textit{asteroids} asteroids,遇到正的行星(即向右移动的行星)则将其入栈,遇到负的行星(即向左移动的行星)则需要考虑该行星可能和栈顶的行星发生碰撞,只有当栈顶的行星为正时才会发生碰撞。可能有如下情况:
-
当前的行星比栈顶的行星小,当前的行星爆炸,栈顶的行星保持不变;
-
当前的行星和栈顶的行星大小相同,两颗行星都会爆炸,将栈顶的行星出栈;
-
当前的行星比栈顶的行星大,栈顶的行星爆炸,将栈顶的行星出栈,然后将当前的行星继续和新的栈顶的行星比较,重复上述操作,直到栈为空或者当前的行星爆炸。
如果当前的行星没有爆炸,则将当前的行星入栈。
遍历结束之后,栈内的元素即为碰撞后剩下的所有行星,从栈底到栈顶依次为从左到右的每颗行星。由于栈的特点是后进先出,因此在使用数组存储结果时,需要从右向左依次填入从栈内弹出的元素。
考虑一个例子: asteroids = [ 10 , 8 , 5 , 2 , − 8 , − 9 , − 12 , − 3 , 1 ] \textit{asteroids} = [10, 8, 5, 2, -8, -9, -12, -3, 1] asteroids=[10,8,5,2,−8,−9,−12,−3,1]。
从左到右遍历数组 asteroids \textit{asteroids} asteroids,对于 10 10 10、 8 8 8、 5 5 5、 2 2 2,由于都为正,因此将这些行星入栈,此时栈为 [ 10 , 8 , 5 , 2 ] [10, 8, 5, 2] [10,8,5,2],其中左边为栈底,右边为栈顶。
对于 − 8 -8 −8,由于为负,因此需要考虑可能和栈顶的行星发生碰撞。由于 2 2 2 和 5 5 5 的行星都小于 − 8 -8 −8 的行星,因此 2 2 2 和 5 5 5 爆炸,将这两颗行星出栈,然后栈顶元素变成 8 8 8,由于 8 8 8 和 − 8 -8 −8 两颗行星的大小相同,因此都会爆炸,将 8 8 8 出栈,且 − 8 -8 −8 不入栈,此时栈为 [ 10 ] [10] [10]。
对于 − 9 -9 −9,由于为负,因此需要考虑可能和栈顶的行星发生碰撞。由于 10 10 10 的行星大于 − 9 -9 −9 的行星,因此 − 9 -9 −9 爆炸,栈顶元素不变, − 9 -9 −9 不入栈,此时栈为 [ 10 ] [10] [10]。
对于 − 12 -12 −12,由于为负,因此需要考虑可能和栈顶的行星发生碰撞。由于 10 10 10 的行星小于 − 12 -12 −12 的行星,因此 10 10 10 爆炸,将 10 10 10 出栈,栈变成空, − 12 -12 −12 没有爆炸,因此将 − 12 -12 −12 入栈,此时栈为 [ − 12 ] [-12] [−12]。
对于 − 3 -3 −3,虽然为负,但是由于栈顶的行星为负,因此 − 3 -3 −3 不会和栈顶的行星发生碰撞,将 − 3 -3 −3 入栈,此时栈为 [ − 12 , − 3 ] [-12, -3] [−12,−3]。
对于 1 1 1,由于为正,因此将 1 1 1 入栈,此时栈为 [ − 12 , − 3 , 1 ] [-12, -3, 1] [−12,−3,1]。
碰撞后剩下的所有行星即为 [ − 12 , − 3 , 1 ] [-12, -3, 1] [−12,−3,1]。
代码
class Solution {
public int[] asteroidCollision(int[] asteroids) {
Deque<Integer> stack = new ArrayDeque<Integer>();
int length = asteroids.length;
for (int i = 0; i < length; i++) {
int curr = asteroids[i];
if (curr > 0) {
stack.push(curr);
} else {
boolean flag = true;
while (!stack.isEmpty() && stack.peek() > 0) {
int prev = stack.peek();
if (-curr < prev) {
flag = false;
break;
} else {
stack.pop();
if (curr == -prev) {
flag = false;
break;
}
}
}
if (flag) {
stack.push(curr);
}
}
}
int size = stack.size();
int[] remain = new int[size];
for (int i = size - 1; i >= 0; i--) {
remain[i] = stack.pop();
}
return remain;
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 asteroids \textit{asteroids} asteroids 的长度。需要遍历数组 asteroids \textit{asteroids} asteroids 一次,每个元素最多入栈和出栈各一次。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是数组 asteroids \textit{asteroids} asteroids 的长度。空间复杂度主要取决于栈空间,栈内的元素个数为 O ( n ) O(n) O(n)。