数组是存放在连续内存空间上的相同类型数据的集合。
下标从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)/2if (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] == targetreturn 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] == targetreturn 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;}};
螺旋矩阵
未解答
