学习目标:
- 掌握几种基本的算法结构,实现算法问题解决的基本思维
学习内容:
题目:搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
请必须使用时间复杂度为 O(log n)
的算法。
示例 1:
输入: nums = [1,3,5,6], target = 5 输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2 输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7 输出: 4
题解明细:
思路:用内置函数解答,比较简单
学习笔记:
1、先判断列表nums中是否有该元素,有则输出该元素的下标即可。没有那就加入该列表,并用sort进行排序,最后返回该元素的下标即可。
2、看到一个算友整理的关于二分法的思维导图不错,这里可以分享给大家:
后续可以参考模版进行类题的输出:
1.先确定查找区间的左右开闭情况
这是写二分查找最根本的问题, 因为根据不同的情况, 下面的操作是完全不同的, 这也是很多朋友感觉二分查找难写的地方, 这里为了方便, 我直接将区间定义为左闭右开[left, right)的形式, 这里根据自己的习惯最好将区间完全固定住, 以后都按照一种模式来写, 如果你习惯左闭右闭也同样固定住, 这样不用每次都纠结.
固定为左闭右开的好处是: 很多函数中对区间的操作都是左闭右开的形式, 这算是语言的特点吧, 比如字符串中截取子串长度, 列表的截取都是左闭右开的形式, 这样就可以和语言特点相对应.
另一个好处就是在返回值的问题上, 下文会提到.
2.数组长度
当区间固定住为左闭右开, 数组长度就固定为[0, len(nums)), 这也是左右端点的初值, 如为左闭右闭, 那长度就是[0, len(nums)-1]了.
3.循环退出条件
这里也要特别注意, 代码为 while left < right 因为区间是右开, 所以不能有=, 否则区间就不存在了, 这样写循环控制的另外一个好处就是在退出循环时, 必然满足 left == right, 这样在最后的返回值就可以任意返回了, 因为它们完全相等, 而不用去纠结该返回哪个端点了.
4.中间值的写法
写法为 mid = left + (right - left)//2 , //2在Python中表示整除, /2在Python中表示正常除, 是有余数的, 这点和其他语言有所不同. 这样写的好处也非常简单, 就是为了防止大数溢出, 因为写成(left + right)//2时, 当数比较大时, left + right是有溢出的风险的, 这种写法就可以避免了.
5.中间值和目标值的比较关系
if nums[mid] < target 这个比较关系涉及到上图中4和5的两种变形, 写成什么样的形式是不固定的, 这里是根据题目要求来写的.
简单来说就是如果是<, 那么当nums[mid]达到是比target小的数中的最大的数的时候, 通过if条件进入, left的值为mid+1后的值必然是大于等于target的值, 这时候可能取到和target相等的位置.
但是当换成≤时, 当nums[mid] == target时, 仍然会进入if条件, left的值为mid+1后的值必然是大于target的值, 这时候就不可能取到和target相等的位置. 就会收敛到第一个大于target的位置.
这点还是有点复杂的, 可以好好多看几遍.
第5条是说数组由递增改为递减, 其实只要将判断条件反过来就可以了, 还是很好理解的.
6.左右区间的变化
区间的变化完全取决于区间的定义, 因为左闭, 所以左区间为left = mid+1, 因为右开, 所以右区间变换为right = mid, 其实真实取的值也就是mid-1
7.最终返回值
最后的返回值上文已经说过了, 随便返回哪个都可以
这个模板算是比较好记忆, 也比较简洁的模板了, 如果你也比较习惯左闭右开的区间, 直接理解记住此模板就可以了, 如果你习惯左闭右闭的区间, 那稍微更改下也就可以了.
学习产出:
- CSDN 技术笔记 1遍
- CSDN
- leetcode 题目闭环 1个
- . - 力扣(LeetCode)