代码随想录算法训练营第一天
LeetCode 704. 二分查找
题目链接:704. 二分查找
文章讲解:代码随想录#704. 二分查找
视频讲解:手把手带你撕出正确的二分法
题目描述
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。
示例1
输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4
示例2
输入: nums = [-1,0,3,5,9,12], target = 2
输出: -1
解释: 2 不存在 nums 中因此返回 -1
思路
二分查找法的两个前提条件:有序数组,元素无重复
使用二分法之前,必须得搞明白边界问题,也就是区间问题。
平时遇到最多的两种区间:左闭右闭即[left, right],或者左闭右开即[left, right),※其他组合也可以解决问题
本题使用左闭右闭即[left, right],需要注意以下几点
- 因为是闭区间,区间范围为[0, numsSize - 1],所以right的初值为numsSize - 1
- 循环条件中left与right存在两者相等的情况,所以while中要使用<=
- 当中间元素middle的值大于目标值target时,需要更改right变量,由于middle已经比较过了,所以right应该等于middle-1,left同理
左闭右开可以参考代码随想录#704. 二分查找,强烈推荐!!!
参考代码
int search(int* nums, int numsSize, int target) {
// 左闭右闭的区间[left, right]
int middle;
int left = 0;
int right = numsSize - 1; // 因为是闭区间,所以整个区间为[0, numsSize-1]
while (left <= right) { // 因为是左开右开区间,所以存在left=right的情况
middle = left + (right - left) / 2; // 防止两数相加溢出
if (nums[middle] > target) {
right = middle - 1;
} else if (nums[middle] < target) {
left = middle + 1;
} else {
return middle;
}
}
return -1;
}
总结
- 一定要搞明白区间的定义,是左闭右闭,还是左闭右开,这决定后面代码的写法。
- 两数相加时注意越界、截位、位反转等情况,可以看看这篇文章《地址范围的边界值判断》
- 在调整left/right时,一定要想明白到底是middle还是middle±1
LeetCode 27. 移除元素
题目链接:727. 移除元素
文章讲解:代码随想录#727. 移除元素
视频讲解:数组中移除元素并不容易!
题目描述
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
示例1
输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。
示例2
输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,3,0,4]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。
思路
本题的解法比较多,可以使用暴力解法,需要用两层for循环,外层for循环用来遍历整个数组,里面的for循环用来调整数组(因为在数组中不能直接删除数组中的元素),这样的话时间复杂比较大。
本题可以使用双指针法,这样只需要一层for循环,通过一个快指针和慢指针在一个for循环下完成移除元素,时间复杂度比较低。
想要使用快指针和慢指针,需要搞清楚它俩的含义。
- 快指针遍历的是不包含目标元素的数组
- 慢指针用来构造新数组
参考代码
int removeElement(int* nums, int numsSize, int val) {
int fast, slow;
slow = 0;
for (fast = 0; fast < numsSize; fast++) {
if (nums[fast] != val) { // 遍历目标元素以外的元素
nums[slow++] = nums[fast]; // slow刚好跳过了目标元素
}
}
return slow;
}
总结
- 时刻要明白每个指针的含义。
- for循环时在快指针会跑过目标元素时,慢指针会+1。
- 此题也可以在一个for循环下将目标元素标记成负数的办法,同时将元素个数减1,这样不使用双指针也可以实现,不过不推荐。