首页 > 编程语言 >代码随想录算法训练营第一天LeetCode704,35,34,27

代码随想录算法训练营第一天LeetCode704,35,34,27

时间:2022-12-28 23:22:49浏览次数:63  
标签:27 target nums int LeetCode704 随想录 mid right left

代码随想录算法训练营第一天|LeetCode704,35,34,27

LeetCode 704 二分查找

//第一次做还不知道二分中的左闭右开和左闭右闭指什么,于是写了很多边界条件辅助判断
class Solution {
public:
    int search(vector<int> &nums, int target) {
        int left = 0, right = nums.size() - 1;

        //判断边界
        if(target < nums[left] || target > nums[right]) return -1;
        if(target == nums[left]) return 0;
        if(target == nums[right]) return right;

        //采用while循环找元素
        while(left < right){
            int mid = (left + right)/2;
            //和mid下标所在元素做比较
            if(nums[mid] < target) left = mid + 1;
		    else if(nums[mid] > target) right = mid;
            else return mid;
        }
        return -1;
    }
};
视频讲解链接:https://www.bilibili.com/video/BV1fA4y1o715
文章讲解链接:https://programmercarl.com/0704.%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE.html

根据视频和文章讲解做出如下总结:

1.易错点:就像第一次做的时候,很难把握while循环中left是<还是<=right;target和mid下标对应元素做比较时,比如mid下标对应元素大于target时,right是等于mid还是等于mid-1?

2.首先明确区间的定义,主流写法是左闭右闭([[left, right]])和左闭右开([left, right))

3.第一种写法左闭右闭:根据区间定义,可得while循环中的判定条件是left <= right(起始right为num.size()-1);假设nums[mid] > target,要去更新左区间的右边界,那这时候right值改为mid还是mid-1?答案是mid-1,因为采取的区间是左闭右闭,即我可以取到最右下标对应的元素值,如果选择mid,意味着算法可以取到mid下标对应的元素值,但已有nums[mid] > target,不能将不是自己搜索区间的值放在搜索区间里,所以选择mid不成立,应选择mid-1;假设nums[mid] < target,亦然,修正右区间的左边界,将left改为mid+1。

// 左闭右闭版本一
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效,所以用 <=
            int mid = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[mid] > target) {
                right = mid - 1; // target 在左区间,所以[left, mid - 1]
            } else if (nums[mid] < target) {
                left = mid + 1; // target 在右区间,所以[mid + 1, right]
            } else { // nums[mid] == target
                return mid; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

4.第二种写法左闭右开:根据区间定义,可得while循环中的判定条件是left < right(起始right为num.size());假设nums[mid] > target,要去更新左区间的右边界,那这时候right值改为mid还是mid-1?答案是mid,因为采取的区间是左闭右开,即我无法取到最右下标对应的值,如果选择mid-1,意味着算法无法取到mid-1下标对应的元素值,但已有nums[mid] > target,可以将mid作为不能被取到的值放在搜索区间里,所以选择mid;假设nums[mid] < target,亦然,修正右区间的左边界,将left改为mid+1。

// 左闭右闭版本二
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,即:[left, right)
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间,所以使用 <
            int mid = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = mid; // target 在左区间,在[left, mid)中
            } else if (nums[mid] < target) {
                left = mid + 1; // target 在右区间,在[mid + 1, right)中
            } else { // nums[mid] == target
                return mid; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

LeetCode 35 搜索插入位置

题目链接:https://leetcode.cn/problems/search-insert-position/
//又是纯胡写,if else了一堆边界条件过得,因为还没有培养左闭右闭/左闭右开的意识
//O(logn)的时间复杂度
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0, right = nums.size() - 1;
        //判断边界:
        if(target == nums[left] || target < nums[left]) return 0;
        if(target == nums[right]) return right;
        if(target > nums[right]) return right + 1;
        int mid;
        while(left < right){
            mid = (left+right+1)/2;
            if(nums[mid] == target) break;
            else if(right == left+ 1 && target > nums[right]){mid++; break;}
            else if(right == left+1 && nums[right]-nums[left] > target - nums[left]) break;
            else if(nums[mid] < target) left = mid;
            else if (nums[mid] > target) right = mid-1;
        }
        return mid;
    }
};
文章讲解链接:https://programmercarl.com/0035.搜索插入位置.html#思路

根据文章讲解做出如下总结:

1.明确目标值target的四种情况:目标值在所有元素之前;目标值等于数组的某个元素;目标值插入数组中的位置;目标值在数组所有元素之后。

2.看到有序数组都可以联想一下能否使用二分查找,因为有序数组是二分查找的基础前提

//左闭右闭版本一
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1; // 定义target在左闭右闭的区间里,[left, right]
        while (left <= right) { // 当left==right,区间[left, right]依然有效
            int mid = left + ((right - left) / 2);// 防止溢出 等同于(left + right)/2
            if (nums[mid] > target) {
                right = mid - 1; // target 在左区间,所以[left, mid - 1]
            } else if (nums[mid] < target) {
                left = mid + 1; // target 在右区间,所以[mid + 1, right]
            } else { // nums[mid] == target
                return mid;
            }
        }
        // 分别处理如下四种情况
        // 目标值在数组所有元素之前  [0, -1]
        // 目标值等于数组中某一个元素  return mid;
        // 目标值插入数组中的位置 [left, right],return  right + 1
        // 目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1
        return right + 1; //return left; //因为跳出区间后left > right
    }
};
//左闭右开版本二
class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size(); // 定义target在左闭右开的区间里,[left, right)  target
        while (left < right) { // 因为left == right的时候,在[left, right)是无效的空间
            int mid = left + ((right - left) >> 1);
            if (nums[mid] > target) {
                right = mid; // target 在左区间,在[left, mid)中
            } else if (nums[mid] < target) {
                left = mid + 1; // target 在右区间,在 [mid + 1, right)中
            } else { // nums[mid] == target
                return mid; // 数组中找到目标值的情况,直接返回下标
            }
        }
        // 分别处理如下四种情况
        // 目标值在数组所有元素之前 [0,0)
        // 目标值等于数组中某一个元素 return mid
        // 目标值插入数组中的位置 [left, right) ,return right 即可
        // 目标值在数组所有元素之后的情况 [left, right),因为是右开区间,所以 return right
        return right; //return left; //因为跳出区间后,left >= right
    }
};

LeetCode 34 在排序数组中查找元素的第一个和最后一个位置

题目链接:https://leetcode.cn/problems/find-first-and-last-position-of-element-in-sorted-array/
//这个领会到了一点,是按照左闭右开写的,不过私以为这种题目更适合双指针
class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        if(nums.empty()) return{-1, -1};
        int start = range(nums, target);
        if(start == nums.size() || nums[start] != target) return{-1,-1};
        int end = range(nums, target+1) -1;
        return{start, end};
	
    }
    int range(vector<int>& nums, int target){
        int left = 0, right = nums.size();
	    	while(left < right){
            int mid = left + (right - left) / 2;
                if (nums[mid] < target)
                    left = mid + 1; // 范围缩小到 [mid+1, right)
                else
                    right = mid; // 范围缩小到 [left, mid)
        }
        return left; //返回第一次遇到元素的位置
    }
};
文章讲解链接:https://programmercarl.com/0034.在排序数组中查找元素的第一个和最后一个位置.html#思路

LeetCode 27 移除元素

题目链接:https://leetcode.cn/problems/remove-element/
//stl容器的暴力遍历
//erase这个库函数的时间复杂度是O(n)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        auto iter = nums.begin();
        for(; iter != nums.end(); ){
            if(*iter == val){
                nums.erase(iter);
            }
            else{
                ++iter;
            }
        }
        return nums.size();

    }
};
视频讲解链接:https://www.bilibili.com/video/BV12A4y1Z7LP
文章讲解链接:https://programmercarl.com/0027.%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.html

根据视频讲解和文章讲解做出如下总结:

1.可以设立快慢指针,让快指针指向的元素覆盖掉慢指针指向的内容,这应该是双指针法的一种。

2.具体为快指针指向新数组所需要的元素,慢指针指向新数组的下标,把快指针指向的值赋给慢指针所在的位置

//快慢指针版本
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
    size_t slowptr = 0;
		for(size_t fastptr = 0; fastptr < nums.size(); fastptr ++){
      //快指针获取元素,慢指针获取更新的位置
			if(nums[fastptr] != val){
			nums[slowptr++] = nums[fastptr];
			}
		}
		return slowptr;

    }
};

3.可以设立相向双指针,一个指针从头开始,一个指针从尾开始。

//相向双指针版本
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        //相向双指针,可以确保移动了最少的元素数量
        int left = 0, right = nums.size() - 1;
        //左闭右闭
        while(left <= right){
            //找左边等于val的元素
            while(left <= right && nums[left] != val) ++left;
            //找右边不等于val的元素
            while(left <= right && nums[right] == val) --right;
            //将右边不等于val的元素覆盖掉左边等于val的元素
            if(left < right) nums[left++] = nums[right--];
        }
        //left一定指向了最终数组末尾的下一个元素
        return left; 
    }
};

标签:27,target,nums,int,LeetCode704,随想录,mid,right,left
From: https://www.cnblogs.com/shadowbringer/p/17011429.html

相关文章