diff --git a/top-interview-leetcode150/55跳跃游戏.js b/top-interview-leetcode150/55跳跃游戏.js new file mode 100644 index 0000000..4e7b584 --- /dev/null +++ b/top-interview-leetcode150/55跳跃游戏.js @@ -0,0 +1,25 @@ +/** + * https://leetcode.cn/problems/jump-game/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {boolean} + */ +const canJump = function (nums) { + +}; + +/* +贪心算法,每一次计数自己能走的最长距离,如果在某一个位置上计算出自己的最长距离超过了数组的最大下标,则表明可以跳跃整个数组 +如果遍历完数组还是没有找到,则表明不能跳跃数组 +*/ +function f1(nums) { + let farthest = 0; // 默认能走到的最远下标,初始的时候能走到数组的第一个下表0 + for (let i = 0; i < nums.length; i++) { + // 如果能走的最远距离连当前下表都无法走到直接放回false + if (farthest < i) return false; + // 如果能走到当前下标,就比较farthest 和 当前下表加上此时的值谁大,更新farthest + farthest = Math.max(farthest, i + nums[i]); + // 如果更新后的farthest 超过了数组的最大下表,就表示数组能跳跃整个数组,直接返回true + if (farthest >= (nums.length - 1)) return true; + } + return false; +} diff --git a/top-interview-leetcode150/numbers-and-strings/121买卖股票最佳时机.js b/top-interview-leetcode150/numbers-and-strings/121买卖股票最佳时机.js new file mode 100644 index 0000000..2acf233 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/121买卖股票最佳时机.js @@ -0,0 +1,36 @@ +/** + * https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/description/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} prices + * @return {number} + * 暴力解法:遍历整个数组,找到买入点和所有卖出点的利润,如果利润大于当前利润就更新最大利润,最后返回最大利润,会超时 + */ + +function f1(prices) { + let maxProfit = 0; + const size = prices.length; + for (let i = 0; i < size; i++) { + for (let j = i + 1; j < size; j++) { + if (prices[j] - prices[i] > maxProfit) maxProfit = prices[j] - prices[i]; + } + } + + return maxProfit; +} + +/** + * @param {number[]} prices + * @return {number} + *单次遍历法,定义两个变量,一个maxProfit = 0;一个minPrice=Infinity,maxProfit和上面的暴力法是一样的,保存最大利润,maxProfit + *表示的是买入的最小价格,买入的越小后面如果遇到大于它的价格利润就越大,如果利润比maxProfit还大就可以更新maxProfit了,最后 + *返回即可 + */ +function f2(prices) { + let maxProfit = 0; + let minPrice = Infinity; + for (let i = 0; i < prices.length; i++) { + if (prices[i] < minPrice) { + minPrice = prices[i]; + } else if (prices[i] - minPrice > maxProfit) { maxProfit = prices[i] - minPrice; } + } + return maxProfit; +} diff --git a/top-interview-leetcode150/numbers-and-strings/134加油站.js b/top-interview-leetcode150/numbers-and-strings/134加油站.js new file mode 100644 index 0000000..512293c --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/134加油站.js @@ -0,0 +1,28 @@ +/** + * https://leetcode.cn/problems/gas-station/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} gas + * @param {number[]} cost + * @return {number} + */ +const canCompleteCircuit = function (gas, cost) { + let totalGas = 0; let + totalCost = 0; + let currentGas = 0; + let start = 0; + + for (let i = 0; i < gas.length; i++) { + totalGas += gas[i]; + totalCost += cost[i]; + + currentGas += gas[i] - cost[i]; + + // 如果油量不足,更新起点为下一个加油站 + if (currentGas < 0) { + start = i + 1; + currentGas = 0; + } + } + + // 如果总油量小于总油耗,无法完成一圈 + return totalGas < totalCost ? -1 : start; +}; diff --git a/top-interview-leetcode150/numbers-and-strings/135分配糖果.js b/top-interview-leetcode150/numbers-and-strings/135分配糖果.js new file mode 100644 index 0000000..37725ba --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/135分配糖果.js @@ -0,0 +1,43 @@ +/** + * https://leetcode.cn/problems/candy/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} ratings + * @return {number} + */ +const candy = function (ratings) { + +}; + +/* + 贪心算法,每个孩子至少分一颗糖果,所以一开始就初始化糖果数组所有元素为1,假设我们是中间的那个孩子,如果我评分比左边的孩子高 + 我应该就要比他多获得一个糖果,如果我们比右边的孩子评分高,我们就应该比右边的孩子多一个糖果,但是我们在处理时先从左往右处理, + 也就是如果当前孩子比左边孩子评分高就在之前的孩子基础上加上1,这样处理之后会发生一个问题,就是前面的孩子在评分比后面孩子高的时候 + 它获得的糖果已经比后面的孩子多了,所以在处理右边孩子的时候,需要比较当前值和后面孩子糖果加一谁大,取大的那个值,说起来挺难理解, + 自己用三个孩子思考一下,还是很好理解的。 + */ + +function f1(ratings) { + const n = ratings.length; + // 初始化糖果数组 + const candies = new Array(n).fill(1); + // 从左往右处理孩子,如果当前孩子的评分比之前孩子高,当前孩子的糖果数改为之前孩子的糖果数加1 + for (let i = 1; i < n; i++) { + if (ratings[i] > ratings[i - 1]) { + candies[i] = candies[i - 1] + 1; + } + } + // 从右往左遍历,如果当前孩子的评分高,就必须保证当前孩子的糖果数比后面孩子的糖果数加1大,但是经过从左往右的处理 + // 当前孩子在评分高的情况下糖果数已经大于右边孩子,这样就已经满足要求了,就不要处理了 + for (let i = n - 2; i >= 0; i--) { + if (ratings[i] > ratings[i + 1]) { + candies[i] = Math.max(candies[i], candies[i + 1] + 1); + } + } + + // 返回总糖果数 + // return candies.reduce((total, cur) => total + cur, 0); + let total = 0; + for (let i = 0; i < n; i++) { + total += candies[i]; + } + return total; +} diff --git a/top-interview-leetcode150/numbers-and-strings/238除自身以外数组的乘积.js b/top-interview-leetcode150/numbers-and-strings/238除自身以外数组的乘积.js new file mode 100644 index 0000000..6ba4959 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/238除自身以外数组的乘积.js @@ -0,0 +1,25 @@ +/** + * https://leetcode.cn/problems/product-of-array-except-self/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number[]} + */ +const productExceptSelf = function (nums) { + const n = nums.length; + const result = new Array(n).fill(1); + + // 计算每个位置左侧的乘积 + let leftProduct = 1; + for (let i = 0; i < n; i++) { + result[i] = leftProduct; + leftProduct *= nums[i]; + } + + // 计算每个位置右侧的乘积并与左侧乘积相乘 + let rightProduct = 1; + for (let i = n - 1; i >= 0; i--) { + result[i] *= rightProduct; + rightProduct *= nums[i]; + } + + return result; +}; diff --git a/top-interview-leetcode150/numbers-and-strings/274h指数.js b/top-interview-leetcode150/numbers-and-strings/274h指数.js new file mode 100644 index 0000000..9f4e585 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/274h指数.js @@ -0,0 +1,31 @@ +/** + * https://leetcode.cn/problems/h-index/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} citations + * @return {number} + */ +const hIndex = function (citations) { + +}; + +/* +思路:利用贪心算法,遍历整个数组,假设默认是h0,表示有至少零篇论文被引用了至少零次,[0][0,0] 这些都满足,我们遍历的时候检查有没有 +符合h+1的论文,如果有的化就h++,说明h指数应该增加了,否则的话就表示这个人的学术水平就是当前h,没有必要找下面的h了,直接break +*/ +function f1(citations) { + const n = citations.length; // 论文的数量 + citations.sort((a, b) => b - a); // 按引用次数降序排序 + let h = 0; // h指数 + + // 遍历排序后的数组,寻找最大符合条件的 h 指数 + for (let i = 0; i < n; i++) { + if (citations[i] >= h + 1) { // 如果当前论文引用次数大于等于 h+1 + h++; // 更新 h 指数 + } else { + break; // 如果不满足条件,则退出循环 + } + } + + return h; +} + +console.log(f1([1, 3, 1])); diff --git a/top-interview-leetcode150/numbers-and-strings/380实现数据结构.js b/top-interview-leetcode150/numbers-and-strings/380实现数据结构.js new file mode 100644 index 0000000..33b1fdb --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/380实现数据结构.js @@ -0,0 +1,50 @@ +// O(1) 时间插入、删除和获取随机元素 +// 学习目标,利用哈希表和动态数组实现高效数据结构 + +const RandomizedSet = function () { + this.nums = [];// 动态数组,储存真实数据,目的是实现O(1)时间内返回随机数据 + this.valToIndex = new Map(); // 哈希表,实现O(1)时间内查找,删除,插入 +}; + +/** + * @param {number} val + * @return {boolean} + */ +RandomizedSet.prototype.insert = function (val) { + // 先判断插入的数据是否已经存在,如果存在直接返回false + if (this.valToIndex.has(val)) return false; + // 维护插入数据的下标 + this.valToIndex.set(val, this.nums.length); + this.nums.push(val); + return true; +}; + +/** + * @param {number} val + * @return {boolean} + */ +RandomizedSet.prototype.remove = function (val) { + // 先判断元素是否存在,如果不存在就返回false + if (!this.valToIndex.has(val)) return false; + // 获取当前元素和最后一个元素的下标 + const index = this.valToIndex.get(val); + const lastVal = this.nums[this.nums.length - 1]; + // 把最后一个元素覆盖到当前位置 + this.nums[index] = lastVal; + // 更新原本最后一个元素在哈希表中维持的位置 + this.valToIndex.set(lastVal, index); + // 删除最后一个位置的数据 + this.nums.pop(); + // 删除当前元素在哈希表中维持的位置 + this.valToIndex.delete(val); + return true; +}; + +/** + * @return {number} + */ +RandomizedSet.prototype.getRandom = function () { + // 引入动态数组的目的就是为了实现O(1)返回随机数据 + const randomIndex = Math.floor(Math.random() * this.nums.length); + return this.nums[randomIndex]; +}; diff --git a/top-interview-leetcode150/numbers-and-strings/45跳跃游戏II.js b/top-interview-leetcode150/numbers-and-strings/45跳跃游戏II.js new file mode 100644 index 0000000..669f04e --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/45跳跃游戏II.js @@ -0,0 +1,54 @@ +/** + * https://leetcode.cn/problems/jump-game-ii/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number} + */ +const jump = function (nums) { + +}; + +/* +贪心算法,从后往前找,能到达最后一个位置则表明符合要求,所以找能到达最后一个位置的所有下标最小的那个,找到这个位置之后继续找能 +到达倒数第二个位置的最远的那个位置,直到发现第0个下标也符合即可 +*/ + +function f1(nums) { + let position = nums.length - 1; // 表示需要到达的位置,初始时是数组的最后一个位置 + let steps = 0; // 记录需要跳跃的步数 + while (position > 0) { + for (let i = 0; i < position; i++) { + // 从左往右遍历能到达position的位置,如果找到那么这个位置就是我们要找的 + if (nums[i] + i >= position) { + // 更新position + position = i; + steps++; + break; + } + } + } + return steps; +} + +/** + * 贪心算法, + * + */ + +function f2(nums) { + // 第一步能到达的最远位置 + let maxPos = 0; + let end = 0; + let steps = 0; + // 无需遍历到最后一个元素,最后一个元素一定能被之前的某个位置跳到 + for (let i = 0; i < nums.length - 1; i++) { + if (maxPos >= i) { // 更新能跳跃的最远位置 + maxPos = Math.max(maxPos, i + nums[i]); + // 如果当前位置已经到达能走的最远位置,则更新下一步的边界 + if (end === i) { + end = maxPos; + steps++; + } + } + } + return steps; +}