今日任务
数组理论学习
- 数组是存放在 连续内存空间 的 相同类型 的 数据集合
- 数组下标从 0开始
- 内存空间 地址连续
- 由于地址空间的连续性
删除 和 添加元素【向数组中间】时,就要移动其他元素
数组元素是不能 直接删除的,需要 覆盖
- 相关操作的时间复杂度
访问元素:支持随机【索引】访问,O(1)
添加元素O(n)----需要移动其他元素的位置
删除元素O(n)
- c++的二维数组在地址空间也是连续的
理解:array[0] [0] 和 array[0] [1]在地址空间是连续的
相差四个字节
- c++中array和vector的区别
-
相同点
1. 都能用下标访问元素 2. 都是顺序容器,采用的属性存储空间
-
不同点
- 创建方式不同
----vector无需指定大小,只需指定类型,eg:vector a
----array要同时指定类型和大小,eg:array<int,3> a
- 内存使用不同
----vector内存空间是动态可变的
----array用多少就申请多少
Vector常用方法
初始化
添加删除
------这里的访问方式是通过迭代器
获取长度
erase()
704.二分查找
题目
代码实现
1.定义:左闭右闭的区间
class Solution {
public:
int search(vector<int>& nums, int target) {
//左闭右闭的区间
int l = 0 , r = nums.size() - 1;
while(l <= r){
int mid = l + (r - l) / 2; //防止溢出,也可以位运算更高效 l + r >>1
if(nums[mid] > target) r = mid - 1;
else if(nums[mid] < target) l = mid + 1;
else return mid ;
}
return -1;
}
};
2.定义:左闭右开的区间
class Solution {
public:
int search(vector<int>& nums, int target) {
//左闭右开的区间
int l = 0 , r = nums.size();
while(l < r){
int mid = l + (r - l) / 2; //防止溢出,也可以位运算更高效 l + r >>1
if(nums[mid] > target) r = mid ;
else if(nums[mid] < target) l = mid + 1;
else return mid ;
}
return -1;
}
};
具体理解
-
题目中 是有序【升序】 的数组 ,有序数组这里我们可以想到二分法
-
循环不变量规则
在循环中坚持 根据查找区间的定义 来做 边界处理
- 区间的定义
我们定义 target 在[left,right]区间内,
27.移除元素
题目
代码实现
- 暴力法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//暴力法,for循环遍历,找到val直接for循环移位
int size = nums.size(); //记录一个size值,在移除后可以更新size,方便返回
for(int i = 0; i < size;i++){
if(nums[i] == val){
for(int j = i + 1; j < nums.size();j++){
nums[j - 1] = nums[j];//将i之后的数组整体向前移一位
}
i--; //举例:i = 0,被覆盖后,进入下一次for循环i++后 i= 1;但是得判断 i= 0位置,因为该值被覆盖了
size--;
}
}
return size;
}
};
- 双指针
- 这是快慢指针,fast和slow同时在0处出发
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//双指针 【快慢指针】
int slow = 0 ,fast = 0;
for(fast = 0; fast < nums.size();fast++){
//相当于为数组重新赋值
//当值不为val的时候,nums[slow] = nums[fast],且slow ++ ;fast进入下一次循环
//当值为val的时候,不进入if语句,fast直接++;进入下一次循环,跳过了所有值为val的地方
//此时slow停在值val的地方,当fast++后不为val,直接赋值给slow
if(nums[fast] != val){
nums[slow++] = nums[fast];
}
}
return slow;
}
};
- 也可以是双向指针 一个从下标0出发,一个从 nums.size() - 1出发
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
//采用相向指针
int l = 0 , r = nums.size() - 1;
while(l <= r){
//左边寻找值为val的下标
while(l <= r && nums[l] != val ) {
l++;
}
//从最右边开始,如果每一个值都为val的话,我们可以直接r--,直接跳过
//我们要找到第一个值不为val的对前面进行覆盖
while(l <= r && nums[r] == val) {
r --;
}
if(l < r){
nums[l++] =nums[r--];
}
}
return l;
}
};
具体理解
- 这道题的主要思想就是:数组的元素在内存地址中是连续的,不能单独删除数组中的某个元素,只能覆盖
所以,当你移除元素后,就必须移动之后元素的位置
- 题目说明在 原地修改我们就不能借助辅助数组
所以只有俩种办法:双重for循环 、双指针
- 一般遇到原地修改数组、链表等都是双指针
- 双指针个人理解有俩种,一种是快慢指针,一种是俩个指针指向不同位置【比如说将数组赋值给辅助数组、相向指针等】
今日感想
二分查找已经不知道写了多少遍了,20遍以上吧,应该
说实话真的快写吐了,觉得之前几期的一个友友说的很好,写二分就像背单词一直abandon、abandon,哈哈哈哈
希望进入这个刷题营能改变自己刷题停滞的现状;
加油!