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

今天依旧是数组知识的训练,加油


977.有序数组的平方

题目:给你一个按 非递减顺序 排序的整数数组 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:最先想到的就是最简单的暴力思路,先把平方数组算出来,再排序,最先想到的排序是常见的冒泡排序,但是冒泡排序的时间复杂度为O(n2),运行的时候超时了,有一个用例又长数又大,故转身投靠STL标准库中的sort()函数,快速排序,默认就是从小到大排序,时间复杂度为O(nlogn)。

C++ 暴力版本

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        for (int i = 0; i < nums.size(); i++) {
            nums[i] *= nums[i];
        }
        sort(nums.begin(), nums.end());  // 快速排序,默认从小到大排序
        return nums;
    }
};

同样的,在python中也有可直接使用的排序函数sort()、sorted()sort()只能用于列表,sorted()用法更为广泛。

sorted()语法:sorted(iterable, cmp=None, key=None, reverse=False)

  • iterable:可迭代对象。
  • cmp:比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出,此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。
  • key:主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序。
  • reverse:排序规则,reverse = True 降序 , reverse = False 升序(默认)。

python3版本

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        return sorted([num*num for num in nums]);  # 列表[]为可迭代对象

思路2:这里依然可以使用昨天的双指针做法,(P.S. 双指针的思想实在是太妙了),当然在数组的题目中还是用下标来表示指针。

因为原本的数组也是升序的,只是有负数,所以平方后最大的值一定在两边,中间的值最小。故设两个下标i, j分别为原数组的头和尾,下标k为新数组的尾,遍历原数组比较头尾数值的平方和,若nums[i]2 <= nums[j]2 ,就将nums[j]写入新数组下标k位置,然后下标k往前移,下标j往前移,反之,将nums[i]写入新数组下标k位置,然后下标k往前移,下标i往后移,遍历结束后,最后返回新数组。代码如下:

C++双指针版本

class Solution {
public:
    vector<int> sortedSquares(vector<int>& nums) {
        vector<int> res(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]) {
                res[k--] = nums[j] * nums[j];
                j--;
            }
            else {
                res[k--] = nums[i] * nums[i];
                i++;
            }
        }
        return res;
    }
};

同理,也可以用python实现该算法,但不能使用for循环,因为无法像c++那样去掉第三个表达式,故使用while循环实现。

Python3双指针版本

class Solution:
    def sortedSquares(self, nums: List[int]) -> List[int]:
        i = 0
        j,k = len(nums) - 1, len(nums) - 1
        res = [0] * len(nums)  # 新列表
        while i <= j:
            if nums[i] ** 2 <= nums[j] ** 2:
                res[k] = nums[j] ** 2
                k -= 1
                j -= 1
            else :
                res[k] = nums[i] ** 2
                k -= 1
                i += 1
        return res

交叉使用两个语言,语法真的很容易混乱。。。


209.长度最小的子数组

题目:给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

示例:输入:s = 7, nums = [2,3,1,2,4,3] 输出:2

解释:子数组 [4,3] 是该条件下的长度最小的子数组。


这题我一开始看到,连暴力思路都没有,不知道该如何遍历出所有区间,转头就去看Carl的讲解,滑动窗口还是不熟。

思路:利用滑动窗口寻找最小的子数组。其实也类似于用了两个指针,不过这次是利用两指针中间数值的和来找最小的子数组。

定义滑动窗口起始位置i和终止位置j,在终止位置j不断前移的过程中,如果起始i到终止j的之间的数值和大于target,则先记录当前窗口的长度,然后更新最短长度,并将起始位置i前移,循环结束返回最短的长度。

C++版本

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        int i = 0;
        int res = INT_MAX;
        int sum = 0, len = 0;
        for (int j = 0; j < nums.size(); j++) {
            sum += nums[j];
            while (sum >= target) {
                len = j - i + 1;
                res = res < len ? res : len;  // 更新最短的窗口长度
                sum -= nums[i++];
            }
        }
        return res == INT_MAX ? 0 : res;  // 若长度没变,则说明没找到
    }
};

同理可写python版本。

python3版本

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        i, sum_ = 0, 0
        res = len(nums) + 1
        for j in range(len(nums)):
            sum_ += nums[j]
            while sum_ >= target :  
                res = min(j - i + 1, res)  # j-i+1当前窗口长度,更新最小的窗口长度
                sum_ -= nums[i]
                i += 1
        return res if res != len(nums) + 1 else 0 

59.螺旋矩阵II

题目:给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。

示例:输入:3, 输出:[ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]

思路:这题一看就感觉很麻烦,一层一层的循环很容易搞错,好不容易写出来的,还得不到正确答案!

一定要注意循环不变量原则,每一次循环都使用左闭右开区间,才不容易乱,切记!

将每一圈分开来写,每一圈又分为四步,从左到右,从上到小,从右到左,从下到上,每一步都使用左闭右开区间就不不会漏掉或重复,当n为奇数是,会多出最后一个数在正方形的正中间,最后补上就好。


C++版本

class Solution {
public:
    vector<vector<int>> generateMatrix(int n) {
        vector<vector<int>> nums(n, vector<int>(n, 0)); // 使用vector定义一个n*n的二维数组
        int loop = n / 2;
        int start_x = 0, start_y = 0;
        int offset = 1;
        int num = 1;
        
        while (loop) {  // 注意模拟循环的时候不要想着第一轮来写边界条件,到第二轮就出问题了!
            int i = start_x;
            int j = start_y;
            for (j = start_y; j < n - offset; j++) {  // 从左往右 左闭右开区间
                nums[i][j] = num++;
            }
            for (i = start_x; i < n - offset; i++) {  // 从上往下 左闭右开区间
                nums[i][j] = num++;
            }
            for (; j > offset - 1; j--) {  // 从右往左 此时j=i=n-offset 左闭右开区间
                nums[i][j] = num++;
            }
            for (; i > offset - 1; i--) {  // 从下往上 此时i=j=offset-1 左闭右开区间
                nums[i][j] = num++;
            }
            offset++;  // 进入下一轮循环 边界-1
            loop--;
            start_x++;  // 第二轮循环开始,起始位置都需要+1
            start_y++;
        }
        if ( n % 2 ) {
                nums[n / 2][n / 2] = num;
            }
            return nums;
    }
};

真的好容易写乱,理解了思路之后还有很多小的地方需要注意,一定要细心!
因为python的for循环机制和c++不一样,太容易出错了,一定要理解这两种循环机制。

Python3版本

class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]  # 创建一个二维数组
        start_x, start_y = 0, 0
        loop = n // 2
        offset = 1
        num = 1
        while loop :
            i,j = start_x, start_y 
            for j in range(start_y, n - offset):  # 执行完for循环j=n-offset-1,j不等于n-offset,下一个循环不能直接用j
                nums[i][j] = num
                num += 1
            for i in range(start_y, n - offset):  # 执行完for循环i=n-offset-1,i不等于n-offset,下一个循环不能直接用i
                nums[i][n - offset] = num  # 所以j应n-offset
                num += 1
            for j in range(n - offset, offset - 1, -1): # 执行完for循环j=offset-2,j不等于offset-1,下一个循环不能直接用j
                nums[n - offset][j] = num  # 所以i应n-offset
                num += 1
            for i in range(n - offset, offset - 1, -1):
                nums[i][offset - 1] = num  # 所以j应为offset-1
                num += 1
            
            loop -= 1
            start_x += 1
            start_y += 1
            offset += 1
        
        if n % 2:
            nums[n // 2][n // 2] = num
        
        return nums

我是照搬c++的思路写的python代码,还是不如Carl老师的简洁,还需继续努力!


数组总结

遇到数组的题,大概就是这几种方法:二分法,双指针,滑动窗口,模拟行为。两天搞定数组类型的题,跟着Carl老师扎扎实实刷题,收获颇丰!

第一次刷代码随想录,调两种语言的代码还是很吃力,下一次打卡还是专注于c++吧,二刷三刷时再提高难度,挑战自己!

你可能感兴趣的:(代码随想录,python,c++,leetcode,代码随想录)