《剑指Offer:专项突破版》 - 数组部分 JavaScript 题解
Posted 前端GoGoGo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《剑指Offer:专项突破版》 - 数组部分 JavaScript 题解相关的知识,希望对你有一定的参考价值。
《剑指Offer:专项突破版》是一个算法题集。该题单包含了程序员在准备面试过程中必备的数据结构与算法知识。具体包含:
本文来分享下数组部分题的解法~
的两个数。此时,用上一题的方法求解。最后,将结果做个去重。核心代码实现如下:
const threeSum = function (nums)
const sortNums = [...nums]
sortNums.sort((a, b) => a - b);
const res = []
for (let i = 0; i < sortNums.length - 2; i++)
const currNum = sortNums[i];
const target = 0 - currNum;
const twoSumRes = twoSum(sortNums.slice(i + 1), target);
if (twoSumRes.length > 0)
res.push(...twoSumRes.map((item) => [currNum, ...item]));
return uniq(res);
function twoSum(numbers, target)
const res = [];
let leftIndex = 0;
let rightIndex = numbers.length - 1;
for (left < right)
let left = numbers[leftIndex];
let right = numbers[rightIndex];
if (left + right === target)
res.push([left, right]);
leftIndex++;
continue;
if (left + right > target)
rightIndex--;
else
leftIndex++;
return res;
力扣地址[2]
题3 - 剑指 Offer II 008. 和大于等于 target 的最短子数组输入一个正整数组成的数组和一个正整数k,请问数组中和大于或等于k的连续子数组的最短长度是多少?如果不存在所有数字之和大于或等于k的子数组,则返回0。例如,输入数组[5,1,4,3],k的值为7,和大于或等于7的最短连续子数组是[4,3],因此输出它的长度2。
该题本质就是找出所有满足条件的数组,挑最短的那个。如果用暴力的方式出找出所有可能性,时间复杂度太高了。
一种高效的解决方案是用双指针。用两个下标的移动来找出所有满足条件的情况。具体算法如下:
开始位置的下标和结束位置下标都为0。 如果开始下标和结束下标之间(包含开始和结束)的数字之和小于k,则结束下标右移一位(和会变大),直到和不小于k或结束下标为数组结束。 开始下标右移一位(和会变小),直到和小于k或开始下标等于结束下标。 重复第二和第三步,直到结束下标为数组结束。
代码实现如下:
const minSubArrayLen = function (target, nums)
let startIndex = 0;
let sum = 0;
let count = Number.MAX_VALUE;
for (let endIndex = 0; endIndex < nums.length; endIndex++)
sum += nums[endIndex];
while (sum >= target && startIndex <= endIndex)
count = Math.min(count, endIndex - startIndex + 1);
sum -= nums[startIndex];
startIndex++; // 尝试少一个数字
return count === Number.MAX_VALUE ? 0 : count;
;
力扣地址[3]
题4 - 剑指 Offer II 009. 乘积小于 K 的子数组输入一个由正整数组成的数组和一个正整数k,请问数组中有多少个数字乘积小于k的连续子数组?例如,输入数组[10,5,2,6],k的值为100,有8个子数组的所有数字的乘积小于100,它们分别是[10]、[5]、[2]、[6]、[10,5]、[5,2]、[2,6]和[5,2,6]。
这题可以用上题一样的算法来解决。只是在细节上稍有不同。这边就不做赘述了。
代码实现如下:
const numSubarrayProductLessThanK = function (nums, target)
let startIndex = 0;
let res = 1;
let count = 0;
for (let endIndex = 0; endIndex < nums.length; endIndex++)
res *= nums[endIndex];
while (res >= target && startIndex <= endIndex)
res /= nums[startIndex];
startIndex++; // 尝试少一个数字
count += endIndex - startIndex + 1; // 1 到 n 的组合
return count;
;
力扣地址[4]
题5 - 剑指 Offer II 010. 和为 k 的子数组输入一个整数数组和一个整数k,请问数组中有多少个数字之和等于k的连续子数组?例如,输入数组[1,1,1],k的值为2,有2个连续子数组之和等于2。
假设数组长度是n,找到结束下标范围是 0 到 n-1 满足条件的子数组,就找到了所有的情况。
假设当前结束下标是 m,存在若干开始下标 x,满足和是 k。其中 x < m
。有等式1:
arr[x + 1] + arr[x + 2] + ... + ...arr[m] = k
假设,前 m 个数的和为 sum。即,有等式2:
arr[0] + arr[1] + ... + arr[m] = sum
等式2减等式1,可得:
arr[0] + arr[1] + ... + arr[x] = sum - k
有多少个这样下标x,就有多少可能的子数组。可以通过缓存数组从开始到某个数的和来减少重复计算。
代码实现如下:
const subarraySum = function (nums, k)
let count = 0;
let sum = 0;
const sum = 0: 1 ; // 和是0的值,初始化有一个。
nums.forEach((num) =>
sum += num;
count += sum[sum - k] || 0;
sumCache[sum] = (sumCache[sum] || 0) + 1;
);
return count;
;
力扣地址[5]
题6 - 剑指 Offer II 011. 0 和 1 个数相同的子数组输入一个只包含0和1的数组,请问如何求0和1的个数相同的最长连续子数组的长度?例如,在数组[0,1,0]中有两个子数组包含相同个数的0和1,分别是[0,1]和[1,0],它们的长度都是2,因此输出2。
将数组中的 0 都替换成 -1,则 0 和 1 的个数相同数组的和为0。本题就转化成了:找出最长的和是0的子数组的长度。可以用上题的算法来解本题。
代码实现如下:
const findMaxLength = function (nums)
let count = 0;
let sum = 0;
const sumIndexCache = 0: -1 ; // key 是 和, value 是下标
nums.forEach((num, i) =>
sum += num === 0 ? -1 : 1;
if (sumIndexCache[sum] !== undefined)
count = Math.max(count, i - sumIndexCache[sum]);
else
sumIndexCache[sum] = i;
);
return count;
;
力扣地址[6]
题7 - 剑指 Offer II 012. 左右两边子数组的和相等输入一个整数数组,如果一个数字左边的子数组的数字之和等于右边的子数组的数字之和,那么返回该数字的下标。如果存在多个这样的数字,则返回最左边一个数字的下标。如果不存在这样的数字,则返回-1。例如,在数组[1,7,3,6,2,9]中,下标为3的数字(值为6)的左边3个数字1、7、3的和与右边两个数字2和9的和相等,都是11,因此正确的输出值是3。
前 n 个数的和等于前 n -1 个数的和加上第 n 的数的和。缓存之前求的和,可以避免重复计算,从而降低算法的时间复杂度。
代码实现如下:
const pivotIndex = function (nums)
const sumCache = []; // 前 n 个,不包含 n
let sumTotal = 0;
nums.forEach((num) =>
sumCache.push(sumTotal);
sumTotal += num;
);
for (let i = 0; i <= nums.length - 2; i++)
if (sumCache[i] === sumTotal - sumCache[i + 1])
return i;
if (sumCache[nums.length - 1] === 0)
return nums.length - 1;
return -1;
;
力扣地址[7]
题8 - 剑指 Offer II 013. 二维子矩阵的和输入一个二维矩阵,如何计算给定左上角坐标和右下角坐标的子矩阵的数字之和?对于同一个二维矩阵,计算子矩阵的数字之和的函数可能由于输入不同的坐标而被反复调用多次。例如,输入图2.1中的二维矩阵,以及左上角坐标为(2,1)和右下角坐标为(4,3)的子矩阵,该函数输出8。
通过观察,任意的 左上角坐标 和 右下角坐标 的子矩阵的数字之和 可以转化为左上角坐标是(0,0)的几个矩阵运算:
res = sum[row2][col2] -
(row1 > 0 ? sum[row1 - 1][col2] : 0) -
(col1 > 0 ? sum[row2][col1 - 1] : 0) +
(row1 > 0 && col1 > 0 ? sum[row1 - 1][col1 - 1] : 0)
其中:
因此,缓存所有的左上角坐标是(0,0)的子矩阵的和,就可以快速的算出任意子矩阵的和。
代码实现如下:
const NumMatrix = function (matrix)
const sum = [];
for (let row = 0; row < matrix.length; row++)
sum[row] = [];
if (row === 0)
let rowSum = 0;
for (let col = 0; col < matrix[0].length; col++)
rowSum += matrix[0][col]
sum[0][col] = rowSum;
continue;
let rowSum = 0;
for (let col = 0; col < matrix[0].length; col++)
rowSum += matrix[row][col];
sum[row][col] = sum[row - 1][col] + rowSum;
this.sum = sum;
;
NumMatrix.prototype.sumRegion = function (row1, col1, row2, col2)
const sum = this.sum;
return (
sum[row2][col2] -
(row1 > 0 ? sum[row1 - 1][col2] : 0) -
(col1 > 0 ? sum[row2][col1 - 1] : 0) +
(row1 > 0 && col1 > 0 ? sum[row1 - 1][col1 - 1] : 0)
);
;
力扣地址[8]
总结解数组的算法题,在某些场景下,用双指针和缓存的技巧可以降低算法的时间复杂度。
用双指针,可以减少遍历数组的次数。难点在确定开始,结束指针的位置,移动规则,做到不遗漏情况。
用缓存可以减少重复计算。多用在求和,求乘等。关键是发现任意输入和缓存值的映射关系。
相关推荐力扣地址: https://leetcode-cn.com/problems/kLl5u1/
[2]力扣地址: https://leetcode-cn.com/problems/1fGaJU/
[3]力扣地址: https://leetcode-cn.com/problems/2VG8Kg/
[4]力扣地址: https://leetcode-cn.com/problems/ZVAVXX/
[5]力扣地址: https://leetcode-cn.com/problems/QTMn0o/
[6]力扣地址: https://leetcode-cn.com/problems/A1NYOS/
[7]力扣地址: https://leetcode-cn.com/problems/tvdfij/
[8]力扣地址: https://leetcode-cn.com/problems/O4NDxx/
《剑指Offer:专项突破版》 - 哈希表部分 JavaScript 题解
《剑指Offer:专项突破版》是一个算法题集。该题单包含了程序员在准备面试过程中必备的数据结构与算法知识。具体包含:
本文来分享下哈希表部分题的解法~
和 最大值做比较,比如:["00: 00", "23:49", "23:59"]
的最小时间差不是 10 分钟(23:59 - 23:49
),而是 1 分钟(00: 00 + 24:00 - 23:59
)。代码如下:
const ONE_DAY_MINUTE = 24 * 60
const findMinDifference = function(timePoints)
if(timePoints.length <= 1)
return 0;
const minutes = timePoints.map(time => toMinute(time));
minutes.sort((a, b) => a - b);
const diff = minutes.map((m, i) =>
if(i === 0) // 最后一个和第一个的差值。
return m + ONE_DAY_MINUTE - minutes[minutes.length - 1];
return m - minutes[i - 1];
)
const min = Math.min(...diff);
return min;
;
function toMinute(time)
const [hour, minute] = time.split(\':\').map(Number);
return hour * 60 + minute;
相关阅读题的力扣地址: https://leetcode-cn.com/problems/FortPu/
[2]题的力扣地址: https://leetcode-cn.com/problems/OrIXps/
[3]题的力扣地址: https://leetcode-cn.com/problems/dKk3P7/
[4]题的力扣地址: https://leetcode-cn.com/problems/sfvd7V/
[5]题的力扣地址: https://leetcode-cn.com/problems/lwyVBB/
[6]题的力扣地址: https://leetcode-cn.com/problems/569nqc/
以上是关于《剑指Offer:专项突破版》 - 数组部分 JavaScript 题解的主要内容,如果未能解决你的问题,请参考以下文章
《剑指Offer:专项突破版》 - 哈希表部分 JavaScript 题解
《剑指Offer:专项突破版》 - 整数部分 JavaScript 题解