一、独立做题
1.704 二分查找
leetcode上的报错是堆溢出,出错样例是[5] 5,我真的是极其疑惑:数组元素就一个咋还能堆溢出了呢,本地调试才发现, int right = sizeof(nums)/sizeof(nums[0])-1 并不能正确的计算出数组长度,这样算出来的数组长度恒为5
!对啊,vector<int>& nums是指针,用这种方法肯定只能算出固定值啊
改掉了这个错误后,新的错误又来啦,不过这次至少执行没出错,而且这个错,emmm是我经常犯的错了,[5] 5 我输出了-1.....我的确没考虑到,只有一个元素,也就是right ==left的情况,让我试试直接简单粗暴的把right>left改成right>=left试试,哈哈哈哈哈救命变成超出时间限制的错误了,我真的很好奇这调试了会是啥情况
害,原来是在元素只有一个时会陷入端点=中点的死循环中,我开始感觉我这个思路有很严重的问题了,我似乎只能再加一个非常冗余的判断去对付这个问题,加了这个看上去非常搞笑的判断后,倒是勉强AC了,这是目前AC的代码:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right =nums.size()-1;
while(right>left){
int mid = (left+right)/2;
if((left+right)%2==0){//区间内元素为奇数,一个中位数
if(target==nums[mid]){
return mid;
}else if(target>nums[mid]){
left=mid;
}else{
right=mid;
}
}else{//区间内元素为偶数,两个中位数
if(target==nums[mid]){
return mid;
}else if(target==nums[mid+1]){
return mid+1;
}else if(target>nums[mid+1]){
left=mid+1;
}else if(target<nums[mid]){
right=mid;
}else{
return -1;
}
}
}
if(right==left&&target==nums[left]){
return left;
}
return -1;
}
};
2. 27 移除元素
在调试移除元素的时候陷入了意想不到的困难中——本来觉得这题好像挺简单的,但是一做发现立刻掉坑:在删掉目标值,把数值元素整体“前移”后,for循环就直接从下一个数值开始比较了,所以如果说有连着几个都等于目标值,就会漏掉,加上了一句冗余的"i--"让每次找到相等值后就退回到当前位置后这个问题倒是勉强解决,然而,数组元素只有一个且与目标值相等时,就会陷入无限退回到当前位置的死循环中.
这也太不讲武德了,我一直把赋值为0当做“删掉”,结果测试样例里竟然出现了目标值为0的样例,这可真是太难受了,我决定去看答案了,先把目前写的代码记录下:
int removeElement(vector<int>& nums, int val) {
int n = 0;
for(int i=0;i<nums.size();i++){
if(nums[i]==val){
n++;
nums[i]=0;//“删掉当前值”
for(int j=i;j<nums.size()-1;j++){//注意边界值!
nums[j]=nums[j+1];
nums[j+1]=0;
}
i--;//“指针”停留在当前位置
}
}
return nums.size()-n;
}
好家伙,这可太打击人了,我试了试Carl写的暴力法,竟然秒过,我怎么连个暴力法都写这么长时间都写不清楚啊,而且这题的知识点其实是快慢指针啊......
对比了Carl的暴力法答案,我在写暴力循环时错在了每次for循环都是遍历的原始的数组长度,更正后能AC的代码如下:
int removeElement(vector<int>& nums, int val) {
int n = 0;
for(int i=0;i<nums.size()-n;i++){
if(nums[i]==val){
n++;
for(int j=i;j<nums.size()-1;j++){//注意边界值!
nums[j]=nums[j+1];
}
i--;//“指针”停留在当前位置
}
}
return nums.size()-n;
}
二、看视频讲解
1.704 二分查找
这视频讲得真的太好了,“循环区间一致性”这真的太妙了:
我原来其实陷入了一个误区——“二分”法的重点根本不在每次搜索的区间长度都一定要是一半(根本没必要写成分奇数偶数的),而是能没有遗漏的遍历到每个可能的值,而始终保持自己定义的搜索区间都是合法、一致(都是左闭右闭或左闭右开)的就能很好的对付这个问题。
2. 27 移除元素
明明代码只有几行,但是理解起来却很费劲,拿干擦笔在塑料板上边写边擦地试图自己模拟了一下感觉也没把握要点
三、看文章讲解
1.704 二分查找
区间的定义就是不变量,那么在循环中坚持根据查找区间的定义来做边界处理,就是循环不变量规则。
2. 27 移除元素
文字版里又提到了一个相向双指针法
* 相向双指针方法,基于元素顺序可以改变的题目描述改变了元素相对位置,确保了移动最少元素 * 时间复杂度:O(n) * 空间复杂度:O(1)
四、重新做一遍题
1.704 二分查找
重新做题的时候感觉自己还是好傻,都没有意识到对于左闭右开的情况,直接让right=nums.size()(也就是比数组本身长一位),这样就不用对left==right的这种情况单独判断了。
2. 27 移除元素
在写相向双指针时又忘记了防溢出处理,在指针移动完毕后其实是不能保证指向的位置都是有效的,所以在更新数组前要再检查一下
为了熟悉双指针法,去做了26. 删除有序数组中的重复项,然而在自己想的时候掉入了一个搞笑的误区:我总觉得只能把右指针的指向的值复制到左指针的位置上,看了别人的题解后脑袋一嗡:卧槽原来直接复制到左指针的后一个位置上不就行了吗
如果相等,
q
后移 1 位
如果不相等,将q
位置的元素复制到p+1
位置上,p、q均后移一位
重复上述过程,直到q
等于数组长度。
上面这段话给我的启发特别大,几乎是照着这段话就能写出正确的代码,看来用语言描述清楚真的非常重要