有序数组的平方
leetcode 977
调库
看到题目,最简单的思路就是直接排序,先将数组nums中的元素平方后,对nums进行排序
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> ivect;
for (auto x: nums) {
ivect.push_back(num * num);
}
sort(ivect.begin(), ivect.end());
return ivect;
}
};
算法的时间复杂度考虑STL库中排序方法,为O(nlogn),空间复杂度对排序来说为O(logn),对存储来说为O(n),考虑较大值,我认为是O(n)。
双指针,正序
在对数组中元素平方后,寻找数组中的最小值的下标索引smallest,创建一个新的vector ivect,长度与原数组相同,且ivect的第一个元素为平方数组numsqure索引为smallest的值,构建两个指针left和right,用于对平方数组的索引,left = smallest - 1,right = smallest + 1,以及一个cur用来指示应键入ivect数组的位置。
可用while和for循环来完成新数组元素的填入,本质都是数组的长度,针对for循环,可以考虑for(cur;cur<ivect,size();cur++),while循环考虑考虑(right - left < ivect.size() + 1)(此处加1的原因在于我对每次键入数据要对left--或right++,导致超出索引,以[0,4]的数组为例,最后left为-1,right为5),在循环体内,对numsqure[left]和numsqure[right]进行比较,较小值放在ivect[cur]的位置上,若放置为nums[left],则left--,若放置为nums[right],则right++,此外,索引cur++,同时,考虑两个特殊情况,当left == -1时,左边已经没有任何数据,可将right之后的数据完全放入数组,ivect[cur++] = numsqure[right++],当right == nums.size()-1时,右边已无任何数据,但考虑到左边数据为逆序,需反向放入数组中 ivect[cur++] = numsqure[left--];
循环结束,返回数组ivect。
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
auto length = nums.size();
vector<int>numsquare;
for(auto x:nums){
numsquare.push_back(x*x);
}
vector<int>ivect(length);
int left =0;
int smallest = 0;
int right = 0;
for(smallest; smallest< length-1;smallest++){
if((numsquare[smallest] - numsquare[smallest+1])<0)
break;
}
int cur{1};
ivect[0] = numsquare[smallest];
left = smallest - 1;
right = smallest + 1;
while((right - left)<length+1){
if(right == length){
ivect[cur++] = numsquare[left--];
continue;
}
if(left == -1){
ivect[cur++] = numsquare[right++];
continue;
}
if(numsquare[left]<numsquare[right]){
ivect[cur++] = numsquare[left--];
}
else{
ivect[cur++] = numsquare[right++];
}
}
return ivect;
}
};
算法的时间复杂度应该为O(n),空间复杂度排序为O(1),存放答案的数组为O(n)。
写的比较差,周末有时间再改改,leetcode上消耗内存击败6.09%的C++用户,悲。
双指针法(参考代码随想录B站视频)
数组的平方考虑二次函数的曲线,在本题是两边大,中间小,创建双指针指向头尾,逆向更新数组,这种方法对边界的考虑更为简单。
利用for循环,创建i和j两个指针,分别指向数组的首和尾,注意for循环中判断的条件为i<=j,若为i<j,则会存在一个元素未排入新数组。由于i和j的更新取决于是否将i和j指向的元素放入新的数组,所以在for循环条件中将更新条件丢弃,在循环体内更新i和j。写法为: for(i,j;i<=j;){},在循环体内,用nums[i]*nums[i]和nums[j]*nums[j]进行大小比较,即可减少空间的使用,方法二的代码可以更新。
class Solution {
public:
vector<int> sortedSquares(vector<int>& A) {
int k = A.size() - 1;
vector<int> result(A.size(), 0);
for (int i = 0, j = A.size() - 1; i <= j;) { // 注意这里要i <= j,因为最后要处理两个元素
if (A[i] * A[i] < A[j] * A[j]) {
result[k--] = A[j] * A[j];
j--;
}
else {
result[k--] = A[i] * A[i];
i++;
}
}
return result;
}
};
长度最小的子数组
自己的思路(有错误)
长度最小的子数组,要找和大于等于target的长度最小的子数组,先看题中给定条件,首先,给定的数组和target均为正整数,若子数组存在,则长度最小的子数组,考虑到题目中数据量非常大,如果使用暴力求解,两次遍历一定会超时,所以考虑其他想法,想法一:长度最小的子数组应该包含数组中最大的数,所以可以考虑找到最大值的索引,在最大索引附近区域进行搜索,添加形成一个子数组。(错误的预设前提,考虑[4,6,1,8,1],target = 10)想法二:完整数组的和一定是最大的,i,j分别表示数组头和数组尾的索引,值为0和nums.size()-1,考虑从数组和中删去nums[i]和nums[j]中较小数值的索引,并更新i和j,并不断继续,当两侧都无法删除时,得到一个最接近的子数组,但我不能确认是否为长度最小的子数组。先按这个写,同时要先排除2个特殊情况,不存在和子数组长度为1的情况,开头需要遍历一边数组,并计算得到数组的和sumnum,若sumnum<target,直接return 0,此外,在遍历过程中,得到maxofnum,若maxofnum>=target,return 1。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
long long maxsum = 0;
int maxofnum = 0;
int left = 0;
int right = nums.size()-1;//滑动窗口的索引 left和right,
for(int i = 0; i < nums.size(); i++){
maxsum += nums[i];
if(nums[i]>maxofnum)
maxofnum = nums[i];
}
if(maxsum<target)
return 0;
if(maxsum == target)
return nums.size();
if(maxofnum == target)
return 1;
int currentlength = nums.size();
while(maxsum >= target){
if(nums[left]<=nums[right]){
maxsum -= nums[left];
if(maxsum>=target){
left++;
currentlength--;
}
}
else{
maxsum -= nums[right];
if(maxsum>=target){
right--;
currentlength--;
}
}
}
return currentlength;
}
};
leetcode 测试用例19/21 也可以预见,这只是个局部最优,思路还是不对。时间复杂度O(n),空间复杂度O(1)。
滑动窗口法(参考代码随想录)
思考如果使用暴力求解的方法,则需要使用两个for循环,一个for循环找最优的初始位置,一个for循环找最优的终止位置,而在这种循环过程中,有很多冗余。
考虑2个指针left和right,left代表最优的初始位置,right代表最优的终止位置,也是循环的判断终点。
以right为循环条件,在循环体中对i进行自加或者不变,当j到达nums.size()时,循环结束
for(right;right<nums.size();right++)
left和right都从0开始,创建变量sum,在每次right的循环过程中,sum+=nums[right],此时,若sum大于等于target,则需计算此时的子长度,子长度为right - left + 1;并将其与全局的最短子长度进行比较,若小于全局的最短子长度,取而代之。且在这时,对left进行改变,sum -= nums[left],此时也应判断sum是否满足大于等于target,满足时则同样计算子长度并比较,直到不满足时,进行下一次的循环。具体的动画可以参考代码随想录官网的内容。
之后,判断我们得到的全局最短子长度是否合理,合理则返回,否则返回0。
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int left = 0;
int right = 0;//滑动窗口的索引 left和right,
int minsubarraylen = INT_MAX;
int temp = -1;
int sum = 0;
for(right;right<nums.size();right++){
sum += nums[right];
if(sum>=target){
temp = right - left + 1;
if (temp < minsubarraylen)
{
minsubarraylen = temp;
}
while(sum >= target){
sum -= nums[left];
left++;
if(sum>= target) {
temp = right - left + 1;
if (temp < minsubarraylen)
{
minsubarraylen = temp;
}
}
}
}
}
if(minsubarraylen>nums.size()+1){
minsubarraylen = 0;
}
return minsubarraylen;
}
};
算法的时间复杂度为O(n),空间复杂度为O(1)
螺旋矩阵
直观思路:
创建一个n*n维的二维数组,vector<vector<int>> matrix(n, vector<int>(n, 0)),由于行和列都为n,一个循环从0到n^2,按顺时针放入矩阵中,同时需要注意的是,vector数组的顺序是从0到n-1,二维数组的赋值:matrix[row][column] = vals;
由于顺时针赋值,考虑四种模式,向右[0,0]->[0,max],向下[1,max]->[max,max],向左[max-1,max]->[min,n-1],向上[min, min+1],min和max为能存放的最大和最小的为0的区域,在向下结束后,max-=1;在向左完成后,min += 1;这种方式有一个问题,当n为2时,如果只是循环向下会越界,单独取出进行return
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n,vector<int>(n,0));//创建一个n*n维全0的二维数组
int count = 1;
int maxnum = n*n;
int column = 0;
int row = 0;
int min = 0;
int max = n - 1;
int mode = 0;
if(n == 2){
matrix = {{1,2},{4,3}};
return matrix;
}
while(count!=maxnum+1){
if(mode%4==0){
matrix[row][column] = count;
count++;
if(column == max){
row += 1;
mode++;
continue;
}
column++;
}
if(mode%4==1){
matrix[row][column] = count;
count++;
if(row == max){
column -= 1;
mode++;
max -= 1;
continue;
}
row++;
}
if(mode%4==2){
matrix[row][column] = count;
count++;
if(column == min){
row -= 1;
mode++;
min++;
continue;
}
column--;
}
if(mode%4==3){
matrix[row][column] = count;
count++;
if(row == min){
column += 1;
mode++;
continue;
}
row--;
}
}
return matrix;
}
};
算法时间复杂度O(n^2),空间复杂度O(1);