代码随想录算法训练营第二天|977有序数组的平方、209长度最小的子数组、59螺旋矩阵

leetcode977有序数组的平方

用暴力算法,先把所有数平方,再排序,在八种排序算法中,快速排序的时间复杂度为O(nlogn),采用快速排序比较好,C++的sort()函数是一种类似快排的方法

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int n = nums.size();
        
        for(int i = 0; i < n; i++)
            nums[i] *= nums[i];
        sort(nums.begin(), nums.end());

        return nums;
        }
};

双指针法

本题更好的方法是双指针法

解析里有一句话很关键,数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间

这里复习了一下for语句格式

  • for 语句中的三个表达式可部分或全部省略,但两个分号不能省略。
  • 常见的是:将表达式1放在了 for 语句之前,或表达式3放在了循环体中,在 for 语句中就可相应地省略表达式1或表达式3。

同时这里需要注意到i<=j还是i<j的问题

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;){ // i++和j++放在了循环体中 // 必须是i<=j,否则最后剩下的元素会处理不到
            if(nums[i]*nums[i] < nums[j]*nums[j]){
                result[k--] = nums[j]*nums[j];
                j--;
            }
            else{
                result[k--] = nums[i]*nums[i];
                i++;
            }
        }
            return result;
        }
};

209长度最小的子数组

我的思路是这样的,第一层循环指定窗口大小,第二层循环指定窗口起始下标,第三层循环就是相加,结果,超时,三层循环,复杂度得有O(n^3)了吧,超时是应该的。

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int n = nums.size();
        int minlen = 0;
        for(int i = n; i > 0; --i) // 长度
        {   
            for(int j = 0; j <= n-i; ++j) // 起始下标
            {
                int sum = 0;
                for(int k = j; k <= j+i-1; ++k) // 求和
                {
                    sum += nums[k];
                }
                if(sum >= target) minlen = i;
            }
        }
        return minlen;
    }
};

看了解析的暴力解法,发现我想的过于复杂了,两层循环的思路是,第一层循环指示起始位置,第二层循环是从起始位置开始加,只要加到值大于target就break,break之前比较子序列长度,如果此时子序列长度小于上一次的子序列长度,就更新,复杂度为O(n^2)。

不过暴力算法还是超时了,leetcode要求为O(n)或O(nlogn)

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX; // 最终结果
        int sublength = 0; // 子序列长度
        int sum = 0; // 子序列数值之和
        for(int i = 0; i < nums.size(); ++i)
        {
            sum = 0;
            for(int j = i; j < nums.size(); ++j)
            {
                sum += nums[j];
                if(sum >= target)
                {
                    sublength = j - i + 1;
                    result = result < sublength ? result : sublength;
                    break;
                }
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

滑动窗口

这是本题的重要解法,只需一个for循环,我猜想之所以里面有一个while循环但是最终复杂度为O(n)的原因是while其实只循环了一次,因为sum减去起始值后必定小于target,退出循环

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0;
        int sublength = 0;
        int i = 0; // 要先给出滑动窗口起始位置,后面才好算窗口大小并且改变起始位置
        for(int j = 0; j < nums.size(); ++j){
            sum += nums[j];
            while(sum >= target){
                sublength = j - i + 1;
                result = result < sublength ? result : sublength;
                sum -= nums[i++]; // 这里是两个语句,先让sum减去起始值,再让起始位置后移
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

59螺旋矩阵||

上图是我的分析过程,然后写出了一个不忍直视的代码,并且结果执行出错

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        int k = n;
        int r = 1, c = 0; // row=1代表行数递增,col=0代表列数递减
        int row = 0, col = 0;
        int num = 1; // 第一个存入的数字
        vector<vector<int>> nums(n, vector<int>(n));
        while(k > 0){
              if(k == n){
                  for(int i = 0; i < k; i++){
                      nums[0][col] = num++; // 每存一个数字,num加一
                      col++;
                  }
                  col--;
                  k--;
              }
              else{
                  if(row == 1){
                      for(int i = 0; i < k; i++){
                          row++;
                          nums[row][col] = num++;
                      }
                  }
                  else{
                      for(int i = 0; i < k; i++){
                          row--;
                          nums[row][col] = num++;
                      }
                  }
                  if(col == 1){
                      for(int i = 0; i < k; i++){
                          col++;
                          nums[row][col] = num++;
                      }
                  }
                  else{
                      for(int i = 0; i < k; i++)
                      col--;
                      nums[row][col] = num++;
                  }
                  k--;
              }
        }
        return nums;
        }
};

看了一下解析,我的思路是每个循环填充一个行一个列,解析里面是每个循环填充一下四周,我想我的代码或许还可以再改改,今天来不及了,等周末查漏补缺补上

看了解析,我发现一定要注意左右闭开的问题,解析里采用的是左闭右开,如图

代码如下

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> nums(n, vector<int>(n, 0));
        int startx = 0, starty = 0; // 每一圈的起始位置
        int loop = n/2; // 圈数
        int mid = n/2; // 矩阵最中间的位置
        int count = 1; // 每次填充的数字
        int offset = 1; // 限制每一圈每个边的长度
        int i, j;
        while(loop--){
            i = startx;
            j = starty;
            // 每次填充都是左闭右开
            for(j = starty; j < n-offset; j++){
                nums[startx][j] = count++; // 这里也可以写nums[i][j]
            }
            for(i = startx; i < n-offset; i++){
                nums[i][j] = count++; //比如上一个for循环最后一个是nums[0][n-2],因为左闭右开,nums[0][n-1]是这里才填充上的
            }
            for(; j > starty; j--){
                nums[i][j] = count++;
            }
            for(; i > startx; i--){
                nums[i][j] = count++;
            }
            // 每一圈的起始都在对角线上
            startx++;
            starty++;
            // 控制每一圈里每一条边遍历的长度
            offset++;
        }

            // 当n为奇数,需要单独个矩阵最中间的位置赋值
            if(n % 2){
                nums[mid][mid] = count;
            }
            return nums;
        }
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值