Java每日一题——>剑指 Offer II 037. 小行星碰撞

Posted stormzhuo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java每日一题——>剑指 Offer II 037. 小行星碰撞相关的知识,希望对你有一定的参考价值。

题目

这是LeetCode上的 [037,小行星碰撞],难度为 [中等]

给定一个整数数组 asteroids,表示在同一行的小行星。

对于数组中的每一个元素,其绝对值表示小行星的大小,正负表示小行星的移动方向(正表示向右移动,负表示向左移动)。每一颗小行星以相同的速度移动。

找出碰撞后剩下的所有小行星。碰撞规则:两个行星相互碰撞,较小的行星会爆炸。如果两颗行星大小相同,则两颗行星都会爆炸。两颗移动方向相同的行星,永远不会发生碰撞。

示例 1:

输入:asteroids = [5,10,-5]
输出:[5,10]
解释:10 和 -5 碰撞后只剩下 10 。 5 和 10 永远不会发生碰撞。

示例 2:

输入:asteroids = [8,-8]
输出:[]
解释:8 和 -8 碰撞后,两者都发生爆炸。

示例 3:

输入:asteroids = [10,2,-5]
输出:[10]
解释:2 和 -5 发生碰撞后剩下 -5 。10 和 -5 发生碰撞后剩下 10 。

示例 4:

输入:asteroids = [-2,-1,1,2]
输出:[-2,-1,1,2]
解释:-2 和 -1 向左移动,而 1 和 2 向右移动。 由于移动方向相同的行星不会发生碰撞,所以最终没有行星发生碰撞。 

提示:

  • 2 <= asteroids.length <= 104
  • -1000 <= asteroids[i] <= 1000
  • asteroids[i] != 0

题解(栈方法)

根据题意,可以在遍历小行星数组时,根据行星的方向不同执行如下的操作

如果当前行星的方向是向右的,可以把当前行星放在一个容器中,因为不知道下个行星的方向

如果当前行星的方向是向左的,那么有下面两种情况

  1. 如果容器不为空,那么容器最后放进的行星可能有两种方向,如下

    • 如果容器最后放进的行星方向是向右的,那么它和当前行星的方向是相反的,因此会碰撞,有三种情况

      • 如果容器最后放进的行星的体积小于当前行星的体积,则容器最后放进的行星会爆炸,取出容器最后放进的行星。之后再从容器中取出后面放进的行星继续比较,直到容器的行星不小于当前行星的体积或容器的行星方向和当前行星相同,即对应下面两种操作

        • 如果容器的行星不小于当前行星的体积,则当前行星会爆炸,不取出容器后面的行星
        • 如果容器的行星方向和当前行星相同,则不会发现碰撞,因此可以把当前行星放进容器中,因为不知道下个行星的方向
      • 如果容器最后放进的行星的体积和当前行星的体积是相同的,则两个行星都会爆炸,取出容器最后放进的行星

      • 如果容器最后放进的行星体积大于当前行星的体积,则当前行星会爆炸,不取出容器最后放进的行星

    • 如果容器最后放进的行星方向是向左的,那么它和当前行星的方向是相同的,因此不会碰撞,可以放在容器中。可以进一步推断此当前行星前面的所以行星方向都是向左的

  2. 如果容器为空,不包含行星,则此行星是第一个向左的行星,即它前面没有行星,因此不会和任何行星相撞,可以放在容器中

综上分析,此容器具有先进后出的特点,因此可以使用栈来作为容器

代码实现

class Solution 
    public int[] asteroidCollision(int[] asteroids) 
       // 创建一个栈来存放行星
        Stack<Integer> stack = new Stack<>();
        // 遍历行星数组
        for (int asteroid : asteroids) 
           /* 若当前行星方向是向左的,并且栈不为空,且栈顶行星方向是向右的且体积小于当前行星
           * 则两行星会碰撞,栈顶行星会爆炸,需要弹出栈,继续判断下一个栈顶行星,直至不满条件
           * 即栈为空或栈顶行星方向和当前行星都是向左的或栈顶行星的体积大于或等于当前行星*/
            while (!stack.isEmpty() && stack.peek() > 0 && stack.peek() < -asteroid) 
                stack.pop();
            
            /* 若当前行星是向左,且栈顶行星方向是向右并且体积等于当前行星
            *  则两行星都会爆炸,需要弹出栈顶行星*/
            if (!stack.isEmpty() && asteroid < 0 && stack.peek() == -asteroid) 
                stack.pop();
            /* 若当前行星是向右,则可以入栈,因为不知道下一个行星的方向
            *  若当前行星是向左,且栈为空,则可以入栈
            * 因为它前面已经没有行星了,永远也不会碰撞
            * 若当前行星向左,栈不为空,但栈顶行星向左,则可以入栈
            * 因为它和前面的行星方向都相同,不会发现碰撞*/
             else if (asteroid > 0 || stack.isEmpty() || stack.peek() < 0) 
                stack.push(asteroid);
            
        
        int[] ans = new int[stack.size()];
        for (int i = ans.length -1; i >= 0; i--) 
            ans[i] = stack.pop();
        
        return ans;
    

复杂度分析


假设用n个行星

时间复杂度

需要遍历行星数组,故时间复杂度为O(n)

空间复杂度

需要一个栈来存放行星,故空间复杂度为O(n)

以上是关于Java每日一题——>剑指 Offer II 037. 小行星碰撞的主要内容,如果未能解决你的问题,请参考以下文章

Java每日一题——>剑指 Offer II 035. 最小时间差(三解,蛮力,排序,哈希)

Java每日一题——> 剑指 Offer II 028. 展平多级双向链表

Java每日一题——>剑指 Offer II 027. 回文链表

Java每日一题——>剑指 Offer II 030. 插入删除和随机访问都是 O 的容器

Java每日一题——>剑指 Offer II 034. 外星语言是否排序

Java每日一题——>剑指 Offer II 034. 外星语言是否排序