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

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

二分查找

使用二分查找前提条件:

  1. 有序数组
  2. 没有重复数值

注意要点

二分查找易错点是条件的判断,条件判断分为两种

  1. while (left ? right) ,?是 <= 还是 < 呢
  2. if (nums[middle] > target)时,right 是 middle - 1 还是 middle 呢

其实这取决于我们在用二分法时,是想用左闭右闭,还是左闭右开的区间来进行二分

左闭右闭

当我们想用左闭右闭的区间时,那我们初始值的设定时应该让right = nums.length - 1,因为我们要比较所有元素,包括数组最右边的值

1. while (left ? right)

当我们选择左闭右闭这样的区间时,就意味着left 是可以等于 right 的。

可能有小伙伴会疑惑道相等不就是一个数吗,一个数也可以是区间?其实此区间非彼区间,我们这里的区间,其本质是数的集合,只需要让这个集合囊括到所有数的判断即可,而单个数也可以作为左闭右闭区间,是因为左闭右闭区间我们认为是两边都可以取到,中间部分可以有值也可以没有值,而单个数即中间没有值的情况,

2. if (nums[middle] > target)时,right 是 middle - 1 还是 middle 呢

因为我们是左闭右闭,所以当middle的值进行判断后,则不需要重复判断,所以right 是 middle - 1

左闭右开

当我们想用左闭右开的区间时,那我们初始值的设定时应该让right = nums.length,这样就能包括所有数

1. while (left ? right)

当我们选择左闭右开这样的区间时,就意味着left 是不可以等于 right 的,否则不符合我们对于区间的定义

2. if (nums[middle] > target)时,right 是 middle - 1 还是 middle

因为我们是左闭右开,所以当middle的值进行判断后,也不需要重复判断,但是我们不能让right等于middle - 1 因为我们是左闭右开,取到middle - 1,就意味着该区间最右边的数是middle - 2,那没有囊括middle - 1这个数的判断,则会出错

代码:

class Solution {
    public int search(int[] nums, int target) {
        // 左闭右闭
        if(target < nums[0] || target > nums[nums.length -1]){
            return -1;
        }
        int right = nums.length - 1;
        int left = 0;
        while(left <= right){
            int 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;
    }
}
class Solution {
    public int search(int[] nums, int target) {
        // 左闭右开
        if(target < nums[0] || target > nums[nums.length -1]){
            return -1;
        }
        int right = nums.length;
        int left = 0;
        while(left < right){
            int middle = left + (right - left) / 2;
            if(nums[middle] > target){
                right = middle;
            }
            else if(nums[middle] < target){
                left = middle + 1;
            }else{
                return middle;
            }
        }
        return -1;
    }
}

移除元素

暴力解法

不做过多解释,主要是需要一个参数新数组的长度,然后两层循环即可,时间复杂度O(n2)

class Solution {
    public int removeElement(int[] nums, int val) {
        int total = nums.length;
        int i = 0;
        while (i < total) {
            // 判断如果为nums就左移,左移的右边界为total
            if (nums[i] == val) {
                for (int j = i; j < total - 1; j++) {
                    nums[j] = nums[j + 1];
                }
                total--;
                continue;
            }
            i++;
        }
        return total;
}

双指针解法

快慢指针法
class Solution {
    public int removeElement(int[] nums, int val) {
        int fast = 0;
        int slow = 0;
        while (fast < nums.length){
            if (nums[fast] != val){
                nums[slow++] = nums[fast];
            }
            fast++;
        }
        return slow;
}

首先明确我们的目的:把数组中不等于val的值放在一起,返回长度

快指针:指向数组中不等于val的值,即指向我们需要但还没有放在一起的目标元素的下标

慢指针:指向我们下一个需要放新元素的下标(还没放),慢指针帮助我们创造了一个“新数组”

我们可以理解为快指针在搜索原数组中不等于val的目标值,找到后就想放到新数组里,即放到慢指针的位置上,这样可以发现慢指针的值始终等于新数组的长度

时间复杂度:O(n)

双向指针法
class Solution {
    public int removeElement(int[] nums, int val) {
        int right = nums.length - 1;
        int left = 0;
        while (left <= right){
            while (left <= right && nums[left] != val){
                left++;
            }
            while (left <= right && nums[right] == val){
                right--;
            }
            if (left < right){
                nums[left++] = nums[right--];
            }
        }
        return left;
}

其实和快慢指针类似

左指针:即“新数组”指针,目标是从左开始遍历,如果遇到数值等于val的时候,将右指针指向的值赋给该位置

右指针:从右开始,指向不等于val的值,指向的值是还未被“新数组收纳的值”,右指针指向右边的数组值是已经被“新数组”收纳,或者等于val不需要被收纳的值

终结条件:当左指针比右指针高一位,相当于把右指针指向的值进行收纳

时间复杂度:O(n)

相较于快慢指针的优势:移动元素位置更少

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值