从双栈思想到空间O(1)

以为《数据结构》学校教学紫书连LeetCode上的中档题都run不了,没想到有些想法还是不错的。

双栈思想是来自算式字符串,一个栈压运算符或括号,一栈压数字。

Leetcode 238题(字节笔试题,第二种解法已经满足空间速度,但最后一种解法才能达到O(1),才能符号面试官的想法)

238. 除自身以外数组的乘积 - 力扣(Leetcode)

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积

题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。

不要使用除法,且在 O(n) 时间复杂度内完成此题。

三种解法:

一.双栈思想。

能顺利跑通,但速度1%、空间5%,太慢了,栈操作还是挺耗时的。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        Stack<Integer> leftStack = new Stack<>();
        Stack<Integer> rightStack = new Stack<>();
        int n = nums.length;

        leftStack.push(1);
        rightStack.push(1);

        for (int i = n - 1; i > 0; i--) {
            rightStack.push(nums[i] * rightStack.peek());
        }

        for (int i = 0; i < n; i++) {
            int num = nums[i];  // 因为我们直接将答案存入原数组,所以记录一下原本值
            nums[i] = leftStack.peek() * rightStack.peek();

            leftStack.push(num * leftStack.peek());
            rightStack.pop();             
        }
        return nums;
    }
}
/**
题意解读:nums = [1,2,3,4]  输出: [24,12,8,6]
24=2*3*4  12=1*3*4  8=1*2*4  6=1*2*3 
不要使用除法,且在 O(n) 时间复杂度内完成此题

想法:用两个栈(栈存的是乘积-累乘)

左栈 
右栈 4 12 24 
弹出 24

左栈 1
右栈 4 12 
弹出 1*12 

左栈 1 2
右栈 4  
弹出 2*4 

左栈 1 2 6
右栈   
弹出 6 

 */

二、双边数组

把栈换成数组的思路,往往能节省时间和空间。速度100%、空间95%,但空间复杂度O(n)。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] left = new int[n + 1];  // left的最后一位是用不到的,因为下面的for循环而加上的
        int[] right = new int[n + 1];  // right的首位是用不到的,为了right和left索引同步,还是把n改为n+1

        left[0] = 1;
        right[n] = 1;
        
        for (int i = n - 1; i > 0; i--) {
            right[i] = right[i + 1] * nums[i]; 
        }

        for (int i = 0; i < n; i++) {
            left[i + 1] = left[i] * nums[i];
            nums[i] = left[i] * right[i + 1];  // 直接将答案存入原数组
                         
        }
        return nums;
    }
}

三、O(1)

这里其实就是把方法二的右数组反序到自身位置,而遍历时,把答案放入另一个数组,答案所占的索引位的原先值才传给后面,显然不重位。速度100%、空间97%,但空间复杂度O(1)。

class Solution {
    public int[] productExceptSelf(int[] nums) {
        int n = nums.length;
        int[] res = new int[n];
        res[n - 1] = 1;  // 初始化
        for (int i = n - 1; i > 0; i--) {
            res[i - 1] = res[i] * nums[i]; 
        }

        // 不需要操作res[0],因为上面初始化好的res[0]就是对的(左边没有数)
        for (int i = 1; i < n; i++) {
            res[i] *= nums[i - 1];
            nums[i] *= nums[i - 1];
        }
        return res;
    }
}
/**
空间复杂度为 O(1) 的方法
其实就是返回自己创建的数组,而这个数组空间不算,因为是返回的,nums自然也不算,所以最多可以有两个数组来使用
res  返回的数组
nums 参数数组
其实从上面的操作中我们是可以发现left和right其实是不重位的,不冲突的在位置上
初始化
res      24 12  4  1
nums (1) 1  2   3  4
res[0] = 24 * (1)  虚拟的1
res   24 12  4  1
nums  1  2   3  4
res[1] = 12 * 1
res   24 12  4  1
nums  1  2*1 3  4
res[2] = 4 * 2
res   24 12  4   1
nums  1  2   3*2  4
res[3] = 1 * 6
*/

猜你喜欢

转载自blog.csdn.net/lxd_max/article/details/128823630