之前停了太久没刷题,前面的都忘了,重新复习一下
704 二分查找
思路:首先这是一个查找问题,接着要发现题目给出的是有序数组(升序),且数组元素无重复(可用二分法),当满足以上条件时,就应该考虑是否用二分法!
想到二分法,就要想到边界问题,这是二分法中最重要的问题,是左闭右开,还是左闭右闭,还是左开右闭?
选定一个边界,就可以开始写代码了!
本次选择的是左闭右闭区间,代码如下:
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0, right = nums.size(); //左闭右开区间
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 n^2 n2),故考虑双指针,时间复杂度为O(n)。
定义快慢指针,快指针寻找新数组元素,慢指针为新数组赋值。
代码:
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int slowIndex = 0;
for(int fastIndex = 0; fastIndex < nums.size(); fastIndex++) {
// 寻找新数组元素,所以这里要选用不等于
if(nums[fastIndex] != val) {
nums[slowIndex++] = nums[fastIndex];
}
//如果当前元素等于val,则直接跳过,fast++
//运行结束之后,slow指向当前数组下标的下一位,即为新数组长度
}
return slowIndex;
}
};
977 有序数组的平方
问题:这题在写的过程中少了(nums.size(), 0)这一部分,就是缺少了对动态数组result的初始化,导致运行出错。因为我对c++的stl相关内容不了解,所以也不知道如何对vector(vector是C++标准模板库(STL)中的一个容器,它是一个动态数组,可以根据需要自动调整大小)进行初始化。
vector<int> result(nums.size(), 0);
这行代码的意思是设置result向量的长度为nums.size(),且初始化数组元素为0。
思路:方法说是双指针,但我感觉是三指针。因为要将平方之后的数据大小进行排序,又考虑到原有数据就是有排序过的,所以只要考虑负数平方之后变大的情况就可以了。因此,最大值只会出现在数组两端,故考虑头尾各放置一个指针来进行平方后的大小比较,再引入第三个指针来指向新数组的末端,从后往前填入数据(即从大到小)。
代码:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(), 0);
int k = nums.size() - 1; //指向新数组的最后一位,从大到小填进去
for(int i = 0, j = nums.size() - 1; i <= j; ) { //头尾两个指针
if(nums[i] * nums[i] > nums[j] * nums[j]) {
result[k--] = nums[i] * nums[i];
i++;
}
else {
result[k--] = nums[j] * nums[j];
j--;
}
}
return result;
}
};
209 长度最小的子数组
思路:在给定数组中找到连续子数组之和大于等于目标值的最短数组,并返回其长度。要注意是连续子数组。可以很自然的想到滑动窗口法(实现还是双指针),就是一个框框住数组,不断往前滑动,问题是怎么滑动,窗口长度怎么变?
要有一个for循环遍历数组,哪个指针来遍历?应该是后一个指针
代码:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int result = INT32_MAX;
int subLength = 0;
int i = 0;
int sum = 0;
for(int j = 0; j < nums.size(); j++) {
sum = sum + nums[j];
while(sum >= target) {
subLength = j - i + 1;
result = result < subLength ? result : subLength;
sum = sum - nums[i];
i++;
}
}
return result == INT32_MAX ? 0 : result;
}
};
59 螺旋矩阵 II
思路:本质是通过多个循环来完成每一条边的绘制,一个循环画一条边。需要注意的是,这里仍然存在边界问题,每一条边的起始点从哪里开始,到哪里结束,要搞清楚。根据对称原则,应该采用左闭右开区间,才能使得绘制的每一条边都等长。需要设置较多变量,写代码时不一定能写得出来。
这里又出现了和上面同样的问题,向量的初始化,这里算是二维向量,也即矩阵,初始化格式为
vector<vector<int>> res(n,vector<int>(n,0));
表示初始化了n个n维向量vector<int>(n)
,每个向量的初值为0。
代码:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n,0));
int loop = n/2;
int startX = 0, startY = 0;
int offset = 1;
int count = 1;
while(loop) {
int i = startX, j = startY;
for( ; j < n - offset; j++) {
res[i][j] = count++;
}
for( ; i < n - offset; i++) {
res[i][j] = count++;
}
for( ; j > startY; j--) {
res[i][j] = count++;
}
for( ; i > startX; i--) {
res[i][j] = count++;
}
loop--;
startX++;
startY++;
offset++;
}
if(n % 2 == 1) {
res[startX][startY] = count;
}
return res;
}
};
数组专题小结
704 二分查找: 有序数组、无重复;重点考虑边界:左闭右开?左闭右闭?
27 移除元素: 数组不可删除,只可覆盖;双指针法,快指针遍历寻找新数组元素,慢指针将元素写进新数组
977 有序数组的平方: 有序数组平方后排序,考虑负数,最大值只会出现在两边,用双指针指向两端进行比较,将大的值填入新数组,新数组从后往前填充
209 长度最小的子数组: 寻找子数组之和达到目标值的连续的最短子数组,注意是连续,所以考虑滑动窗口法,本质是双指针;重点是后一个指针来遍历,前一个指针只有当子数组之和达到目标值之后,才会往后移动
59 螺旋矩阵 II: 没有什么技巧,就是通过多个for循环画出每一条边,要注意的仍然是边界问题,应采用左闭右开
小结: 二分查找和螺旋矩阵都要考虑边界问题;移除元素、有序数组的平方和长度最小的子数组都用到了双指针;说明数组专题中重点考虑边界和双指针法。