算法训练Day1—数组I(704二分查找、27移除元素)

98 阅读2分钟

1. 二分查找

LeetCode 704

题目:给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

前提:
有序数组,且数组中无重复元素
易错点:
1.注意左右区间的开闭
2.更新left或者right时与middle的具体关系+1或-1

左闭右闭

注意点:
1.由于left和right都可以取得边界值,数组从0开始,因此将left赋值为0,而right赋值为n-1;
2.假设left和right都取1,左闭右闭成立,因此while循环中left<=right;
3.当if条件middle的值大于target,此时target肯定也不会取该middle(right取右闭),故right只能取到middle-1;当if条件middle的值小于target,此时target肯定也不会取该middle(left取左闭),故left只能取到middle+1。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 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;
    }
};

左闭右开

注意点:
1.由于left可以取边界,right不能取边界,数组从0开始,因此将left赋值为0,而right赋值为n;
2.假设left和right都取1,左闭右开显然不成立,因此while循环中left不能等于right,只能有left<right;
3.当if条件middle的值大于target,此时target肯定也不会取该middle(right取右开),right取到middle;当if条件middle的值小于target,此时target肯定也不会取该middle(left取左闭),故left只能取到middle+1。

class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size();
        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;
    }
};

tips:
防止溢出的写法:(两个int型变量相加可能会导致数组越界,但该题不会)

int middle = (left + right)/2;

改为

int middle = left + ((right - left)/2);

2. 移除元素

LeetCode 27

题目:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并原地修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1: 给定 nums = [3,2,2,3], val = 3, 函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
示例 2: 给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。

前提:
不要使用库函数,因为该题用库函数一步就解决了,而且erase是一个时间复杂度为O(n)的操作。

暴力法

解法:
两个for循环,当找到与目标值val相同的元素时,就将该元素之后的元素全都向前移动一位。

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int size = nums.size();
        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;
    
};

双指针法

解法:
1.用一个for循环完成了暴力法两个for循环实现的功能,采用快慢指针进行移动,快指针用来找目标值val;
2.当快指针没找到val时,将此时快指针的值赋给慢指针,然后快慢指针均向后移动;当快指针找到目标值val时,只有快指针向后移动;
3.最后循环完慢指针所指向的数组下标就是新数组的下标。

class Solution{
public:
    int removeElement(vector<int>& nums, int val){
        int slow = 0;
        for(int fast = 0;fast < nums.size(); fast++){
            if(val != nums[fast]){
                nums[slow] = nums[fast];
                slow++;
            }
        }
        return slow;
    }
};