leetcode刷题记录(14)-中等

tech2022-08-01  136

1.逆波兰表达式求值

题目:

根据 逆波兰表示法,求表达式的值。

有效的运算符包括 +, -, *, / 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

 

说明:

整数除法只保留整数部分。给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况

思路:基本思路,用一个栈保存数字,遇到数字推入栈,遇到字符,则取出栈顶的两个字符计算,并累加。用eval方便,不过内存消耗更大一点

时间复杂度:O(n),空间复杂度(n)

/** * @param {string[]} tokens * @return {number} */ var evalRPN = function(tokens) { const pression = ["+", "-", "*", "/"]; const stack = []; for (const s of tokens) { if (pression.includes(s)) { const v2 = stack.pop(); const v1 = stack.pop(); switch (s) { case "+": stack.push(+v1 + +v2); break; case "-": stack.push(v1 - v2); break; case "*": stack.push(v1 * v2); break; case "/": stack.push(~~(v1 / v2)); break; } } else { stack.push(s); } } return stack[0]; };

优化:可以从后往前递归处理。从后往前,遇到字符,说明前面要对两个数字的值进行操作,遇到数字则转为数字返回,减法和除法注意顺序问题

时间复杂度:O(n),空间复杂度O(1)

/** * @param {string[]} tokens * @return {number} */ var evalRPN = function(tokens) { let fun = () => { let char = tokens.pop(); let num; switch (char) { case "+": return fun() + fun(); case "-": num = fun(); return fun() - num; case "*": return fun() * fun(); case "/": num = fun(); return ~~(fun() / num); default: return +char; } } return fun(); };

2.翻转字符串里面的单词

题目:给定一个字符串,逐个翻转字符串中的每个单词。

思路:先用自带的方法,split分割然后反转

时间复杂度:O(n),空间复杂度O(n)

/** * @param {string} s * @return {string} */ var reverseWords = function(s) { return s .trim() .split(" ") .filter((i) => i).reverse().join(" "); };

也可以自己反转,就是把字符串推入一个数组,新成员放在开头

时间复杂度:O(n),空间复杂度O(n)

/** * @param {string} s * @return {string} */ var reverseWords = function(s) { let left = 0; let right = s.length - 1; while (s[left] === " ") left++; while (s[right] === " ") right--; const res = []; let word = ""; while (left <= right) { if (s[left] === " " && word) { res.unshift(word); word = ""; } else if (s[left] !== " ") { word += s[left]; } left++; } res.unshift(word) return res.join(" "); };

3.乘积最大子数组

题目:给你一个整数数组 nums ,请你找出数组中乘积最大的连续子数组(该子数组中至少包含一个数字),并返回该子数组所对应的乘积。

思路:乘积最大,因为0的特殊性,用0将数组分割成一个个小数组,对每一个小数组进行处理。如果数组的累积大于0,那么这个数就是最大乘积,如果小于0,从两边开始计算遇到第一个负数的乘积,左侧值更大,则除以左侧的乘积,反之右侧(负数越大,绝对值越小),最终对所有的子数组求最大值即可

时间复杂度:O(n),空间复杂度O(n)

/** * @param {number[]} nums * @return {number} */ const maxP = (nums) => { if (nums.length < 2) return nums[0]; let max = nums.reduce((a, b) => a * b); if (max > 0) return max; let index = 0; let sumleft = 1; let sumRight = 1; while (sumleft > 0) { sumleft *= nums[index]; index++; } index = nums.length - 1; while (sumRight > 0) { sumRight *= nums[index]; index--; } if (sumleft > sumRight) return max / sumleft; return max / sumRight; }; var maxProduct = function (nums) { if (nums.length < 2) return nums[0]; const res = []; let temp = []; for (const n of nums) { if (n === 0 && temp.length) { res.push(temp); temp = []; } else if (n !== 0) { temp.push(n); } } if (temp.length) res.push(temp); return Math.max(...res.map((item) => maxP(item)), 0); };

优化:转换思路,动态规划。乘积最大有两种情况,要么自身是正数,乘积就是前面数的最大乘积乘以自己。要么自身的负数,乘积就是前面数的最小值乘以自己,所以要保存最大的正数和最小的负数。当然为了区分0的情况,我们可以再将当前的积和自身比较,最终最后的结果就是最大值

时间复杂度:O(n),空间复杂度O(1)

/** * @param {number[]} nums * @return {number} */ var maxProduct = function (nums) { let res = nums[0] let prevMin = nums[0] let prevMax = nums[0] let temp1 = 0, temp2 = 0 for (let i = 1; i < nums.length; i++) { temp1 = prevMin * nums[i] temp2 = prevMax * nums[i] prevMin = Math.min(temp1, temp2, nums[i]) prevMax = Math.max(temp1, temp2, nums[i]) res = Math.max(prevMax, res) } return res };

4.寻找旋转数组的最小值

题目:

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

请找出其中最小的元素。

你可以假设数组中不存在重复元素。

思路:先用自带的方法:

时间复杂度:O(n),空间复杂度O(1)

/** * @param {number[]} nums * @return {number} */ var findMin = function(nums) { return Math.min(...nums) };

然后可以用二分法。先比较开头和末尾的值的关系。如果开头的值小于末尾的值,相当于没有旋转,最小值就是开头的数。

取中点,比较中点和开头的值的关系。如果left的值小于middle的值,那么最小值在右半段(这说明left到middle一直是升序),反之在左半段

时间复杂度:O(logn),空间复杂度O(1)

/** * @param {number[]} nums * @return {number} */ var findMin = function(nums) { let left = 0, right = nums.length - 1; if (nums[left] < nums[right]) return nums[0]; while (left !== right) { let middle = ~~((left + right) / 2); if (middle === left) { if (nums[left] > nums[right]) { left = right; } else { right = left; } } else if (nums[middle] > nums[left]) { left = middle; } else { right = middle; } } return nums[left]; };

5.寻找峰值

题目:

峰值元素是指其值大于左右相邻值的元素。

给定一个输入数组 nums,其中 nums[i] ≠ nums[i+1],找到峰值元素并返回其索引。

数组可能包含多个峰值,在这种情况下,返回任何一个峰值所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞。

思路:先用迭代的方式,因为开头和末尾可视为负无穷,那么第一个比后面的数大的数就是峰值

时间复杂度:O(n),空间复杂度O(1)

/** * @param {number[]} nums * @return {number} */ var findPeakElement = function(nums) { for (let i = 0; i < nums.length - 1; i++) { if (nums[i] > nums[i + 1]) { return i; } } return nums.length - 1; };

也可以用二分法。如果middle的值比下一个值更大,那么肯定有个峰值在左侧,反之肯定有个峰值在右侧

时间复杂度:O(logn),空间复杂度O(1)

/** * @param {number[]} nums * @return {number} */ var findPeakElement = function(nums) { let left = 0; let right = nums.length - 1; while (left < right) { let mid = ~~((left + right) / 2); if (nums[mid] > nums[mid + 1]) { right = mid; } else { left = mid + 1; } } return left; };

 

最新回复(0)