携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第 19 天,点击查看活动详情
在前一篇文章中,我们通过js实现了二分查找。今天我们继续来看一下leetCode上的真题,顺便一起来回顾一下上一篇中的相关内容。
总结来说就是,给定一个递增的数组,和一个指定的值,通过数组中两个数相加,最终的和是这个给定的值,让我们通过js来实现这么一发方法,下面我们一起看一下常规的解题的方案。
常规思路
通过循环嵌套,找到数组中的一个数,然后继续去遍历下一个数,以此来进行求和,并判断最终的值与指定的值是否相等,这样的实现思路的时间复杂度是O(n^2),基本是不可以的,具体我们可以一起来看一下代码,如下:
/**
* 寻找和为 N 的两个数 (嵌套循环)
* @param arr number[]
* @param n number
* @return number[]
*/
const findTwoNumber1 = (arr: number[], n: number): number[] => {
// 需要返回两个数在数组中的值
const rest: number[] = [];
// 获取数组的长度
const len = arr.length;
// 如果数组为空,则直接返回
if (len === 0) return rest;
// 通过循环来遍历数组,时间复杂度 O(n^2)
for (let i = 0; i < len - 1; i++) {
// 获取当前值
const n1 = arr[i];
// 标记是否得到了结果
let flag = false;
// 二次循环来查找第二个值
for (let j = i + 1; j < len; j++) {
const n2 = arr[j];
// 如果两数之和等于指定的值
if (n1 + n2 === n) {
rest.push(n1);
rest.push(n2);
// 标记为以找到
flag = true;
break;
}
}
// 如果已经查找到了两个数,则直接跳出循序
if (flag) break;
}
// 如果没有找到两数之和与指定值相等的值,则直接返回空数组
return rest;
};
// 功能测试
const arr = [1, 2, 4, 7, 11, 15];
const target = 15;
console.log(findTwoNumber1(arr, target)); // [4, 11]
具体的执行效果可以狠戳这里。
虽然我们通过嵌套循环实现了这个题目,但是因为用到了嵌套循环,因此这个算法的时间复杂度是O(n^2),基本算是不可用的,因此我们还需要用另外一种思路来实现这个题目。
二分思路
在前面的题干描述中,我们知道了这个数组是递增的,因为我们可以利用这个特性,在数组中线随便找两个数组,如果它们的和大于给定的值,则需要向前面继续查找;如果它们的和小于给定的值,则需要向后进行查找。这个思路是不是跟我们在前一篇文章中说的二分查找很类似。下面我们一起来看一下用二分思路进行优化有的代码,如下:
/**
* 寻找和为 N 的两个数 (双指针)
* @param arr number[]
* @param n number
* @return number[]
*/
const findTwoNumber2 = (arr: number[], n: number): number[] => {
// 需要返回两个数在数组中的值
const rest: number[] = [];
// 获取数组的长度
const len = arr.length;
// 如果数组为空,则直接返回
if (len === 0) return rest;
// 定义双指针
let i = 0; // 头指针
let j = len - 1; // 尾指针
while (i < j) {
const n1 = arr[i];
const n2 = arr[j];
const sum = n1 + n2;
// 如果sum大于n,则尾指针要向前移动
if (sum > n) {
j--;
} else if (sum < n) {
// 如果sum小于n,则头指针要向后移动
i++;
} else {
// sum 等于 n,则表示找到了
rest.push(n1);
rest.push(n2);
break;
}
}
return rest;
}
// 功能测试
const arr = [1, 2, 4, 7, 11, 15];
const target = 15;
console.log(findTwoNumber2(arr, target)); // [4, 11]
具体的执行效果可以狠戳这里。
通过双指针的思路思想实现的代码,它的时间复杂度是O(n),下面我们一起来做一下性能分析。
性能分析
我们还是通过性能测试来对这两个方法进行分析,代码如下:
const arr = [1, 2, 4, 7, 11, 15];
const target = 15;
// 方法一
console.time('findTwoNumber1');
for (let i = 0; i < 100 * 10000; i++) {
findTwoNumber1(arr, target);
}
console.timeEnd('findTwoNumber1');
// 方法二
console.time('findTwoNumber2');
for (let i = 0; i < 100 * 10000; i++) {
findTwoNumber2(arr, target);
}
console.timeEnd('findTwoNumber2');
当我们进行测试是数组的长度约长时,循环嵌套的实现思路跟二分实现的思路,它们之间的差别就是O(n)。
最后
当一个算法的时间复杂度达到了O(n^2),那这个算法基本就是不可以的算法。当我们遇到有序的数据结构时,首先想到的一定是二分。如果我们要优化嵌套循环的题目,可以考虑"双指针"。
最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家