数组是存放在连续内存空间上的相同类型数据的集合。
下标从0开始
空间是连续的
元素不可删除只能覆盖
1.二分查找法
前提:数组有序,且不重复(重复会返回多个下标)
保证区间定义清晰,坚持循环不变量原则
左闭右闭写法:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
示例:
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;
左闭右开写法:
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
示例:
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;
35. 搜索插入位置
34. 在排序数组中查找元素的第一个和最后一个位置
69. x 的平方根
367. 有效的完全平方数
此题可以参考69的解法,当时即为解
我的解答:https://github.com/kaixuhuang/CPP/blob/master/array/erfen.cpp
2.移除元素
要点:不能移除数组中单个元素,只能覆盖
27. 移除元素
26. 删除有序数组中的重复项
此题需要原地修改,因此当不重复时对慢指针进行+1,返回前n个不重复的值
283. 移动零
此题也是原地修改,方法同26,修改前n个位置为不重复的元素,在后面补充0
977. 有序数组的平方
由于是有序数组,可以从两端求平方,将较小的值存进新数组里
我的解答:https://github.com/kaixuhuang/CPP/blob/master/array/yichu.cpp
3.滑动窗口
所谓滑动窗口,就是不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果。
多用于求子数组
主要确定如下三点:
- 窗口内是什么?
- 如何移动窗口的起始位置?
- 如何移动窗口的结束位置?
滑动窗口的精妙之处在于根据当前子序列和大小的情况,不断调节子序列的起始位置。从而将O(n^2)暴力解法降为O(n)。
904. 水果成篮
采用滑动窗口,设立两个果篮,当超出果篮时终止窗口
示例解法:
class Solution {
public:
int totalFruit(vector<int>& fruits) {
int l=0;
int sub=0;
int r=0;
int fruitsB=fruits[r];//两个果篮,起始都是fruits[0]
int fruitsA=fruits[l];
int result=0;
int n=fruits.size();
while(r<n){
if(fruitsA==fruits[r]||fruitsB==fruits[r]){//遇到不同的水果停下,记录目前的最大值
sub=r-l+1;
r++;
result=result>=sub?result:sub;
}
else{
l=r-1;
fruitsA=fruits[l];
while(l>0&&fruits[l-1]==fruitsA){//将果篮里的水果替换为停下时的水果和它前一位的水果,并且将l移回连续相同的最开端处。
l--;
}
fruitsB=fruits[r];
result=result>=sub?result:sub;
}
}return result;
}
};
76. 最小覆盖子串
未解答
我的解答:https://github.com/kaixuhuang/CPP/blob/master/array/zishuzu.cpp
4.螺旋矩阵
主要考模拟过程
模拟顺时针画矩阵的过程:
- 填充上行从左到右
- 填充右列从上到下
- 填充下行从右到左
- 填充左列从下到上
螺旋矩阵 II
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空格赋值
int offset = 1; // 每一圈循环,需要控制每一条边遍历的长度
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < starty + n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < startx + n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
螺旋矩阵
未解答