day1-数组part01| 704. 二分查找、27. 移除元素

文章介绍了C++编程中vector和array两种容器的区别,vector的内存大小可变,array的内存固定。详细阐述了二分查找算法的原理和实现,包括不同区间定义下的二分查找,并提供了多个示例,如搜索插入位置、查找元素的第一个和最后一个位置以及移除元素的方法。
摘要由CSDN通过智能技术生成

数组理论基础

数组是存放在连续内存空间上的相同类型数据的集合。
数组下标从0开始
数组内存空间的地址是连续的
请添加图片描述

c++中vector和array的区别

1、vector是顺序容器,其利用连续的内存空间来存储元素,但是其内存空间大小是能够改变的
2、array是顺序容器,其也是利用连续的内存空间来存储元素,但它的内存空间是固定大小的,申请之后就无法改变。
3、vector的底层是array实现的

二维数组

请添加图片描述

二维数组在内存的空间地址是连续的

704|二分查找

思路

1、把整个数组一分为二;
2、判断目标值在左区间还是右区间,若在左区间,则修改右区间指针的位置;若在右区间,则修改新区间的左区间位置
3、重复上述过程,直到left>(=)right

二分查找的前提条件:

1、数组是有序数组
2、数组中无重复元素

二分法的区间定义

循环不变量规则:在二分查找的过程中,需要保持不变量,在while寻找中每一次边界的处理都要坚持根据区间的定义来操作

二分法区间定义:
1、左闭右闭【left,right】
2、左闭右开【left,right)

【left,right】

  • while(left<=right),left==right是有意义的
  • if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1,如果写middle的话,已知不是却又检查了一次
class Solution {
public:
    int search(vector<int>& nums, int target) {
        int left=0,right=nums.size()-1;//区间是左闭右闭
        while(left<=right){//左闭右闭,则left==right
            int mid=left+((right-left)/2);
            if(nums[mid]>target){//目标值在左区间,则修改右边界
                right=mid-1;
            }else if(nums[mid]<target){//目标值在右区间,修改左边界
                left=mid+1;
            }else
            return mid;
        }
        return -1;
    }
};

【left,right)

  • while (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,right=nums.size()-1;//区间是左闭右闭
        while(left<right){//左闭右开,则left!=right
            int mid=left+((right-left)/2);
            if(nums[mid]>target){//目标值在左区间,则修改右边界
                right=mid;
            }else if(nums[mid]<target){//目标值在右区间,修改左边界
                left=mid+1;
            }else
            return mid;
        }
        return -1;
    }
};

35|搜索插入位置

二分法可以查找数组内外的元素

思路

在于分清target可能位置的几种情况
1、 目标值在数组所有元素之前 [0, -1]
2、目标值等于数组中某一个元素 return middle;
3、目标值插入数组中的位置 [left, right],return right + 1
4、目标值在数组所有元素之后的情况 [left, right], 因为是右闭区间,所以 return right + 1

左闭右闭条件下,情况1、3、4返回的位置都是right+1

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

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

二分法寻找目标值所在的区间

思路

1、分开寻找左右边界
2、分开情况处理
情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}

如果左右边界全部被移动了;说明目标值在数组里

class Solution {
public:
    vector<int> searchRange(vector<int>& nums, int target) {
        int leftBorder = getLeftBorder(nums, target);
        int rightBorder = getRightBorder(nums, target);
        // 情况一
        if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
        // 情况三
        if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
        // 情况二
        return {-1, -1};
    }
private:
     int getRightBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] > target) {
                right = middle - 1;
            } else { // 寻找右边界,nums[middle] == target的时候更新left
                left = middle + 1;
                rightBorder = left;
            }
        }
        return rightBorder;
    }
    int getLeftBorder(vector<int>& nums, int target) {
        int left = 0;
        int right = nums.size() - 1;
        int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
        while (left <= right) {
            int middle = left + ((right - left) / 2);
            if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
                right = middle - 1;
                leftBorder = right;
            } else {
                left = middle + 1;
            }
        }
        return leftBorder;
    }
};

27. 移除元素

通过一个快指针和慢指针在一个 for 循环下完成两个 for 循环工作
快指针可以理解成在旧数组中找非目标元素,然后赋值给慢指针指向的新数组,虽然都指向一个数组

定义快慢指针

  • 快指针:寻找新数组的元素,新数组就是不包含要删除元素的数组
  • 慢指针:更新新数组

思路

  • 快指针移动,如果不是目标元素,快指针指向就赋值给慢指针指向,慢指针移动

  • 如果是目标元素,则慢指针不操作,快指针继续移动。

  • 慢指针最后指向的下标,就是新数组中的大小

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;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值