Jan 15, 2020
Edit me
#. Problem
Minimum Operations to Reduce X to Zero
1. My approach
BFS
I was quite sure that it would be DP, but I couldn’t think of any ways so I just used BFS just to see if it works. And obviously it didn’t work. Time complexity would be O(2^N).
/**
* @param {number[]} nums
* @param {number} x
* @return {number}
*/
var minOperations = function(nums, x) {
const q = [{
curX: x,
curArr: nums,
curCnt: 0
}];
let ans = 0;
while(q.length !== 0){
let temp = q.shift();
let curX = temp.curX;
let curCnt = temp.curCnt;
let curArr = temp.curArr;
if(curX === 0){
return temp.curCnt;
}
if(curX - curArr[0] >= 0){
q.push({
curX: curX - curArr[0],
curArr: curArr.slice(1),
curCnt: curCnt+1
})
}
if(curArr.length > 1 && curX - curArr[curArr.length-1] >= 0){
q.push({
curX: curX - curArr[curArr.length-1],
curArr: curArr.slice(0, curArr.length-1),
curCnt: curCnt+1
})
}
// console.log(q);
}
return -1;
};
Time(O(2^N)) | Space(O(?)) |
---|---|
? ms | ? MB |
super long DP
and I came up with this DP which was quite cleaver at the time but it was too big, making ‘heap out of memory’ error.
- TC - O(N)
- SC - O(N)
- Where N = 1e9 :P
/**
* @param {number[]} nums
* @param {number} x
* @return {number}
*/
var minOperations = function(nums, x) {
const arr = Array.from({length:x+1}, ()=>{
return {
curCnt: 100000,
curLeft: 0,
curRight: 0
}
});
let i = 0, j = nums.length-1;
arr[0].curCnt = 0;
arr[0].curLeft = 0;
arr[0].curRight = nums.length-1;
for(let i = 0; i <= x; i++){
let curCnt = arr[i].curCnt;
let curLeft = arr[i].curLeft;
let curRight = arr[i].curRight;
if(i + nums[curLeft] <= x && curCnt + 1 < arr[i + nums[curLeft]].curCnt){
arr[i + nums[curLeft]].curCnt = curCnt + 1;
arr[i + nums[curLeft]].curLeft = curLeft + 1;
arr[i + nums[curLeft]].curRight = curRight;
}
if(curLeft < curRight && i + nums[curRight] <= x && curCnt + 1 < arr[i + nums[curRight]].curCnt){
arr[i + nums[curRight]].curCnt = curCnt + 1;
arr[i + nums[curRight]].curLeft = curLeft;
arr[i + nums[curRight]].curRight = curRight - 1;
}
}
// console.log(arr);
return arr[x].curCnt === 100000? -1:arr[x].curCnt;
};
2. Solution
It’s called ‘Sliding window’, using two pointers, left and right. Starting from 0 and moving towards the end. Very cleaver, and efficient.
The time complexity here would be O(N) where N = 1e5, and space complexity would be, O(1). This time complexity might be confusing, but here’s why it’s O(N):
- O((right in 0 to N) + (left in 0 to k)) = O(N + k) = O(N)
Sliding window
/**
* @param {number[]} nums
* @param {number} x
* @return {number}
*/
var minOperations = function(nums, x) {
const sum = nums.reduce((acc, cur) => acc + cur, 0);
const target = sum - x;
let curSum = 0;
if(target < 0) return -1;
if(!target) return nums.length;
let l = 0, r = 0;
let maxLen = -Infinity;
while(r <= nums.length){
curSum += nums[r];
while(curSum > target) curSum -= nums[l++];
if(curSum === target) maxLen = Math.max(r-l+1, maxLen);
r++;
}
return maxLen === -Infinity? -1:nums.length-maxLen;
};
Time(O(N)) | Space(O(1)) |
---|---|
108 ms | 50.6 MB |
3. Epilogue
What I’ve learned from this exercise:
- DP is difficult.
- JavaScript has ‘Infinity’ as a value of number type.
- Mostly, we approach from how we solve the problem in our head, but sometimes it’s important to approach it differently since it might be better solution.