【LeetCode刷题】Day02 数组基础Ⅱ

完成977有序数组的平方,209长度最小的子数组,59螺旋矩阵。拓展题目二刷做。

LeetCode 977-有序数组的平方

题目描述: 给你一个按非递减顺序排序的整数数组 nums,返回每个数字的平方组成的新数组,要求也按非递减顺序排序
输入: nums = [-4,-1,0,3,10]
输出: [0,1,9,16,100]
解释: 平方后,数组变为 [16,1,0,9,100];排序后,数组变为 [0,1,9,16,100]
题目链接: https://leetcode.cn/problems/squares-of-a-sorted-array/

解题思路

  • 暴力解法 O( n + n l o g n n+nlogn n+nlogn)
  • 双指针解法 O( n n n)

1 暴力解法

  • 先平方,再快排
  • 用了python列表内置的排序方法
**暴力解法python实现**
class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        new_nums = [];
        for i in range(len(nums)):
            temp = nums[i] * nums[i];
            new_nums.append(temp);
        new_nums.sort();
        return new_nums;

2 双指针法

  • 分析题目可知,数组平方的最大值就在数组的两端,不是最左边就是最右边,不可能是中间
  • 考虑相向而行的双指针解法
  • 这里注意python列表需要初始化
**双指针解法python实现**
class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        new_nums = [0]*len(nums);
        k = len(nums) - 1; # 控制新数组从后往前填入元素
        p = 0; # 左指针
        q = k; # 右指针
        for i in range(k, -1, -1):
            if p == q: # 临界条件:p == q 时代表旧数组已经遍历完毕,可以退出循环 
                new_nums[i] = nums[p]*nums[p];
                break;
            if nums[p]*nums[p] >= nums[q]*nums[q]:
                new_nums[i] = nums[p]*nums[p];
                p += 1;
            else:
                new_nums[i] = nums[q]*nums[q];
                q -= 1;
        return new_nums;

3 心得体会

看到本题的第一想法就是暴力求解,没有考虑双指针。感觉是看了题目就觉得可以暴力求解,就没有思考这个题目给出的数据的特殊性,从而没有考虑到更快的方法。希望以后能看到题目后先想想会不会有更好的方法再去写代码,而不是一味的图做完题就可以。
昨天今天的题目均用到双指针解法,昨天的移除元素是同方向的双指针,今天是相向的双指针,双指针法在数组中很常见,以后应该多考虑这种方法

LeetCode 209-长度最小的子数组

题目描述: 给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [ n u m s , n u m s l + 1 , . . . , n u m s r − 1 , n u m s r ] [nums,nums_{l+1},...,nums_{r−1},nums_{r}] [nums,numsl+1,...,numsr1,numsr],并返回其长度。如果不存在符合条件的子数组,返回 0
输入: target = 7, nums = [2,3,1,2,4,3]
输出: 2
解释: 子数组 [4,3] 是该条件下的长度最小的子数组
题目链接: https://leetcode.cn/problems/minimum-size-subarray-sum/

解题思路

  • 暴力解法 O( n 2 n^2 n2)
  • 滑动窗口解法 O( n n n)

1 暴力解法

  • 这里给出两种代码,一种是随想录里面的,一种是自己的
  • 随想录思路:利用两个for循环,一个遍历起始位置,一个遍历终止位置。即给每个起始位置都找一个最小的符合sum>=s的子数组,然后比较这些子数组的长度哪个最小,返回最小长度
  • 自己思路:利用两个for循环,外层循环控制窗口大小,内层循环遍历以该窗口为大小的所有子数组。即先控制了子数组大小,由于子数组从小到大,所以一旦出现一个sum>=s的子数组,则返回该窗口长度即可
  • 暴力解法LeetCode会超时TwT
**暴力解法c++实现**
class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};
**暴力解法python实现**
class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        sub_nums = [];
        for i in range(len(nums)): # 控制子数组长度
            for j in range(len(nums)): # 遍历该长度下的所有子数组
                sub_nums = nums[j:j+i+1];
                if sum(sub_nums) >= target:
                    return i+1;
        return 0;

2 滑动窗口解法

  • 滑动窗口,就是 不断的调节子序列的起始位置和终止位置,从而得出我们要想的结果
  • 滑动窗口的精妙之处在于 根据当前子序列和大小的情况,不断调节子序列的起始位置。
  • 只用一个for循环,那么这个for循环索引应用滑动窗口的终止位置。如果用起始位置,那么只能暴力求解。
  • 起始位置应在每次窗口内元素大于target值时移动
**滑动窗口法python实现**
class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        i = 0;
        sum = 0; # 窗口内元素之和
        result = sys.maxint; # 定义最大值,以便于更新result
        for j in range(len(nums)): # j为后指针,可以减少复杂度
            sum += nums[j];
            while(sum >= target): 
            # 此处用循环因为要确定以后指针为终点的最小窗口值,i可能要向后移动几个位置
                sub_length = j-i+1; # 窗口内元素个数
                if sub_length < result:
                    result = sub_length;
                sum -= nums[i]; # 前指针滑动,窗口内元素和需要减去前指针指向的值
                i += 1; # 前指针滑动以确定以后指针为终点的最小窗口值
        if result == sys.maxint:
            return 0;
        else:
            return result;

理解
暴力解法是找出所有以每一个位置为起始符合条件的最小子数组,再比较这些子数组的长度,找出最小长度返回
滑动窗口法是找出以窗口终止位置为终点符合条件的最小子数组,再比较这些子数组的长度,找出最小长度返回
暴力解法两层循环,显然复杂度为O( n 2 n^2 n2)
滑动窗口法可以分析,外层循环 j j j n n n,内循环 i i i最多运行 j − i j-i ji次,所以复杂度为O( ( j − i ) ∗ n (j-i)*n (ji)n),故为O( n n n)
思考一下两种算法的过程,可以很清晰的感觉到暴力解法很多搜索过程是没必要的,比如以第一个元素为起始的符合条件的子数组区间为【0,4】,那对于第二个元素就没有必要搜索【1,1】【1,2】【1,3】【1,4】这些位置了,而滑动窗口法就是把这些不必要再去搜索的枝剪掉了

3 心得体会

看到这道题的第一想法就是暴力求解,但是力扣会报超时,还思考了不少时间,当时想到一个找包含最大元素的最短子序列,但是今天想想这种想法应该是不对的,因为题目中有连续二字,所以最短子序列是不一定包含最大元素的,以后要好好审题,不然耽误时间
这道题运用了滑动窗口法去降低时间复杂度,第一次接触到,今后要再好好思考滑动窗口的使用条件以及怎么用

LeetCode 59-螺旋矩阵Ⅱ

题目描述: 给定一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix
输入: n = 3
输出: [[1,2,3],[8,9,4],[7,6,5]]
题目链接: https://leetcode.cn/problems/spiral-matrix-ii/

解题思路

  • 不涉及算法,模拟过程
  • 循环不变量原则,每条边填充都采用左闭右开的处理方式

1 代码实现

  • 首先确定填入所转的圈数,即循环次数
  • 然后确定填入边界,采用左闭右开形式填入,避免对一个位置重复处理
  • 要考虑到奇数情况,需要单独填入中间元素
**python实现**
class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        matrix = [[0] * n for _ in range(n)]
        left, right, up, down = 0, n - 1, 0, n - 1
        number = 1  # 要填充的数字
        count = 0 # 转圈数

        while count < n // 2:
            # 从左到右填充上边
            for x in range(left, right):
                matrix[up][x] = number
                number += 1
            # 从上到下填充右边
            for y in range(up, down):
                matrix[y][right] = number
                number += 1
            # 从右到左填充下边
            for x in range(right, left, -1):
                matrix[down][x] = number
                number += 1
            # 从下到上填充左边
            for y in range(down, up, -1):
                matrix[y][left] = number
                number += 1
            # 缩小要填充的范围
            left += 1
            right -= 1
            up += 1
            down -= 1
            count += 1
            
        # 如果阶数为奇数,额外填充一次中心
        if n % 2:
            matrix[n // 2][n // 2] = number
        return matrix
**随想录python实现**
class Solution:
    def generateMatrix(self, n: int) -> List[List[int]]:
        nums = [[0] * n for _ in range(n)]
        startx, starty = 0, 0               # 起始点
        loop, mid = n // 2, n // 2          # 迭代次数、n为奇数时,矩阵的中心点
        count = 1                           # 计数

        for offset in range(1, loop + 1) :      # 每循环一层偏移量加1,偏移量从1开始
            for i in range(starty, n - offset) :    # 从左至右,左闭右开
                nums[startx][i] = count
                count += 1
            for i in range(startx, n - offset) :    # 从上至下
                nums[i][n - offset] = count
                count += 1
            for i in range(n - offset, starty, -1) : # 从右至左
                nums[n - offset][i] = count
                count += 1
            for i in range(n - offset, startx, -1) : # 从下至上
                nums[i][starty] = count
                count += 1                
            startx += 1         # 更新起始点
            starty += 1

        if n % 2 != 0 :			# n为奇数时,填充中心点
            nums[mid][mid] = count 
        return nums

2 心得体会

这个题没有自己认真写,模拟过程总觉得很麻烦,看了视频课,重点在于循环不变量原则。希望二刷时候不要懒惰,自己好好写一次。

总结

今天掌握了双指针法,滑动窗口法以及模拟类题目的一些需要注意的地方。重点体会了滑动窗口法,以后还得多看多思考这部分。
到这里,数组部分的练习题结束了,放假的那天记得写一个数组部分的总结。
进度还是慢了一个博客,希望明天能补回来TwT。

你可能感兴趣的:(LeetCode题目总结,leetcode,算法,职场和发展)