代码随想录DAY1
数组理论基础
c++ vector 和数组有差别,虽然底层是用array实现的
C++ 二维数组是把二维的数组接在一起形成一条直线
二分查找
- 给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
1 | 输入: nums = [-1,0,3,5,9,12], target = 9 |
1 | 输入: nums = [-1,0,3,5,9,12], target = 2 |
- 你可以假设 nums 中的所有元素是不重复的。
- n 将在 [1, 10000]之间。
- nums 的每个元素都将在 [-9999, 9999]之间。
暴力解
- 错误的测试代码01
1 | int search(int* nums, int numsSize, int target) { |
there are a few issues with the code:
Array Indexing: In C and many other programming languages, array indexing starts at 0, not 1. Therefore, you should initialize
i
to 0, not 1.Loop Termination: The code uses an infinite loop (
for(;;)
) and relies oni
andj
being equal to terminate. This may lead to an infinite loop if the target element is not found in the array. You should add a condition to break the loop when necessary.Missing Return: If the target element is not found in the array, the function does not have a return statement. You should return -1 as the default value to indicate that the target is not in the array.
Here’s an improved version of the code:
1 | int search(int* nums, int numsSize, int target) { |
This code initializes i
to 0 and j
to numsSize - 1
, uses a while
loop with proper termination conditions, and returns -1 if the target element is not found in the array.
以上解法是暴力求解,用二分法要重新设计
二分法
二分查找涉及的很多的边界条件,逻辑比较简单,但就是写不好。例如到底是 while(left < right)
还是 while(left <= right)
,到底是right = middle
呢,还是要right = middle - 1
呢?
写二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。
写二分法,区间的定义一般为两种,左闭右闭即[left, right],或者左闭右开即[left, right)。
借助C++库函数
1 | class Solution { |
lower_bound
函数[first, last)
中查找第一个不小于(即大于或等于)指定 val
的元素。这个范围应该是已排序的,或者至少以 val
为基准进行了划分。该函数返回指向该元素的迭代器,表达式lower_bound(nums.begin(), nums.end(), target) - nums.begin()
计算找到的元素相对于向量开头的索引。
搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
你可以假设数组中无重复元素。
示例 1:
- 输入: [1,3,5,6], 5
- 输出: 2
示例 2:
- 输入: [1,3,5,6], 2
- 输出: 1
示例 3:
- 输入: [1,3,5,6], 7
- 输出: 4
示例 4:
- 输入: [1,3,5,6], 0
- 输出: 0
1、 错误测试1
1 | class Solution { |
2. 暴力解
1 | class Solution { |
二分法解
题目的前提是数组是有序数组,这也是使用二分查找的基础条件。
第八行必须要在位移运算的时候带括号,否则会进行无效位移导致超时
在排序数组中查找元素的第一个和最后一个位置
给你一个按照非递减顺序排列的整数数组 nums
,和一个目标值 target
。请你找出给定目标值在数组中的开始位置和结束位置。
如果数组中不存在目标值 target
,返回 [-1, -1]
。
你必须设计并实现时间复杂度为 O(log n)
的算法解决此问题。
示例 1:
1 | 输入:nums = [5,7,7,8,8,10], target = 8 |
示例 2:
1 | 输入:nums = [5,7,7,8,8,10], target = 6 |
示例 3:
1 | 输入:nums = [], target = 0 |
写两个二分来进行搜索左边界和右边界
手写代码和错误修正
1 | class Solution { |
- 循环条件错误:在
get_rb
和get_lb
函数中,循环条件while(r <= l)
应该是while(l <= r)
。这是因为需要在l
小于或等于r
时继续搜索。 - 边界更新逻辑:在
get_lb
函数中,当nums[mid] > target
时,您应该直接将r
设置为mid - 1
,而不是在额外的lb = r
之后。同样,在get_rb
函数中,当nums[mid] < target
时,您应该将l
设置为mid + 1
而不是在之后的rb = l
。 - 返回值处理:在
searchRange
函数中,当找到有效的下界和上界时,您应该返回{lb, rb}
而不是{lb + 1, rb - 1}
。根据您的辅助函数定义,lb
和rb
已经是目标值的正确边界。
1 | class Solution { |
总结
二分法主要有两种左闭右闭和左闭右开,这两周模式分别对应了l<=r和l<r的边界条件,以及分别对应了mid位置不同的取值方法,mid不能取越界的部分,左闭右闭就必须取mid+1,mid-1,左闭右开就只能取mid+1和mid,来分别做二分之后新的边界。