终于开始代码随想录day1了,之前也跟着ppt大概刷过一点,但是不全面也没有坚持下来,更没有写博客,那现在就当作从头开始,加油!
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
提示:
- 你可以假设 nums 中的所有元素是不重复的。
- n 将在 [1, 10000]之间。
- nums 的每个元素都将在 [-9999, 9999]之间。
一、二分法易错点:
- while循环中的条件到底是left < right还是left <= right?
- 边界的取值问题,比如: if(nums[midddles] > target) { right = middle;(还是right = middle- 1;呢?) }
二、二分法的大概思路:
首先,二分法的前提是:有序数组,且数组中无重复元素。前者因为二分的比较基于大小的对比,后者则如果又重复元素会导致返回值不唯一。
然后就是正题,为什么写二分法会乱?最主要的原因就是区间的定义:区间的定义是不变量,不变量就是区间的定义。
循环不变量原则:在while中寻找每次边界的处理都要坚持根据区间的定义来操作。
回到易错点一: while的条件取决于区间的定义,二分法的区间定义一般为两种,要么是左闭右闭[left , right],要么是左闭右开[left, right)。 对于左闭右闭,[left, right]是有意义的(当left = right时)故while的条件得是(left <= right)。
易错点二: 在if (nums[middle] > target) 时right 要赋值为middle - 1,因为在if里已经判断出nums[middle] 一定不等于 target,不需要放到下一次的判断中,只需要取当前nums[middle] 的前一个当作为右边界,放入后续的判断。懂得这个道理之后对于nums[middle] < target的情况也很好解释。
那么对于左闭右开的情况,在理解前者的基础上,易错点一和二都很好理解:因为右边边界是开的,故右值不在区间内,开头的rgiht的定义就得改成int right = nums.size(); if (nums[middle] > target)时 right要赋值为middle,如果还是middle - 1的话就会漏判一个nums[middle - 1]。
以上就是对于二分易错点的基本解析,下面是代码:
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 1;//数组的定义为左闭右闭
while(left <= right){//终止条件为左指针大于右指针
int mid = left + (right - left)/2;
//取中点(两个int相加有时候会越界,故这种写法比(left + light)/2 要更加稳妥
if(nums[mid] == target) {
return mid;//返回中点下标
}
else if (nums[mid] < target) {
left = mid + 1;
}
else {
right = mid -1;
}
}
return -1;//不存在目标值则返回-1
}
};
27. 移除元素
力扣题目链接(opens new window)
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。 元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。 示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。 你不需要考虑数组中超出新长度后面的元素。 示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。 你不需要考虑数组中超出新长度后面的元素。
首先明确数组的基础知识:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。
那么我们可以先试试暴力解法,就是两层for第一层遍历数组元素,第二层更新数组。 代码如下:
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for( int i = 0; i < size; i++) {
if(nums[i] == val) {
for(int j = i + 1; j < size; j++) {
nums[j - 1] = nums[j];
//暴力解法:把等于val值的nums[i]后面所有元素往前移一个单位顶替掉nums[i]
}
size--;
i--;//防止两重复的导致漏判,得将i往前移保证下次的i循环是从当前位置开始
}
}
return size;
}
};
双指针的思路其实就是在一层for循环上进行数组更新,代码如下:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
if (val != nums[fastIndex]) {
nums[slowIndex++] = nums[fastIndex];
}
}
return slowIndex;
}
};
另一种双指针
public:
int removeElement(vector<int>& nums, int val) {
int l = 0, r = nums.size();
while(l < r) {
if(nums[l] == val) {
nums[l] = nums[r - 1];
r--;
} else {
l++;
}
}
return l;//因为l从0开始,每当有不等于val值的元素就+1,故为新数组长度
}
};
今天的学习内容基本上都是代码随想录的,包括视频和文章,[这里大概放个链接](代码随想录 (programmercarl.com))因为是第一天在时间上没有安排充足,做得很仓促,明天再刷点今天的内容并且整理一下当作复习,再学习明天的内容和预习后天的内容,有时间还得看几遍c++primer。