这次的题目977和209主要考察双指针的应用、
首先是第997题:有序数组的平方
题目:
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
示例 1:
输入:nums = [-4,-1,0,3,10] 输出:[0,1,9,16,100] 解释:平方后,数组变为 [16,1,0,9,100] 排序后,数组变为 [0,1,9,16,100]
示例 2:
输入:nums = [-7,-3,2,3,11] 输出:[4,9,9,49,121]
提示:
1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums
已按 非递减顺序 排序
这里有一点就是这个非递减顺序,就是递增,刚开始我还懵了一下
最简单的方法就是把每个元素全部平方然后使用快速排序,详见下面代码,这里就不细讲,看不懂的话可以复习一下快速排序(我这里的分区法是挖空法,还有另外两种方法可以使用)
class Solution {
public:
int partition(vector<int>& nums,int start, int end){
int pivot = nums[start];
while (start < end){
while (nums[end] > pivot && start < end){
end--;
}
nums[start] = nums[end];
while (nums[start] <= pivot && start < end){
start++;
}
nums[end] = nums[start];
}
nums[start] = pivot;
return start;
}
void quick_sort(vector<int>& nums, int start,int end){
if (start >= end){
return;
}
int mid = partition(nums, start, end);
quick_sort(nums, start, mid-1);
quick_sort(nums, mid+1, end);
}
vector<int> sortedSquares(vector<int>& nums){
int pivot = nums[0];
for (int i = 0; i < nums.size(); i++){
nums[i] = nums[i]*nums[i];
}
quick_sort(nums, 0, nums.size()-1);
return nums;
}
};
第二种方法是比较简单的解法:双指针法
因为输入的数组就是从小到大的,所以我们需要顾忌的排序其实主要是整数和负数绝对值之间的比较,绝对值更大的数平方肯定就会更大,要是有负数存在的话,只可能在两边然后中间有0。
设置一个left指针从数组头开始,right指针从数组尾部开始,每次比较left和right位置元素的绝对值大小,每次选择最大的放在最右边(因为最大的只有可能在两边。当没有负数的时候,最小值在左边,有负数的时候不知道在中间什么地方,所以我们每次把最大的元素找到放在结果数组的最右边)
新开一个result数组(初始化大小和nums大小一样)用来存放结果,维护一个result的索引k用来放入数据,初始为最右边,当left指针指的元素大于right指针指向的元素,就把left指针指向的元素(最大)放到result数组k处,将left指针向右移动;反之,如果right指针指向的数字更大就把这个更大的元素放在k索引处,right向左移动一次。每次成功放入result数组后,索引向前一个用来放下一个元素。当left>right的时候说明所有元素都比较完了,结束循环返回result。这里left=right仍然循环是因为方便放元素到result里,不然有一个元素漏掉了没放,循环外面处理就更麻烦。
下面是c++代码
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums){
int left = 0;
int right = nums.size()-1;
vector<int> result(nums.size(),0);
int k = nums.size()-1;
while (left <= right){
if (abs(nums[left]) > abs(nums[right]) ){
result[k--] = pow(nums[left], 2);
left++;
}else{
result[k--] = pow(nums[right], 2);
right--;
}
}
return result;
}
};
接下来是209题:长度最小的子数组
题目:
给定一个含有 n
个正整数的数组和一个正整数 target
。
找出该数组中满足其总和大于等于 target
的长度最小的 连续
子数组
[numsl, numsl+1, ..., numsr-1, numsr]
,并返回其长度。如果不存在符合条件的子数组,返回 0
。
示例 1:
输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]
是该条件下的长度最小的子数组。
示例 2:
输入:target = 4, nums = [1,4,4] 输出:1
示例 3:
输入:target = 11, nums = [1,1,1,1,1,1,1,1] 输出:0
提示:
1 <= target <= 109
1 <= nums.length <= 105
1 <= nums[i] <= 105
这道题也是使用双指针的思想
因为要寻找长度最小的子数组,且元素之和一定要大于等于target,这里提醒大家一定要认真审题,我刚开始以为是等于target,哐哐哐写完以后发现错了还反应不过来。
同样的,设置一个左指针i一个右指针j,维护一个变量sum初始化为0,用来计算和,接着维护一个变量length用来记录最小长度,初始化为数组的大小+1,因为就算全部数组元素加起来才刚好等于target也应该是数组大小,不可能大过数组大小还+1。
接下来移动指针j,每次移动的时候sum都要加上j值,当sum刚刚好大于或等于target的时候开始执行操作,设置一个while循环(因为要是这时候j指针再往后移动就是没必要的增加数组长度,后面再加肯定更比target大了)如果当前子数组长度小于length,就把length设置为子数组长度(注意子数组长度是j-i+1)然后将i往右边移动一个单位,减去前一个值以后,继续看target和数组和之间大小关系(这里可能没想到,给个提示,比如数组[1,2,3,1],target = 4,最先出来的肯定是[1,2,3], 但是实际上仅仅i往后面移动一个单位的话是数组[2,3]满足比target大且比之前那个数组长度小)最后i和j从头遍历到尾后跳出循环,准备返回长度,如果length还是之前的数组大小+1的话,必定是没有找到子数组,设置length = 0,然后最后统一返回length。
下面是c++代码:
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums){
int i = 0;
int length = nums.size()+1;
int sum = 0;
for (int j = 0; j<nums.size(); j++){
sum += nums[j];
while (sum>=target){
if (j-i+1 < length){
length = j-i+1;
}
sum -= nums[i++];
}
}
if (length == nums.size()+1){
length = 0;
}
return length;
}
};
59题主要考察对逻辑的处理
题目:
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1 输出:[[1]]
提示:
1 <= n <= 20
其实这道题本质上最难的是处理边界条件,要统一处理每一条边的边界条件,这道题我选择对于每一行,不处理最后一个值,下一行从之前的最后一个值(也就是下一行开头)开始。以示例为例,比如第一行我就把1 2放入数组,然后下一行就是3 4,以此类推 。
在处理之前,因为每一圈起点和终点都不一样,所以我们首先要维护两个变量:startx和starty作为每次出发的起点,初始化为0,接着是count作为插入结果的值,每次加1,初始化为1就好。然后就是插入的位置坐标,这里我设置为i和j,分别初始化为0。设置result数字,初始化大小填充0进去,最后要设置的就是偏移量offset,初始化为0,作用后面会讲。
不难发现,转圈的次数和n紧密相关,就是n/2次,但是这里有个问题,当n是奇数的时候,正方形容器(这里可以看那个示例)最最中间的值插入会被忽略,所以后面会额外处理这个。
最外层for循环是转圈次数
接下来开始遍历从左到右的行,我们习惯写[i][j],所以刚开始是写j的for循环。因为每一行最后一个元素不管,对于第一行j<n-1,第二行(第二圈循环)j<n-2,以此类推,所以减掉的数字就是偏移量offset,每次最外层循环结束后会累加。把count累加放入result数组对应位置中,因为是从左向右所以纵向位置还是starty不变,横向位置就是j的位置。
现在遍历右边从上到下的行,与前面结束条件一样,唯一不一样的是result索引为[i][j]因为前面j已经固定到横向终点的地方了,纵向移动的时候横坐标是不会变的。
然后遍历下面从右到左的行,这里与前面基本一样,唯一不同的点是结束条件是j要大于起点
最后以与上面相同的形式从下到上遍历左边
最后的最后,跳出外层for循环后,我们已经完成转圈插入的过程了,需要处理的是当n是奇数的时候插入中间的值,在本例中就是那个9,因为我们一直在增加startx和starty,虽然for循环结束了,startx和starty已经累加到第三圈起始点(这一“圈”只有一个值),之前最后一次插入值的时候count也已经累加1了,就直接作等result[starty][startx] = count即可,返回result数组,题目结束
下面是c++代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
int startx = 0;
int starty = 0;
int offset = 1;
int count = 1;
int j = 0;
int i = 0;
vector <vector<int>> result(n, vector<int>(n,0));
for (int time = 0; time < n/2; time++){
for (j = startx;j < n-offset; j++){
result[starty][j] = count++;
}
for (i = starty;i < n-offset; i++){
result[i][j] = count++;
}
for(;j > startx; j--){
result[i][j] = count++;
}
for(;i > starty; i--){
result[i][j] = count++;
}
offset++;
startx++;
starty++;
}
if (n%2){
result[starty][startx] = count;
}
return result;
}
};