引言
对于程序员来说, 算法二字应该都不陌生!!
首先什么是 算法
? 它是解决问题的一个思路, 至于使用什么语言去完成不是它的重点, 你可以使用 Java
也可以使用 JS
甚至可以是任何你所熟悉的语言!!
那么我们为什么要学习 算法
呢? 即便我们没研究过它, 我们不是照样一样能完成手头的工作嘛? 其实在工作中, 业务开发过程中, 我们肯定会遇到一个个问题, 并通过编码解决它, 这中间其实或多或少都会用到 算法
只是我们可能对 算法
没怎么研究, 所以并没有感知到!!!
那么学习 算法
到底能给我带来什么呢?
- 对于求职者而,
算法
能力能成为你拿到优质offer
的敲门砖, 不管是大厂还是国外面试都要求有一定算法
能力 算法
是解决某类问题的的思路, 通过它可以很好培养我们的逻辑思维能力- 也行在平常工作中你用不到你所学的
算法
, 但是当遇到一些复杂问题, 它可以帮助你在复杂的情况中更好地分析问题、解决问题、从而寻找出最优的解决问题的思路 - 让我们自己研究
算法
很难, 但是目前很多算法
都是几代人努力, 研究出来的, 我们可以直接研究学习, 站在巨人的肩膀上成就自我, 何乐而不为呢 算法
是一种解决问题的思路和方法, 也行有机会应用到生活和事业的其他方面呢- 长期来看, 大脑思考能力是个人最重要的核心竞争力, 而算法是为数不多的能够有效训练大脑思考能力的途径之一
所以相信我, 学习
算法
应该是一件很酷的事情, 所以这里我专门整了个专栏, 希望我能坚持下去吧....
一、题目介绍
- 描述:
- 给定一个整数数组
nums
和一个目标整数target
- 请从数组
nums
中找出两个
值, 它们的和为目标整数target
- 注意 📢 最终我们需要返回这两个值在数组中的下标
- 补充:
- 只会存在一个有效答案, 也就是说你只需要找到一对数值就行
- 数组中同一个元素在答案里不能重复出现
- 可以按任意顺序返回答案
- 示例:
js
示例 1:
输入: nums = [2, 7, 11, 15], target = 9
输出: [0, 1]
解释: 因为 nums[0] + nums[1] == 9, 返回 [0, 1]
示例 2:
输入: nums = [3, 2, 4], target = 6
输出: [1, 2]
示例 3:
输入: nums = [3, 3], target = 6
输出: [0, 1]
下面开始做题....
二、 暴力破解
这里最简单做法就是, 直接对数组进行嵌套循环
- 第一层循环, 遍历整个数组
- 第二层循环, 遍历所有剩余的数据
- 这时就能拿到两个值, 判断它们之和是否是等于目标值
- 结束循环的条件是: 拿到的两个值等于目标值, 这时直接
return
出这两个值的索引, 从而结束循环
js
const nums = [2, 7, 11, 15]
const target = 9
const twoSum = (nums = [], target = 0) => {
for (let i = 0; i < nums.length; i ++) { // 第一层循环, 遍历整个数组
for (let j = i + 1; j < nums.length; j ++ ) { // 第二层循环, 遍历所有剩余的数据
// 判断, 两数之和是否等于 target, 如果是则结束循环
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
}
twoSum(nums, target)
最后运行时间为 60ms
整体时间复杂度为 O(n^2)
,
三、使用 JS 原生方法
其实这里思路本质上和上面那个方法是一样的, 只是这里将第二个循环的处理逻辑改用 JS 的原生方法来实现, 相对来说可能更好理解一点:
- 首先先循环, 遍历整个数组
- 然后每次循环, 都尝试使用
indexOf
从剩余的数据中找到一个, 和当前值
相加能够等于目标值
的数据, 当然indexOf
最终拿到的是对应值的索引, 刚好符合我们的要求(因为这里我们也只是需要值的索引) - 结束循环的条件是: 找到和
当前值
相加等于目标值
的索引
js
const nums = [2, 7, 11, 15]
const target = 9
const twoSum = (nums = [], target = 0) => {
for (let i = 0; i < nums.length; i ++) { // 首先先循环, 遍历整个数组
// 尝试从剩余的数据中找到一个, 和 `当前值` 相加能够等于 `目标值` 的数据(拿到的是索引值)
const nextIndex = nums.indexOf(target - nums[i], i + 1)
// 如果找到, 则结束循环
if (nextIndex !== -1) {
return [i, nextIndex]
}
}
}
twoSum(nums, target)
最后运行时间为 60ms
时间复杂度还是 O(n^2)
四、进阶(一次循环)
上面两种方法从时间复杂度上来看都是 O(n^2)
, 下面我们尝试只进行一次循环来解决我们的问题!
这里思路其实也简单, 就是通过空间来换取时间, 所谓空间可以理解为内存空间, 就是用一个数据, 可以是对象可以是 map
也可以是任何数据结构的数据, 来记录数据, 从而减少我们循环的次数(代码运行时间)。
那么这里我们解决思路就是:
- 遍历循环整个数组
- 在最开始创建一个
map
用于记录遍历过的数据,map
的key
是数组中的数值
,value
则是我们需要的索引
- 那么在每次循环时, 我们就可以使用
map
来查找, 和当前值
相加等于目标值
的数字是否出现过, 如果出现过就直接拿到它的索引和当前值的索引进行返回 - 结束循环的条件是:
map
中存在某个值, 和当前循环的值相加等于目标值
js
const nums = [2, 7, 11, 15]
const target = 9
const twoSum = (nums = [], target = 0) => {
const map = new Map() // 用于记录遍历过的数据, key 是数组中的数值, value 则是我们需要的索引
for (let i = 0; i < nums.length; i ++) { // 循环数组
const targetNum = target - nums[i] // 目标数字: 和当前数值相等能够等于 target 的值
if (map.has(targetNum)) { // 判断之前存的值是否有目标数值
return [map.get(targetNum), i] // 有则返回索引
}
map.set(nums[i], i) // 没有, 则将当前值作为 key, 索引作为值存到 map 中
}
}
twoSum(nums, target)
最后运行时间为 62ms
时间复杂度 O(n)