代码随想录Day1 704. 二分查找,27. 移除元素

442 阅读2分钟

Day1 704. 二分查找,27. 移除元素

今日学习时长:3h

数组理论知识

vector是容器,不是数组

cpp在二维数组的地址空间上是连续的

java不对程序员暴露元素的地址,寻址操作完全交给虚拟机,所以看不到每个元素的地址情况

704 二分查找

vector的片段截取

blog.csdn.net/qq_40250056…

初始化创建

vector<int>::const_iterator beg = nums.begin();
    vector<int>::const_iterator mid = nums.begin() + nums.size()/2;
    vector<int>::const_iterator end = nums.end();
    std::vector<int> left(beg, mid);
    std::vector<int> right(mid, end);

void assign(const_iterator first,const_iterator last); //两个指针,分别指向开始和结束的地方

思路

递归(最初的构思)

先和middle比较,然后再递归查找

class Solution {
 public:
  int search(vector<int>& nums, int target) {
    if(nums.size() <= 0) return -1;
    if(nums.size() == 1){
      return nums[0] == target? 0 : -1;
    }
    for(auto& item: nums){
      cout << item << ' ';
    }
    cout << endl;
    vector<int>::const_iterator beg = nums.begin();
    vector<int>::const_iterator mid = nums.begin() + nums.size()/2;
    vector<int>::const_iterator end = nums.end();
    std::vector<int> left(beg, mid);
    std::vector<int> right(mid, end);
    if(*(mid) == target){
      return nums.size()/2;
    }else if(*(mid) > target){
      return search(left, target);
    }else{
      int rightRet = search(right, target);
      if(rightRet == -1){
        return -1;
      }else{
        return nums.size()/2 + rightRet;
      }
    }
  }
};

不要用递归,看清题目是否有重复元素,是否是已经排过序的数组

左闭右闭的区间

  • while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 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 - left) / 2;//防止溢出
        if(target == nums[middle]){
            return middle;
        }else if(target < nums[middle]){
            right = middle - 1;
        }else{
            left = middle + 1;
        }
    }
    return -1;
  }
};

左闭右开的区间

  • while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
  • if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]

注意边界的处理与考虑

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 middle = left + ((right - left) >> 1);
            if (nums[middle] > target) {
                right = middle; // target 在左区间,在[left, middle)中
            } else if (nums[middle] < target) {
                left = middle + 1; // target 在右区间,在[middle + 1, right)中
            } else { // nums[middle] == target
                return middle; // 数组中找到目标值,直接返回下标
            }
        }
        // 未找到目标值
        return -1;
    }
};

27 移出元素

初始解法

class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int count = 0;
        int len = nums.size();
        for(int i = 0; i < len - count; i++){
            if(nums[i] == val){
                while(nums[len - 1 - count] == val){
                    count ++;
                    if(len - 1 - count < 0) return 0; //[1], val = 5
                }
                if(i >= len - count) break;//[4, 5], val = 5
                int tmp = nums[len - 1 - count];
                nums[i] = tmp;
                nums[len - 1 - count ] = val;
                count ++;
            }
        }
        return nums.size() - count ;
    }
};

数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖。

双指针解法

// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
    int removeElement(vector<int>& nums, int val) {
        int slowIndex = 0;
        for (int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
            if (val != nums[fastIndex]) {
                nums[slowIndex++] = nums[fastIndex];
            }
        }
        return slowIndex;
    }
};