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

前言


前言

1.二分查找

二分查找又叫折半查找,即每次查找后都将搜索区间减半。

对于在有序数组nums中查找一个数target时,有可能遇到三种可能:①target == nums[mid];②target位于mid以左的区间;③target位于mid以右的区间。

当遇到第一种情况,那么这个target已经被找到了;当nums[mid]不等于target时,我们可以通过比较大小,判断是遇到第二种还是第三种情况,从而排除掉另一个区间,这样就缩小了搜索范围。

二分查找的适用范围:有序且无重复元素。

2.数组

数组是在一片连续的内存空间中存储数据类型相同线性结构。当存储的是基本数据类型时,数组中存储的数据就是对应的值;当存储的是引用数据类型时(如Java中的对象),数组中存储的是引用数据所对应的地址值。

数组的查询速度快。数组可以根据索引查询到想要的数据,时间复杂度为O(1)。

数组插入和删除操作比较低效。前面提到,数组为线性结构,也就是说像一条线一样,只有前后两个方向。当要要在k索引处插入一个数据时,首先需要保证数组不会溢出,再将k及其以后的所有元素后移一个位置,最后再对k处的数组元素赋值。当要删除k索引处的数据时,则是将k+1及其之后的所用元素往前移动一个位置。可以看出我们并不是删除了这个元素,只是将其覆盖了。插入和删除的平均时间复杂度都为O(n)。

一、704.二分查找

题目链接:704.二分查找

题目简述:在一个升序整型数组中找到和目标值target一致的元素,返回其下标,无则返回-1。

解题思路:

1.暴力算法

遍历数组,找到和target一致的元素,返回其下标。

时间复杂度:O(n),空间复杂度:O(1)

class Solution{
    public int search(int[] nums, int target) {
        int len = nums.length;
        for(int i = 0; i < len; i++){
            if(nums[i] == target){
                return i;
            }
        }
        return -1;
    }
}

2.二分查找**

题目中提到数组为有序数组且无重复元素,这正是二分法查找的适用条件。

时间复杂度:O(log n),空间复杂度:O(1)

写二分查找时要注意边界问题,可以根据循环不变量来确定在判断时是<还是<=,在更换区间时是mid = right 还是 mid = right + 1。

第一种写法:闭区间[left,righht],也就是说搜索范围包括left或者right。

  • while条件:因为是闭区间,left和right相等时,区间是成立的,所以判断条件应该为left<=right
  • 更新区间:因为搜索范围包括left和right,所以当要更换区间时,mid对应的元素不应该出现在下一个搜索区间里,因此在更新区间时,right = mid - 1,left = mid + 1。
class Solution {
    public int search(int[] nums, int target) {    
        int left = 0;
        int right = nums.length-1;
    
        while(left <= right){
            int mid = left + (right - left)/2;
            if(nums[mid] > target){
                right = mid - 1;
            }else if(nums[mid]<target){
                left = mid + 1;
            }else
                return mid;
        }
        return -1;
    }
}

第二种写法:左闭右开区间[left,righht),也就是说搜索范围包括left但不包括right。

  • while条件:因为是左闭右开区间,left和right相等时,区间不成立,所以判断条件应该为left < right
  • 更新区间:因为搜索范围包括left但不包括right,所以当要更换区间时,mid对应的元素不能出现在下一个搜索区间里左端点,但可以是下个搜索区间的右端点,因此在更新区间时,right = mid,left = mid + 1。
class Solution {
    public int search(int[] nums, int target) {
        int left = 0;
        int right = nums.length-1;
    
        while(left < right){
            int mid = left + (right - left)/2;
            if(nums[mid] > target){
                right = mid;
            }else if(nums[mid] < target){
                left = mid + 1;
            }else
                return mid;
        }
        return -1;
    }
}

二、27.移除元素

题目链接:27.移除元素

题目简述:

  • 在一个数组nums中移除与val值相等的元素,并返回移除后的数组的长度。
  • 原地删除,即不能创建新数组。

思路:

1.暴力算法

遍历数组,当发现有与val值相等的元素时,把该元素之后的所有元素的下标往前移一位进行覆盖,达到删除的效果。需要使用两层for循环。

时间复杂度:O(n^2),空间复杂度:O(1)

2.双指针***

  • fast指针:用于遍历数组
  • slow指针:初始化为数组0索引位置,之后指向”新数组“的末位+1,即下个元素填充的位置(这里的“新数组”为移除元素后的数组,但实际上与“原数组”为同一个内存空间)
  • 移动过程:当fast指针指向的元素不等于val时,更新nums[slow],并将slow后移;当fast指针指向的元素等于val时,只移动fast,不移动slow,也不更新nums[slow]的值。这样可以使元素值等于val的元素被其后面的元素覆盖,这样就达到了删除的目的。当程序结束时,“新数组”的长度为slow的值。

 时间复杂度:O(n),空间复杂度:O(1)

class Solution {
    public int removeElement(int[] nums, int val) {
        int slow = 0;
        int len = nums.length; 
        for(int fast = 0; fast<len; fast++){
            if(nums[fast] != val){
                nums[slow++] = nums[fast];
            }
        }
        return slow;

    }
}

总结

  • 二分查找注意边界问题,通过最开始定义的搜索区间来确定判断条件和更新区间。
  • 数组中删除元素实际为覆盖元素,使用双指针解题时要明确两个指针指向的意义和移动过程。
  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值