代码随想录算法训练营第1天|704. 二分查找、27. 移除元素。

数组理论基础

文章链接:link


704.二分查找

leetcode题目链接:704.二分查找
文章链接:link
视频链接:link

一、做题感受&第一想法

轻松。(408学了二分查找,比较轻松地用了左闭右闭的思想写出来了。)

二、学习文章后收获

1.二分法整体思路

  • 两种思路:①左闭右闭 ②左闭右开
  • 两个易错点:①while循环条件:考虑区间合法性! ②更新low和high时需不需要加一减一:考虑区间定义!
  • mid的计算方法一直不变,一直是mid = (low+high)/2或者mid = low +(high-low)/2(防溢出)。
  • 适用范围:①有序表 ②无重复元素(因为二分法只能找到其中一个,返回不了所有)(只要看到面试题里给出的数组是有序数组,都可以想一想是否可以使用二分法。)

2.如何解决“两int型相加可能越界”的问题

mid = (left + right)/2; //error,可能越界
mid = left + (right - left)/2; //ok,防止越界

三、实现过程中的困难

1.Leedcode错误 error:control reaches end of non-void function[-Werror=return-type]

原因在于,某些编译器要求函数的结尾必须保证有返回值。如果代码中某个分支情况导致了没有经过return语句(或者看似没有经过),那么有的编译器就会报warning或error。

解决方法:在函数结尾加一个显式的return(虽然可能永远都不会经过它)。

四、代码

1.左闭右闭

int search(int* nums, int numsSize, int target) {
    int low = 0, high = numsSize - 1, mid;
    while(low<=high) //左闭右闭
    {
        mid = low + (high - low)/2; //防溢出
        if(nums[mid]==target) return mid;
        else if(nums[mid]>target) high = mid -1;
        else if(nums[mid]<target) low = mid +1;
    }
    return -1; //不用if判断了。只要走到这儿就一定是low>high了。
}

2.左闭右开

int search(int* nums, int numsSize, int target) {
    int low = 0,mid,high = numsSize;//左闭右开
    while(low<high)
    {
        mid = low + (high - low)/2; //防溢出
        if(nums[mid]==target) return mid;
        else if(nums[mid]>target) high = mid;
        else if(nums[mid]<target) low = mid +1;
    }
    return -1;
}

27.移除元素

一、做题感受&第一想法

有思路但难以用代码表示。弄巧成拙写了相向双指针,但是边界条件出错了几次,最后算是坎坎坷坷写出了。

二、学习文章后收获

1.思路一:暴力解法

int removeElement(int* nums, int numsSize, int val) {
    int i = 0, j = 0, temp = 0, k = 0;
    for( i = 0; i < numsSize - k; ){
        if(nums[i] == val){
            k++;
            for(j = i; j < numsSize - 1; j++){
                nums[j] = nums[j+1];
            }
        }
        else{
            i++;
        }
    }
    return numsSize-k;
}

2.思路二:双指针法之“快慢指针”:用快指针从前往后遍历,最后移动到慢指针处。

int removeElement(int* nums, int numsSize, int val) {
    int fast = 0, slow = 0;
    while(fast < numsSize){ //用fast指针来遍历
        if( nums[fast] != val){
            nums[slow] = nums[fast]; //如果找到不为val的元素,则搬动到slow的位置。
            slow++;
        }
        fast++;
    }
    return slow;
}

3.思路三:双指针法之“相向指针”:右指针从右往左寻找合法元素,移动给左指针

int removeElement(int* nums, int numsSize, int val) {
    int leftIndex = 0, rightIndex = numsSize - 1;
    while(leftIndex <= rightIndex){ //等号:考虑整个数组没有val的情况
        while( leftIndex <= rightIndex && nums[leftIndex] != val ){ //两个判断条件的顺序不能调换!否则数组越界
            leftIndex ++;
        }
        while( leftIndex <= rightIndex && nums[rightIndex] == val){
            rightIndex --;
        }
        if( leftIndex < rightIndex ){
            nums[leftIndex] = nums[rightIndex];
            leftIndex++;
            rightIndex--;
        }
    }
    return leftIndex; 
}

三、实现过程中的困难

1.&&运算符的执行顺序问题(“相向指针”中的一个bug)

上述的代码中,while循环的两个判断条件不能调换顺序!

错误写法:

        while( nums[leftIndex] != val && leftIndex <= rightIndex ){ //error,数组越界
            leftIndex ++;
        }
        while( nums[rightIndex] == val && leftIndex <= rightIndex ){ //error,数组越界
            rightIndex --;
        }

错误原因:
&&||运算符可能会导致某些判断并不会做。a && b如果条件a已经为假,则b不会再执行a || b如果条件a已经为真,则b不会再执行。
所以,如果先判断nums[leftIndex] != val再判断leftIndex <= rightIndex,则可能会导致在前者nums[leftIndex] != val时数组越界!(举个例子:数组中所有元素都不是val,那么最后leftIndex不断加加,直到leftIndex为numsSize。此时数组越界!)

【The END】


【另:今日份“相关题目”记录】

“704.二分查找”相关题目

35.搜索插入位置

题目
讲解文章

一、二分查找如果查找失败元素插入位置——找low

左闭右闭时,二分查找如果查找失败(low>high),则被查找元素大小在high和low之间!故应该取代low的位置。
左闭右开时,二分查找如果查找失败(low=high),则被查找元素大小就在high和low上!故应该取代low的位置。

二、暴力解法有时也很ok

int searchInsert(int* nums, int numsSize, int target) {
    int i=0;
    for(i=0;i<numsSize;i++)
    {
        if(nums[i]==target) return i;
        else if(nums[i]>target) return i;
    }
    return numsSize; //注意数组边界的处理!
}

1.在最后一句return上卡住了,因为一开始并没有考虑数组元素全部小于target的情况
这种情况下,i = numsSize,会跳出for循环,此时应插在所有数组元素的后面,所以需要添补return numsSize
2.暴力解法有时不一定比其他方法慢。

三、代码记录:

1.左闭右闭:

int searchInsert(int* nums, int numsSize, int target) {
    int low = 0,high = numsSize-1, mid;
    while(low<=high)
    {
        mid = low + (high-low)/2;
        if(nums[mid]==target) return mid;
        else if(nums[mid]>target) high = mid -1;
        else low = mid +1;
    }
    //没有找到target,此时low>high
    //target应该插在high和low中间!即:取代low的位置!
    return low;

2.左闭右开:

int searchInsert(int* nums, int numsSize, int target) {
    int low = 0,high = numsSize, mid;
    while(low<high)
    {
        mid = low + (high-low)/2;
        if(nums[mid]==target) return mid;
        else if(nums[mid]>target) high = mid;
        else low = mid +1;
    }
    //没有找到target,此时low=high
    //target应该放在high和low的位置!即:取代low的位置!
    return low;
}

34. 在排序数组中查找元素的第一个和最后一个位置

题目链接
讲解链接

一、二分法如何寻找左边界和右边界:两次二分

二、我一开始的思路:二分法找到一个target,再直接遍历左右元素,寻找左右边界(比较暴力的解法)

int* searchRange(int* nums, int numsSize, int target, int* returnSize) {
    int low=0 , high = numsSize-1 , mid, i=0;
    *returnSize = 2;
    int* p = (int*)malloc(sizeof(int)*2);
    while(low<=high)
    {
        mid = low +(high - low)/2;
        if(nums[mid] == target) break;
        else if(nums[mid] >target) high = mid -1;
        else low = mid +1;
    }
    if(low>high){
        p[0]=-1;
        p[1]=-1;
    }
    else //找到了target
    {
        for(i=mid;i>=0;i--){
            if(nums[i]!=target) break;
        }
        p[0] = i+1;
        for(i=mid;i<numsSize;i++){
            if(nums[i]!=target) break;
        }
        p[1] = i-1;
    }
    return p;
}
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值