算法学习| Day1数组 Leetcode 704.二分查找 27.移除元素

数组

1. 数组理论

  • 数组是连续的,相同数据类型集合
  • 数组下标从0开始
  • 内存地址连续——>删除/添加元素,要移动其他元素地址
  • 二维数组当然也是连续的

2. 二分查找理论

2.1 使用条件

思路:数组且有序,且无重复元素,使用二分查找

2.2 2种写法

  • 左闭右闭 [left,right]
  • 左闭右开 [left,right)

一般,不用左开右闭

注意:在循环中根据区间定义来做边界处理

2.2.1 左闭右闭
  • while (left <= right) 要使用 <=

  • if (nums[middle] > target) 中right 要赋值为 middle - 1;if (nums[middle]<target) 中left要赋值为 middle + 1

  • 初试值 left=0right=nums.length-1;

    注意要区别于左闭右开区间的初始值:left=0;right=nums .length

2.2.2 左闭右开
  • while (left < right),这里使用 <;因left==right在区间[left,right)内无意义

  • if (nums[middle] > target)中 right 更新为 middle;if (nums[middle]<target) 中 left 依旧是为 middle + 1

    因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,

    即:下一个查询区间不会去比较nums[middle]

  • 初试值 left=0right=nums.length

    举个例子😃

    一个长度为5的数字,最后一个数下标为4,length=5,若要数组下标在左闭右开区间[left,right)中,则right必须为5,才能使得区间在04中,此刻区间为[0,5)包含数组下标0~4

3. 704 二分查找 E

3.1 思路

数组且有序,且无重复元素,因此使用二分法

3.2 左闭右闭写法

class Solution {
    public int search(int[] nums, int target) {
        //左闭右闭区间
        //要想到这些,省去一些不必要的循环查找
        if (target<nums[0]||target>nums[nums.length-1]) {
            return -1;
        }
        int left=0;
        int right=nums.length-1;
        while (left <= right) {
            int middle=left+((right-left)>>1);//右位移运算,提高效率  >>1 相当于除以2
            if (nums[middle]==target) {
                return middle;
            } else if (nums[middle]>target) {
                right=right-1;
            }else if (nums[middle]<target){
                left=left+1;
            }
        }
        return -1;
    }
}

注意: 位运算>> 是除以2操作不会带小数 ,向下取整

3.3 左闭右开写法

class Solution {
    public int search(int[] nums, int target) {
        //左闭右闭区间
        //要想到这些,省去一些不必要的循环查找
        if (target < nums[0] || target > nums[nums.length - 1]) {
            return -1;
        }
        int left = 0;
        int right = nums.length;//左闭右开 right为数组长度
        while (left < right) {
            int middle = left + ((right - left) >> 1);//右位移运算,提高效率
            if (nums[middle] == target) {
                return middle;
            } else if (nums[middle] > target) {
                right = middle;//左闭右开区间,right值为middle;跟左闭右闭有区别
            } else if (nums[middle] < target) {
                left = left + 1;
            }
        }
        return -1;
    }
}

4. 27 移出元素 E

4.1 思路

数组的元素在内存中是连续存放的,不能单独删除数组中的某个元素,只能覆盖

4.2 暴力解法

思路:两层for循环:一层循环遍历数组,一层循环更新数组

class Solution {
    public int removeElement(int[] nums, int val) {
        int length=nums.length;
        //循环遍历数组
        for (int i = 0; i < length; i++) {
            if (nums[i]==val) {//发现要移出元素,将后面的数组集体向前移动一位进行覆盖
                for (int j = i+1; j < length; j++) {//j=i+1 要移出元素的后面所有数组开始
                    nums[j-1]=nums[j];//覆盖
                }
                i--;//一定要i--!!!,因为下标i后的数值都向前移了一位了,所以i必须也向前移动一位
                length--;//覆盖后数组长度少1
            }
        }
        return length;
    }
}

注意:

一定要 进行 i-- 操作

举例:数组{0,1,2,2,3,0,4,2},要移出2。

当i为2时,找到了符合要移出的数值,将后面的数往前移动之后是{0,1,2,3,0,4,2},如不对i进行–操作,则此处i还是为2,当进入下一个for循环当中要先对i++使得i为3,就少了下标为2的数组元素2没有移出

4.3 双指针法

思路:通过一个快指针和慢指针在一个for循环下完成两个for循环的工作

  • 快指针:寻找 新数组(不含目标元素) 中的元素
  • 慢指针:指向更新 新数组 下标的位置
class Solution {
    public int removeElement(int[] nums, int val) {
       int slow=0;
       //fast快指针遍历数组
        for (int fast = 0; fast < nums.length; fast++) {
            if (nums[fast]!=val) {
                nums[slow]=nums[fast];//指向更新新数组 小标
                slow++;//此后加一
            }
        }
        return slow;
    }
}
  • 时间复杂度:O(N)
  • 空间复杂度:O(1)

4.4 相向双指针法

这个方法是从代码随想录看到的,第一次看到有点新颖。

相向,顾名思义就是从两头开始向中间靠拢,一头一尾定义2个左右指针,因为此题只是要求移出元素后返回新数组的长度,并没有对其中数组内部元素的序列有要求,所以正是因为这一点,本题采用此方法,让右指针先移动到从右数第一个值不为val的位置,之后开始while循环,移动左指针,当左指针指向val时,让右指针指向的数组元素覆盖其val,同时右指针如果等于val继续向左移动。

总结下来 左右指针移出操作各不同:

  • left:指向val时,用right指向的元素覆盖val
  • right:指向val时,做 right-- 操作,忽略值为val的元素

这样讲的话应该就通透了吧 !

class Solution {
    public int removeElement(int[] nums, int val) {
      int left=0;
      int right=nums.length-1;
      //先对右指针进行操作,移动到右边第一个不为val的位置
        //right移出操作
        while (right >= 0 && nums[right] == val) {
            right--;
        }
        while (left <= right) {//left移出操作
            //左指针指向元素若为val
            if (nums[left]==val) {
                //right位置元素覆盖left
                nums[left]=nums[right];
                //right位置移出
                right--;
            }
            //同时左指针继续++
            left++;
            //右指针指针指向元素若为val,则--
            while (right >= 0 && nums[right] == val) right--;
        }
        return left;
    }
}

注意:

  • left位置移出操作:将right位置元素覆盖给left后,要记得 right–,将right位置移出,向左移动

📓以上为 5.24 所学,加油!踏踏实实走好每一步,你走过的路每一步都算数~!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值