栈题目:行星碰撞

题目

标题和出处

标题:行星碰撞

出处: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} 2asteroids.length104
  • -1000 ≤ asteroids[i] ≤ 1000 \texttt{-1000} \le \texttt{asteroids[i]} \le \texttt{1000} -1000asteroids[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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

伟大的车尔尼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值