数组理论基础
数组是存放在连续内存空间上的相同类型数据的集合。内存上的地址空间是连续的,所以添加或删除元素的时候需要移动其他元素,覆盖原有的元素,而不能直接删除。
704. 二分查找
题目链接
二分查找的一个前提条件就是,有序且不重复的数组,在这样的数据中找到某一个满足条件的元素。二分查找的时间复杂度是O(logn)。
重点
区间的定义是循环不变量,从始至终边界的处理都要根据循环不变量。
-
区间边界问题
-
左闭右闭
定义target在[left, right]中,循环条件就是 while (left <= right),if (nums[middle] > target) ,right值应该为middle-1。 -
左闭右开
定义target在[left, right)中,循环条件就是 while (left < right),if (nums[middle] > target) ,right值应该为middle。
-
-
middle的表示问题
middle不能表示为(right + left) / 2,会溢出,而应该表示为left + (right - left) /2。
代码
- 左闭右闭
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(nums[middle]>target){
right=middle-1;
}
else if(nums[middle]<target){
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(); //right取不到
while(left<right){
int middle=left+(right-left)/2;
if(nums[middle]>target){
right=middle;
}
else if(nums[middle]<target){
left=middle+1;
}
else{
return middle;
}
}
return -1;
}
};
27. 移除元素
题目链接
数组中没有真正的删除,只有覆盖。
重点
有两种解法,最容易想到的是暴力解法,两层for循环,一层查找,一层移动后面的元素,时间复杂度O(n^2)。
但重点要掌握的是双指针的思想,即快慢指针法,快指针在前面探路,慢指针在后面接收新的数组元素,在一个for循环下完成两个for循环的工作,时间复杂度O(n)。
代码
- 暴力解法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size=nums.size();
for(int i=0;i<size;i++){//这里是size,不是nums.size()
if(nums[i]==val){
for(int j=i+1;j<size;j++){
nums[j-1]=nums[j];//覆盖操作,注意j的边界范围
}
i--;//超容易想不到
size--;//size的位置,两个for循环都结束后再减减
}
}
return size;
}
};
- 双指针(快慢指针)
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slow=0;
for(int fast=0;fast<nums.size();fast++){
if(nums[fast]!=val){
nums[slow]=nums[fast];
slow++;
}
}
return slow;//不用nums.size()了,这个slow就是新数组的大小
}
};
之前这两道题都刷过,二刷随稍微轻松一些,但由于算法水平不好,总是在各种各样的问题上出错,甚至我清楚地记得上次也是这么错的,所以我把出过问题的地方标了注释,以后再看也是一个提醒。这是我第一次写博客,也是第一次尝试使用markdown编写文档,废了不少时间,但是很有收获。