DAY 2!有序数组的平方,长度最小的子数组,螺旋矩阵

题目:977. 有序数组的平方

链接: leetcode题目链接

给你一个按 非递减顺序 排序的整数数组 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).1 <= nums.length <= 104
2)-104 <= nums[i] <= 104
3)nums 已按 非递减顺序 排序。

进阶:
请你设计时间复杂度为 O(n) 的算法解决本问题。

实现算法:双指针

前后相向比较遍历

因为数组是非递减有序数列,但存在负数的平方的情况,因此前后遍历平方后数组时,大概率是由大到小再到大,因此头尾双指针比较最大值,相向向中间移动,可以排出新数组从后到前的顺序。

// 双指针法
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;
    }
};

自我实现

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        int len = nums.size();
        vector<int>nums_2;
        for (int i = 0; i < len; i++) {
            nums_2.push_back(pow(nums[i], 2));
         
        }
        int left = 0;
        int right = len - 1;
        for (int j = len-1; j >=0; j--){
                if (nums_2[right] > nums_2[left]) {
                    nums[j] = nums_2[right];
                    right--;
                }
                else { 
                    nums[j] = nums_2[left];
                    left++;
                }


        }
         
        return nums;
    }
};

p.s.
乘方运算用pow()函数,尝试使用nums[i]^2失败。

做题心得

一开始忽略了数组有序的问题,虽然实现了排序功能,但是时间复杂度超了,用了选择排序,复杂度O(n^2)。看了题解思路后,自己实现了代码,AC了,本地调试有点诡异小问题,但无伤大雅。
关于平方表示问题,一开始没有用pow(),用 ^,运算错误,说是要了解一下位运算,这是异或运算符,后面再慢慢了解吧。

题目:209. 长度最小的子数组

链接: leetcode题目链接

给定一个含有 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)1 <= target <= 109
2)1 <= nums.length <= 105
3)1 <= nums[i] <= 105

进阶:
如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

实现算法:滑动窗口

双指针不断调节子序列的起始位置和终止位置

如果用嵌套循环遍历的话,时间复杂度应为O(n^2),而使用滑动窗口可以将时间复杂度将为O(n)。
算法原理是,先让右侧终止点开始遍历,找到最初的>=target的子数组,得到最初的子数组长度。然后起始点开始移动,找到该终止点下最短的子数组,并与已有子数组长度比较,若比已有长度短,则更新子数组长度。一旦起始点移动之后子数组和不满足条件

// 滑动窗口
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX;
        int sum = 0; // 滑动窗口数值之和
        int i = 0; // 滑动窗口起始位置
        int subLength = 0; // 滑动窗口的长度
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            // 注意这里使用while,每次更新 i(起始位置),并不断比较子序列是否符合条件
            while (sum >= s) {
                subLength = (j - i + 1); // 取子序列的长度
                result = result < subLength ? result : subLength;
                sum -= nums[i++]; // 这里体现出滑动窗口的精髓之处,不断变更i(子序列的起始位置)
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

自我实现

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int len=nums.size();
        int sum=0;
        int left=0;
        int right=len-1;
        int min_len=len+1;;
        int sub_len;
        for(int right=0;right<len;right++){
            sum+=nums[right];
            
            while(sum>=target){
                sub_len=right-left+1;
                min_len=min_len<sub_len?min_len:sub_len;
                sum-=nums[left];
                left++;      
            }
            
        }
        if(min_len==len+1){
            return 0;
        }else{
            return min_len;
        }
        
    }
};

p.s.
INT_MAX是个很大的数,如果没有被赋值,说明没有符合条件子数列。

做题心得

一开始没有思路。看了题解思路后,自己实现了代码,但少了两个细节。
1.为什么要与已有子数组长度比较?因为除了终止点不动右移起始点的情况外,最外侧循环也会更新终止点位置,可能存在移动起始点,新的子数组不包含前面的某一个较大的值后,子数组和不达要求,开始右移终止点,连续移动多位才达到要求,使新的子数组长度变长的情况。因此要有一个比较。
2.没有符合条件的子数组怎么办?自我实现时采用的是,给子数组长度初始赋一个循环中不可能出现的值,比如 num.length+1 ,如果最后验证子数组长度为这个,说明循环没改变长度值,没有子数组满足条件。

题目:59. 螺旋矩阵 II

链接: leetcode题目链接

给你一个正整数 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

实现算法:循环不变量

定义边界左闭右开

一层一层遍历

// 动态规划
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 < n - offset; j++) {
                res[startx][j] = count++;
            }
            // 模拟填充右列从上到下(左闭右开)
            for (i = startx; i < 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 += 1;
        }

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

自我实现

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> mat(n, vector<int>(n, 0));
        int layer = 0;
        int i = 0, j = 0;
        int num;
        int count = 1;
        while (layer<(n/2)) {
            num = n - 2 * layer - 1;//每一圈每条边应该有的元素数量
            while (num--) {
                mat[i][j] = count++;
                j++;
            }
            num = n - 2 * layer - 1;
            while (num--) {
                mat[i][j] = count++;
                i++;
            }
            num = n - 2 * layer - 1;
            while (num--) {
                mat[i][j] = count++;
                j--;
            }
            num = n - 2 * layer - 1;
            while (num--) {
                mat[i][j] = count++;
                i--;
            }
            i++;
            j++;//每一圈的终点应该位于该圈起始点下方,下一圈起始点的左边,即向右移一位就可以开始下一圈循环
            layer++;
        }
        if (n % 2 != 0) {
            mat[i][j] = count;
        }
        return mat;
    }
};

做题心得

太不容易了!!!!!!!!!!这是今天耗时最长的一道题!!!也请教了好多人。一开始完全没思路,看了视频以后开始自己实现,但是遇到了好多问题。
1.for循环格式就出了问题,初定义后输入应直接为空。
2.每条边的循环次数。由于四个循环是按先后顺序排列的,每条边循环完之后都将num更新为0了,不重新赋值的话后面的循环根本跑不起来。
3.关于上一圈终点和下一圈起点坐标问题。一开始一直认为一圈之后终点在起点下方。后来发现每点赋值完坐标才更新,因此实际每一圈终点和起点是重合的,因此 i, j 都要加一。
4.最后循环圈数直接为 n/2 ,若为奇数直接最后补中间位置的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值