数组理论基础
数组——存放在连续内存空间上的相同类型数据的集合
在C++中,vector和array的区别,vector的底层实现是array,vector的本质是容器不是数组
在C++中,二维数组是连续分布的
Leetcode 704二分查找
Leetcode题目链接
力扣https://leetcode.cn/problems/binary-search/
给定一个n个元素有序的整型数组和一个target,写一个函数搜索nums中的target,如果存在返回下标,不存在返回-1
重点——区间问题
[left, riht]左闭右闭,[left,right)左闭右开
左闭右闭——[left,right]
left = 0
right = num_size-1
while(left <= right){ //保证区间合理
middle = (left + right)/2
if(nums[middle] > target){
right = middle -1 //已经保证不包含middle了
}
elseif(nums[middle] < targrt){
left = middle + 1 //已经保证不包含middle了
else{
return middle
}
return -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 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; // 数组中找到目标值,直接返回下标
}
}
// 未找到目标值
return -1;
}
};
左闭右开——[left,right)
left = 0
right = num_size-1
while(left < right){ //保证区间合理
middle = (left + right)/2
if(nums[middle] > target){
right = middle //由于右开,所以right直接 = middle没问题
}
elseif(nums[middle] < targrt){
left = middle + 1 //由于左闭,所以left需要 = middle+1
else{
return middle
}
return -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 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;
}
};
时间复杂度:O(logn)
空间复杂度:O(1)
讨论点
- 二分的几种情况,l=0,r=n时,while(l<r)是正确的写法,而当r = n-1时,while(l<=r)是正确写法
- 二分的本质:根据是否满足题目的条件来缩小答案所在的区间
- 二分的前提:有序数组
- 最大的优势:时间复杂度为O(logn),看到有序数组需要反应是否可以使用二分
- 二分mid溢出的问题:mid = l+(r-l)/2 or mid = l+((r-l)>>1)
Leetcode27.移除元素
删除数组中的元素,数组的连续很重要,删除元素不仅仅是删除,而是覆盖
数组的erase函数是O(n)的一个操作
暴力实现
时间复杂度为O(n^2)
双指针解法
快慢指针定义
- 快指针:寻找新数组的元素,即不包含目标元素的数组
- 慢指针:指向更新新数组下标的位置
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]; //将快指针指向的数传给慢指针指向的地方
slowIndex++;
}
}
return slowIndex;
}
};
时间复杂度:O(n)
空间复杂度:O(1)
讨论点
- 快指针可以理解成在旧数组中找到非目标元素,然后赋值给慢指针指向的新数组
- fast < nums.size()正确,而fast < nums.size()-1时报错,由于后者会在空数组时报错,
vector的size()函数返回值是无符号整数,空数组时返回0,-1得到溢出