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

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

目录

  • 代码随想录算法训练营第一天| 704. 二分查找、27. 移除元素
    • 二分查找
      • 注意要点
        • 左闭右闭
          • 1. while (left ? right)
          • 2. if (nums[middle] > target)时,right 是 middle - 1 还是 middle 呢
        • 左闭右开
          • 1. while (left ? right)
          • 2. if (nums[middle] > target)时,right 是 middle - 1 还是 middle
    • [移除元素](https://leetcode.cn/problems/remove-element/ )
      • 暴力解法
      • 双指针解法
        • 快慢指针法
        • 双向指针法

二分查找

使用二分查找前提条件:

  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)

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

你可能感兴趣的:(代码随想录_刷题,算法,leetcode,数据结构)