代码随想录算法训练营第一天| 704. 二分查找、35.搜索插入位置、34.在排序数组中查找第一个和最后一个元素的位置、27. 移除元素

文章讲解:代码随想录 (programmercarl.com)

视频讲解:手把手带你撕出正确的二分法 | 二分查找法 | 二分搜索法 | LeetCode:704. 二分查找_哔哩哔哩_bilibili

状态:

Leetcode704二分查找  题目链接704. 二分查找 - 力扣(LeetCode)

二分查找的条件:数组有序且没有重复元素
时间复杂度O(logn)
空间复杂度O(1)

两种情况:
1.左闭右闭[left, right]:

  • 在这种情况下就应当明白,left = right是一个合法条件,所以while(left <= right)
  • left和right都是可以被取得的数,所以当nums[middle]大于或小于target时,right或left就不能等于middle,而是要等于middle - 1或middle + 1,从而确保下一次进行查找时不会包含nums[middle]这种已经查阅过的值
    class Solution {
        public int search(int[] nums, int target) {
            int left = 0;
            int right = nums.length - 1;
            while(left <= right){
                int middle = (left + right) / 2;
                if(nums[middle] > target)right = middle - 1;
                else if(nums[middle] < target)left = middle + 1;
                else return middle;
            }
            return -1;
        }
    }

2.左闭右开[left, right):

  • left = right不是合法条件,所以while(left < right)
  • right不是可以被取得的数,所以right初始值为nums.length,right = middle
    class Solution {
        public int search(int[] nums, int target) {
            int left = 0;
            int right = nums.length;
            while(left < right){
                int middle = (left + right) / 2;
                if(nums[middle] > target)right = middle;
                else if(nums[middle] < target)left = middle + 1;
                else return middle;
            }
            return -1;
        }
    }

Leetcode35. 搜索插入位置 - 力扣(LeetCode)
在二分查找法结束后,left = right + 1,nums[right] < target,nums[left] > target,因此left所在的位置一定是元素要插入的位置。
明白这点,题目就能解了
时间复杂度O(logn)
空间复杂度O(1)

class Solution {
    public int searchInsert(int[] nums, int target) {
        int left = 0;
        int right = nums.length - 1;
        while(left <= right){
            int middle = (left + right) / 2;
            if(nums[middle] > target)right = middle - 1;
            else if(nums[middle] < target)left = middle + 1;
            else return middle;
        }
        return left;
    }
}

34. 在排序数组中查找元素的第一个和最后一个位置 - 力扣(LeetCode)

在这道题目中有三种情况需要考虑

  1. target不在nums数组中,且要在左右边界之外
  2. target在nums数组中
  3. target不在nums数组中,但大小是在nums数组的范围之内


之后要明白二分查找法如何确定左边界和右边界:当确认左边界时,我们应当采用以下代码

public int getLeftBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int leftRecord = -2;
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] < target)left = mid + 1;
            else{
                right = mid - 1;
                leftRecord = right;
            }
        }
        return leftRecord;
    }

在上述代码中,可以看到如果nums >=  target的话right = mid - 1,之后我们对leftReocrd的值进行分情况讨论。
如果是第一种情况:target要在nums数组的右边界之外,则leftRecord仍等于-2;如果在左边界之外,则leftRecord等于-1
如果是第二种情况:则leftReocrd最终的值为target对应的左边界-1
如果是第三种情况:则最终得到的左边界和右边界之间的差值一定 <= 1

右边界同理,下附题目的完整源码
 

class Solution {
    public int[] searchRange(int[] nums, int target) {
        int[] res = new int[2];
        int rightBorder = getRightBorder(nums, target);
        int leftBorder = getLeftBorder(nums, target);
        if(rightBorder == -2 || leftBorder == -2){ // 情况1
            res[0] = -1;
            res[1] = -1;
            return res;
        }
        if(rightBorder - leftBorder > 1){          // 情况2
            res[0] = leftBorder + 1;
            res[1] = rightBorder - 1;
            return res;
        }
        res[0] = -1;                               // 情况3
        res[1] = -1;
        return res;
    }
    public int getRightBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int rightRecord = -2;
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] > target)right = mid - 1;
            else{
                left = mid + 1;
                rightRecord = left;
            }
        }
        return rightRecord;
    }
    public int getLeftBorder(int[] nums, int target){
        int left = 0;
        int right = nums.length - 1;
        int leftRecord = -2;
        while(left <= right){
            int mid = (left + right) / 2;
            if(nums[mid] < target)left = mid + 1;
            else{
                right = mid - 1;
                leftRecord = right;
            }
        }
        return leftRecord;
    }
}

27. 移除元素 - 力扣(LeetCode)
暴力解法
时间复杂度O(n^2)
空间复杂度O(1)

class Solution {
    public int removeElement(int[] nums, int val) {
        int length = nums.length;
        for(int i = 0; i < nums.length; i++){
            if(i == length)break;
            if(nums[i] == val){
                for(int j = i; j < length - 1; j++){
                    nums[j] = nums[j + 1];
                }
                nums[length - 1] = 0;
                length--;
                i--;
            }
        }
        return length;
    }
}

双指针解法
时间复杂度O(n)
空间复杂度O(1)
快指针找到要加进新数组的元素,慢指针找到要加元素的位置。

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

相向双指针解法
时间复杂度O(n)
空间复杂度O(1)
left指针从左边寻找与target相等的值的地址,右指针从右边找与target不相等的值的地址。
代码如下
 

class Solution {
    public int removeElement(int[] nums, int val) {
        int left = 0;
        int right = nums.length - 1;
        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;
    }
}

为什么left <= right?
如果left < right,则当left = right时,若nums[left] != target,则数组遗漏了一个值

心得:704、35、移除元素暴力解法都可以自己做出来,其他的需要讲解下思路
二分查找本身有过一定基础,但是左闭右闭和左闭右开还是第一次接触,更能理解这个范围的概念了。
34、35更像是二分查找的变种,从而理解二分查找结束后left、right的含义。
现在还是不太熟练,自己想出的代码需要一些数据来测试完善。

你可能感兴趣的:(算法,java)