【LeetCode刷题笔记】一维数组

1.两数之和

解题思路:
  • 哈希 ,每次循环将 元素值 和对应 下标 放入 map 中,每次更新 map 之前先判断一下,如果 map 中已经包含 target - nums[i] 的  key ,则找到答案,返回 当前下标 和之前的 key 对应的 下标

167. 两数之和 II - 输入有序数组

解题思路:
  • 1. 对撞指针 从  开始, 从  N - 1  开始,搜索  nums[L] + nums[R]  的和,如果 等于  target ,就返回  L + 1  和  R + 1 , 否则如果 小于  target  就让  L++ ,如果 大于 target  就让  R--
解题思路:
  • 2. 二分查找 ,既然数组是有序的,那么在一个有序数组中查找固定目标值,自然想到的就是二分查找,对于 [0, N - 1] 区间的每个元素 nums[i] ,  到剩余区间 [i + 1, N - 1] 二分查找 元素为 target - nums[i] 的下标 index ,如果找到就返回  i + 1  和  index + 1

170. 两数之和 III - 数据结构设计

解题思路:
  • 1. 数组 + 排序 + 对撞指针 ,参考167.  创建一个 足够大的数组 来存储数字,设置一个标志位 isSorted 表示是否排序,每次调用 find() 先判断有没有排序,如果未排序就先排序,将 isSorted 标记为 true , 然后进行 对撞指针 查找两数之和等于 value 的,每次调用 add() 时将 isSorted 标记为 false .

注意这里的对撞指针的L指针使用 nums.length - i 即可,因为 表示数组当前实际使用的大小,排序后已使用的部分一定是从 nums.length - i 开始到数组的末尾,形如 [-100001,-100001,-100001,.........,3, 7, 8, 12, 23,   94]

当然,上面代码你也可以选择在add方法中进行排序,效果是一样的,就看add和find方法哪个调用的频率高了。

解题思路:

  • 2. 哈希 ,在 add() 方法中使用 Map 记录每个元素 出现的次数 ,在 find() 方法中,每次遍历 map 的所有 key , 判断  value - key  这个数是否存在于 map 中,如果存在,那么:
  • 1)如果该数和 key 的值 不同 ,则一定存在解,
  • 2)如果该数和 key 的值 相同 ,则该数在 map 中必须至少出现 2 次才有解。

 解释一下上面代码中关键的部分,即当 map 中包含 num = value - key 时:

1)如果 num 和 key 不是同一个数,肯定有解,这很好理解,比如 value = 10, key = 3,那么 num = 10 - 3 = 7,只要 7 存在于 map 中,且 7 != 3 则一定有解

2)如果 num 和 key 相同,例如 value = 10,  key = 5,那么 num = 10 - 5 = 5,也就是 value 此时是由两个相同的数字之和构成,因此,map 中必须至少有 2 个 5 才行

653. 两数之和 IV - 输入 BST

解题思路:
  • 1. 中序遍历 + 对撞指针 ,先对二叉搜索树进行 中序遍历 ,可以得到一个 有序数组 ,再用 对撞指针 在有序数组中查找。
解题思路:
  • 2. DFS + 哈希 ,对二叉搜索树进行 DFS遍历 ,每次访问一个节点就将该节点值加入到HashSet 中,但是在这之前先判断一下 target - 当前节点值 是否已经包含在 HashSet 集合中,如果已经存在,则存在解,返回 true 。否则将 递归调用左右子树 的返回结果作为当前递归函数的返回值,左右子树有一个返回 true 即可。
  • 递归终止:空树返回 false 表示不存在解。

15. 三数之和 

解题思路:
  • 排序 + 对撞指针 ,第一步先 排序 ,然后外层循环中 i 枚举 [0, N - 3] ,固定数字 nums[i] ,在内层循环中使用 对撞指针 求解, L i + 1 开始, R N - 1 开始,找 nums[i] + nums[L] + nums[R] == 0 的。
  • 如果三数之和等于 0,就将三个数收集到答案结果集中,然后 L++,R-- ,此时  和  两头都要通过 while循环去重
  • 如果三数之和 小于 0 ,让 L++
  • 如果三数之和 大于 0 ,让 R--
  • 优化:外层循环中首先判断一下如果 当前元素大于 0 直接跳出 ,不用进行内层循环了,因为是排序后的,后面的肯定都 大于 0 ,不可能得到三数之和 等于 0 的。
  • 注意:进入内层循环的对撞指针之前,外层循环的 nums[i] 和 nums[i - 1] 需要去重判断,去重判断的方法:比较当前的跟前一个的值,如果相等则跳过

 

解释一下上面代码中的3处去重的作用:

  • “跳过重复元素 ①”  :例如 [-3, -3, -3, -3, -3, 1, 2],如果选择了第一个 -3 和 1, 组成和为 0 的三元组 [-3, 1, 2],那么后面的连续相同的 -3 就需要跳过,否则就会得到若干个重复的三元组[-3, 1, 2],这跟题目要求不符
  • “去重②” 和 “去重③” 处的代码,其实等价于下面代码的写法:
while (L < R && nums[L] == nums[L + 1]) L++;
L++;
while (L < R && nums[R] == nums[R - 1]) R--;
R--;
  • 比如 [-2, -1, -1, -1, 3, 3, 3],当 i = 0, L = 1, R = 6,即将[-2, -1, 3]收集答案后,L 和 R 就需要排除相邻重复的 -1 和 3,否则就会得到若干个重复的三元组[-2, -1, 3],这跟题目要求不符

注意:本题在LeetCode上最新版本的提示条件改了 3 <= nums.length <= 3000,否则还需要添加一个特判 N < 3 时,return res。

18. 四数之和

解题思路:
  • 同15. 三数之和,也是 先排序 ,只不过 外面多套一层for循环 ,第一层 i [0, N - 4] 遍历,第二层 j [i + 1, N - 3] 遍历,最内层用 对撞指针 L j + 1 开始, R N - 1 开始,搜索 nums[i] + nums[j] + nums[L] + nums[R] == target  的。
  • 注意点:第一层的  i 和第二层的 都需要 去重 操作, 等于target 时收集完答案之后,移动  L 和 R  时也需要 去重 判断。

16. 最接近的三数之和 

解题思路:
  • 类似15.三数之和,这题求的不是三个元素的集合,而是三个数的和,当  sum == target  时,直接返回 sum 即可,否则就更新最接近 target 的答案,并移动 L R 指针。
  • 初始时设一个变量 closest 表示最接近 target 的和,开始时设置为一个比数组中任意三数之和还小的值,需要更新 closest 时,看如果  abs(sum - target) < abs(closest - target) ,就将 closest 更新为 sum

 31. 下一个排列

 

解题思路:
  • 先从 右往左 第一个 出现的 降序点 】(当前的小于其右边的),找到后记住这个位置 i ,然后再从 右往左 第一个 比之前找到的 降序点 】大 的位置,记为 j , 然后 交换 i 和 j  位置的值,最后 反转 i 之后的所有元素 ,即得到下一个全排列。
  • 如果第一步中没有找到 降序点 ,也就是 i == -1 ,那么说明从右往左都是升序的,也就是 从左往右 是一个 降序序列 ,如54321,则 下一个排列 应该返回 字典序最小的排列 。这通过反转整个数组即可得到。
  • 反转 操作通过 对撞指针 可以实现,从两头往中间逼近,不断的交换两头指针的元素。

 

下图是上面算法的执行动画过程:

输入:   1 5 8 4 7 6 5 3 1
第一步:1 5 8
<
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

川峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值