diff --git a/top-interview-leetcode150/double-point/11盛水最多的容器.js b/top-interview-leetcode150/double-point/11盛水最多的容器.js new file mode 100644 index 0000000..1faa508 --- /dev/null +++ b/top-interview-leetcode150/double-point/11盛水最多的容器.js @@ -0,0 +1,79 @@ +/** + * https://leetcode.cn/problems/container-with-most-water/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} height + * @return {number} + */ +const maxArea = function (height) { + +}; + +/* +暴力解法,组合所有的可能,得出最大的面积,会超时 +*/ +function f1(height) { + const n = height.length; + let mArea = -Infinity; + let cArea;// 计算的面积 + for (let i = 0; i < n; i++) { + for (let j = i + 1; j < n; j++) { + cArea = Math.min(height[i], height[j]) * (j - i); + if (cArea > mArea) mArea = cArea; + } + } + return mArea; +} + +/* +使用双指针,定义一个left和一个right,首先判断height[left] 和 height[right]两者直接谁底,因为盛水量是底的那一端决定的, +计算当前值并且使用mArea保存起来这个值,之后继续移动地的那一侧,假设height[right]比height[left]底,那么就right--,然后继续 +比较,反之left++,循环条件为left < right +*/ +function f2(height) { + let mArea = -Infinity; // 能盛水的最大面积 + let cArea; // 缓存计算的面积,避免重复计算 + let left = 0; + let right = height.length - 1; + while (left < right) { + cArea = Math.min(height[left], height[right]) * [right - left]; + if (cArea > mArea) mArea = cArea; + if (height[right] > height[left]) { // 如果右端比左端高,移动底的那一段 + left++; + } else { + right--; + } + } + return mArea; +} + +/* +优化:假设当前右边比左边高,那么我们应该移动左边,面积不仅仅是由两端高度较低的那一端决定的,同时也由right - left这个底部决定, +在移动的过程中,这个底部一定是不断变小的,所以当这个决定性的最短边小于之前的最短边,那就没必要计算了,100%小于之前的结果,所以 +应该定义一个记录有效边高度的变量,用来跳过不必要的判断,以此提高小效率 +*/ +function f2(height) { + let mArea = -Infinity; // 能盛水的最大面积 + let cArea; // 缓存计算的面积,避免重复计算 + let left = 0; + let right = height.length - 1; + + while (left < right) { + cArea = Math.min(height[left], height[right]) * (right - left); + if (cArea > mArea) mArea = cArea; + + if (height[left] < height[right]) { + let prevLeft = height[left]; + // 增加跳过无效left的逻辑 + while (left < right && height[left] <= prevLeft) { + left++; + } + } else { + let prevRight = height[right]; + // 增加跳过无效right的逻辑 + while (left < right && height[right] <= prevRight) { + right--; + } + } + } + return mArea; +} + diff --git a/top-interview-leetcode150/double-point/15三数之和.js b/top-interview-leetcode150/double-point/15三数之和.js new file mode 100644 index 0000000..12f5914 --- /dev/null +++ b/top-interview-leetcode150/double-point/15三数之和.js @@ -0,0 +1,85 @@ +/** + * https://leetcode.cn/problems/3sum/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} nums + * @return {number[][]} + */ +const threeSum = function (nums) { + +}; + +/* +先对数组排序,然后遍历数组,如果遍历的当前元素大于零则表示后面的元素不可能组合在一起和为0,如果当前元素小于零那么就对剩余的所有元素 +使用两数之和的方法寻找target=当前元素的相反数,如果找到就返回这几个元素,然后遍历下一个元素 +*/ +function f1(nums) { + nums.sort((a, b) => a - b); // 先对数组排序 + const result = []; // 结果数组 + for (let i = 0; i < nums.length; i++) { + // 如果当前元素大于零直接结束循环,后面的查找没意义 + if (nums[i] > 0) break; + // 如果当前元素等于前面的元素也跳过 + if (i > 0 && nums[i] === nums[i - 1]) continue; + // 按照两数之和的方法寻找nums[i] 的相反数,也就是target == -nums[i] + let left = i + 1; + let right = nums.length - 1; + while (left < right) { + const target = nums[left] + nums[right]; + if (target === -nums[i]) { + result.push([nums[i], nums[left], nums[right]]); // 把查到的结果放入结果集中 + // 继续寻找往下找 + // 跳过重复元素 + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + left++; + right--; + continue; + } + if (target < -nums[i]) { + left++; + continue; + } + if (target > -nums[i]) { + right--; + continue; + } + } + } + return result; +} + +/* +使用ifelse 减少判断逻辑 +*/ +function f2(nums) { + nums.sort((a, b) => a - b); // 先对数组排序 + const result = []; // 结果数组 + + for (let i = 0; i < nums.length - 2; i++) { + // 如果当前元素大于零直接结束循环,后面的查找没意义 + if (nums[i] > 0) break; + + // 如果当前元素等于前面的元素也跳过 + if (i > 0 && nums[i] === nums[i - 1]) continue; + + let left = i + 1; + let right = nums.length - 1; + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + + if (sum === 0) { + result.push([nums[i], nums[left], nums[right]]); + // 跳过重复元素 + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + left++; + right--; + } else if (sum < 0) { + left++; + } else { + right--; + } + } + } + + return result; +} diff --git a/top-interview-leetcode150/double-point/167两数之和II.js b/top-interview-leetcode150/double-point/167两数之和II.js new file mode 100644 index 0000000..9bf5d52 --- /dev/null +++ b/top-interview-leetcode150/double-point/167两数之和II.js @@ -0,0 +1,27 @@ +/** + * https://leetcode.cn/problems/two-sum-ii-input-array-is-sorted/?envType=study-plan-v2&envId=top-interview-150 + * @param {number[]} numbers + * @param {number} target + * @return {number[]} + */ +const twoSum = function (numbers, target) { + +}; + +/* +利用数组有序的特性,使用首位指针,不断的缩小范围找到可能的值 + */ +function f1(numbers, target) { + let left = 0; + let right = numbers.length - 1; + const temp = Infinity; + while (left < right) { + const temp = numbers[left] + numbers[right]; + if (temp == target) { + return [left + 1, right + 1]; + } if (temp > target) { + right--; + } else { left++; } + } + return []; // 没有找到,不过这个题目测试用例保证一定有解 +} diff --git a/top-interview-leetcode150/numbers-and-strings/68文本左右对齐.js b/top-interview-leetcode150/numbers-and-strings/68文本左右对齐.js new file mode 100644 index 0000000..67ff4f8 --- /dev/null +++ b/top-interview-leetcode150/numbers-and-strings/68文本左右对齐.js @@ -0,0 +1,9 @@ +/** + * https://leetcode.cn/problems/text-justification/description/?envType=study-plan-v2&envId=top-interview-150 + * @param {string[]} words + * @param {number} maxWidth + * @return {string[]} + */ +const fullJustify = function (words, maxWidth) { + +}; diff --git a/top-interview-leetcode150/sliding-Window/3无重复字符的最长子串.js b/top-interview-leetcode150/sliding-Window/3无重复字符的最长子串.js new file mode 100644 index 0000000..9323e42 --- /dev/null +++ b/top-interview-leetcode150/sliding-Window/3无重复字符的最长子串.js @@ -0,0 +1,57 @@ +/** + * https://leetcode.cn/problems/longest-substring-without-repeating-characters/?envType=study-plan-v2&envId=top-interview-150 + * @param {string} s + * @return {number} + */ +const lengthOfLongestSubstring = function (s) { + +}; + +/* +暴力解法,定义左右两个指针,左指针表示这个无重复字符串的开始位置,右指针表示无重复字符的结束位置,右指针不停的往右边遍历 +每一个字符,之后再用一个循环来查找这个字符在左指针到右指针直接是否发生了重复,如果发生重复就把左指针移动到这个重复字符的 +后一个位置,之后继续移动右指针 +*/ +function f1(s) { + if (s.length === 0) return 0; + if (s.length === 1) return 1; + let left = 0; // 定义左指针 + let maxLen = 0; // 最长长度 + for (let right = 1; right < s.length; right++) { + // 从left到right,检查当前字符是否发生了重复 + for (let i = left; i < right; i++) { + if (s[i] === s[right]) { + left = i + 1; + break; + } + } + maxLen = Math.max(maxLen, right - left + 1); + } + return maxLen; +} + +/* +利用hash优化,我们在查找left到right直接是否有重复字符串的时,使用的是遍历,这样的化效率比较底,可以使用哈希表来储存每一个字符,值为 +对应的下标,如果当前字符为哈希表中的某一个值,直接取出下标赋值给left,否则继续移动right添加新的值到哈希表中 +*/ +function f2(s) { + if (s.length === 0) return 0; + + let left = 0; // 定义左指针 + let maxLen = 0; // 最长长度 + const map = new Map(); // 用于存储字符及其最新位置 + + for (let right = 0; right < s.length; right++) { + // 如果字符在哈希表中出现过,并且它的索引大于等于 left,说明当前窗口有重复字符 + if (map.has(s[right]) && map.get(s[right]) >= left) { + // 将 left 移动到重复字符的下一个位置 + left = map.get(s[right]) + 1; + } + // 更新字符的最新位置 + map.set(s[right], right); + // 计算当前窗口的长度并更新最大值 + maxLen = Math.max(maxLen, right - left + 1); + } + + return maxLen; +}