以为《数据结构》学校教学紫书连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
*/