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

LeetCode 704. 二分查找

思路:基本的二分查找方法。
关键点:在while循环中的L<R(L<=R)中间的符号,取决于区间的左闭右开、左闭右闭。(当然题目变化的话也可能和具体题目相关。)
所谓的:
**左闭右闭:**就是R=size-1;R指向数组最后一个元素;
**左闭右开:**就是R=size; R指向数组最后一个元素的下一个。
之后的循环中保持这个规律即可。

左闭右闭

class Solution {
    public int search(int[] nums, int target) {
    	// 左闭右闭
        int size = nums.length;
        int L = 0;
        int R = size - 1;
        while(R>=L){ // 因为最右边的一个元素是有效的所以存在L=R的情况,因此要保留。
            int mid = L + ((R-L)>>1);
            if(nums[mid]>target){
                R = mid - 1;
            }
            else if(nums[mid] < target){
                L = mid + 1;
            }
            else{
                return mid;
            }
        }
        return -1;
    }
}

左闭右闭

class Solution {
    public int search(int[] nums, int target) {
        // 左闭右开
        // whlie(L<R)  此时L==R 没有含义 因为左闭右开 的情况不会出现相等
        int size = nums.length;
        int L = 0;
        int R = size;
        while(L<R){
            int mid = L + ((R - L)>>1);
            if(nums[mid] > target){
                R = mid;
            }
            else if(nums[mid] < target){
                L = mid + 1;
            }
            else{
                return mid;
            }
        }
        return -1;
    }
}

LeetCode:35:搜索插入位置

思路:题目要求的复杂度是logn。想到二分查找
关键点:二分查找中选择使用左闭右开的方法,这样可以避免另外加一个变量来记录target不在nums的情况。

// java
class Solution {
    public int searchInsert(int[] nums, int target) {
        // 左闭右开
        int size = nums.length;
        int L = 0;
        int R = size;
        while(L<R){
            int mid = L + ((R-L)>>1);
            if(nums[mid] > target){
                R = mid;
            }
            else if(nums[mid] < target){
                L = mid + 1;
            }
            else{
                return mid;
            }
        }
        return R;
    }
}
// CPP
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int L  = 0;
        int R = nums.size();
        while(R > L){
            int mid = L + ((R - L) >> 1);
            if(nums[mid] > target){
                R = mid;
            }
            else if(nums[mid] < target){
                L = mid + 1;
            }else{
                return mid;
            }
        }
        return R;
    }
};

LeetCode:34:在排序数组中查找元素的第一个和最后一个位置(需要二刷)。

思路: 看到题目要求O(logn)。第一反应就是二分查找。但是这一题关键是一步二分查找不行,得需要两次,一次找到左边界,另一次找到右边界。由于数组是递增的,因此分为两种情况:
左边界:就是找到第一个大于等于target的位置;
右边界:找到第一个大于等于target+1的位置。
细节很繁琐。
关键点:target >= num[mid]。这里就是找到
不同于传统的二分查找,最后的返回出口是target == num[mid]

解法一:完整的展示所有情况的代码(代码随想录)

分别通过if target <= nums[mid]找到左边界;if target >= nums[mid] 找到右边界;分情况讨论。

解法二:如上所述

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int left = binarySearch(nums, target);
        int right = binarySearch(nums, target+1)-1;
        if(right>=left && nums[left] == target && right <= nums.size()){
            return {left, right};
        }
        return {-1, -1};
    }

    int binarySearch(vector<int>& nums, int target){
        int L = 0;
        int R = nums.size() - 1;
        while(L <= R){
            int mid = L + ((R - L) >> 1);
            if(target <= nums[mid]){ // 关键
                R = mid - 1;
            }
            else{
                L = mid + 1;
            }
        }
        return L;
    }
};
class Solution {
    public int[] searchRange(int[] nums, int target) {
        // 基本思想能够想到,但是具体的细节有很多要考虑
        // 这一题,再次认识里面的符号的作用>=

        int left = binarySearch(nums, target); // 寻找左边第一个大于>=target的数,全是大于则没有,有了等于就是第一个。
        int right = binarySearch(nums, target+1)-1; // 寻找右边第一个大于target的数
        if(left<=right && right<nums.length && nums[left]==target && nums[right]==target){
            return new int[] {left, right};
        }
        return new int[] {-1, -1};
    }

    public int binarySearch(int[] nums, int target){
        int L = 0;
        int R = nums.length - 1;
        while(L<=R){
            int mid = L + ((R-L)>>1);
            if(nums[mid] < target){
                L = mid+1;
            }
            else if(nums[mid] >= target){  // 注意这里的变化,要的是>= 这样会接着执行指导左边第一个target,或者是左边第一个大于target的数。
                R = mid-1;
            } 
        }
        return L;
    }
}

LeetCode:27.移除元素

思路:被题目迷惑了。’这道题目不单单返回最后的长度,还要将里面对应的val删除。

  • 暴力。两层for,遍历找到val,然后将后面的元素依次向前移动一位。
  • 双指针:fast找到val,slow记录新数组的最后一位。

注意:还要删除里面的对应值为val的元素。

解法一:暴力

class Solution {
    public int removeElement(int[] nums, int val) {
        // 半天没读懂题目,明确说返回新数组的长度就好了。
        // 先来一个暴力解法:
        // 两层for循环,找到对应的val之后就将后面的元素整体往前移一位,
        // 同时,i--;size-- 因为整体都少了一个
        // 最后返回size。
        int size = nums.length;
        for(int i=0;i<size;i++){
            if(nums[i] == val){
                for(int j = i+1;j<size;j++){
                    nums[j-1] =nums[j]; 
                }
                i--;
                size--;
            }
        }
        return size;
    }
}

解法二:双指针(看了代码随想录)

class Solution {
    public int removeElement(int[] nums, int val) {
        // 单单返回最后的长度还不行,还要将里面的元素删除。
        // 双指针解法;快慢指针
        // 画图就好理解了。
        int size= nums.length;
        int slow = 0;
        for(int fast=0;fast<size;fast++){
            if(nums[fast] != val){
                nums[slow] = nums[fast]; 
                slow++;
            }
        }
        return slow;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值