【leetcode刷刷】977.有序数组的平方、209.长度最小的子数组、59.螺旋矩阵II、数组总结

977. 有序数组的平方

  1. 暴力法:时间复杂度O(n+nlogn),是遍历平方+排序。空间复杂度O(1),如果排序不需要额外空间的话。(python自带的sort是怎么实现的?)
  2. 双指针:时间复杂度O(n),空间复杂度O(n)。空间换时间?
class Solution(object):
    def sortedSquares(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        # 1.遍历平方,O(n),排序O(nlogn)——但这样的话就没有用到原数组的顺序
        for i in range(len(nums)):
            nums[i] *= nums[i]
        nums.sort()    # 本地修改?
        return nums

        # 2.列表推导法
        return sorted(x*x for x in nums)

        # 3. 双指针?一个在前,一个在后,向中间靠近.时间复杂度O(n),空间复杂度O(n)
        length = len(nums)
        left = 0
        right = length - 1
        res = [float('inf')] * length # 需要提前定义列表,存放结果
        for i in reversed(range(length)):
            if abs(nums[left]) < abs(nums[right]):
                res[i] = nums[right] **2
                right -= 1
            else:
                res[i] = nums[left] ** 2
                left += 1 
        return res        

209. 长度最小的子数组

  1. 暴力法,时间复杂度O(n2),python运行的话leetcode会超出时间限制。
    (1)一开始想的是外循环是子序列长度的大小,从1到L;内循环是子序列开始的数字;这样计算cur_sum的时候还得加一个长度为L,时间复杂度为O(n2L),最大的时候是O(n3)!!!后来想到用一个数组保存子序列长度为l-1的来计算子序列长度为l的,但这样相当于用O(n)的空间复杂度换了O(L)的时间复杂度,也不是很划算。
    (2)后来看题解,暴力解法的外循环是子序列开头;内循环是子序列结尾,这样直接可以用O(1)挨个计算1-L长度的子序列的sum,直接省掉了前面O(L)的时间复杂度或者说O(n)的空间复杂度,感觉自己是个傻子。。
  2. 移动窗口法:感觉也就是双指针法。外循环是子序列的结尾,也就是快指针;慢指针是子序列的开头。两个指针的中间就是子序列。当子序列的sum小于target时,子序列长度不够,因此快指针不断右移;当子序列的sum大于等于target时,表示子序列长度够了,记录下此时的min-len,再缩短子序列直到sum小于target,此时移动的是慢指针。时间复杂度为O(n),相当于快慢指针各遍历了一遍数组。
  3. 数组如果有负的,就不能使用这个方法了吧。那除了暴力还有什么方法呢???
class Solution(object):
    def minSubArrayLen(self, target, nums):
        """
        :type target: int
        :type nums: List[int]
        :rtype: int
        """
        # 滑动窗口法
        left = 0
        right = 0
        cur_sum = 0
        cur_len = 0
        min_len = float('inf')
        for i in range(len(nums)):   # 表示窗口的结束点
            cur_sum += nums[i]
            cur_len += 1
            while(cur_sum >= target):
                min_len = min(min_len, cur_len)  # cur-sum大于等于target,更新窗口大小
                cur_sum -= nums[left]   # 一旦cur-sum小于target,则退出循环了
                left += 1   # 减小窗口
                cur_len -= 1
        return 0 if min_len == float('inf') else min_len

        # 暴力法
        # python用暴力法好像不行?
        l = len(nums)
        min_len = float('inf')
        for i in range(l):
            cur_sum = 0
            for j in range(i, l):
                cur_sum += nums[j]
                if cur_sum >= target:
                    min_len = min(min_len, j-i+1)
                    break
        return 0 if min_len == float('inf') else min_len
        
        # 长度最小的连续子数组
        # 窗口从0到l-1遍历,暴力法O(n2*l)
        # 记录下来前一个计算的结果的话,也就是O(n2),没有提高很多啊?而且空间复杂度变成O(n)了
        length = len(nums)
        # O(n)
        res = [0] * length
        for l in range(1, length+1): # l表示的是窗口长度,[1,... ,length]
            # O(n)
            for i in range(length-l+1):    # [0, 1, ..., length-1-l]
                add_l = res[i] + nums[i+l-1]
                # O(l)              
                res[i] = add_l
                if add_l >= target:
                    return l
        return 0

59. 螺旋矩阵II

  1. 首先定义开闭区间,采用左闭右开的方式最符合直觉。
  2. 外循环为offset偏置,[0, mid],mid=n//2。在每一个offset下,有四个方向的循环,从左到右,从上到下,从右到左,从下到上。此时左闭右开就能完全覆盖一圈。但需要好好琢磨range的大小起点终点,以及matrix的坐标。
  3. ps:赋值的时候不能采用[[0]*n]*n,群里给出的解答:[[0]*n]*n 事实上是创建了一个包含 n 个指向同一行引用的列表。你改一个地方,所有的行都会跟着修改。(但是我不理解为什么内层的[0]*n不会被最后的赋值覆盖)
  • 群里给出的解答:大概意思是一开始的[0]*n是创建一个列表,创建数组是一个分配连续空间的过程,这个不可能是复制一个[0]这样一个int空间。
  • 但是[[0]*n]*n相当于复制上面创建的这个列表,而不是在新建列表,因此只是生成了指向已创建空间的指针。而for _ in range(n)相当于在一个循环里生成了n个列表。
  • 说实话,感觉是个python定义数组里的坑。总的来说,数组n是为了定义n个copy,数字n是为了定义一个列表。
    在这里插入图片描述
class Solution(object):
    def generateMatrix(self, n):
        """
        :type n: int
        :rtype: List[List[int]]
        """
        matrix = [[0]*n for _ in range(n)]   # [[0]*n]*n这样的定义方式好像会出问题
        start_x, start_y = 0, 0
        mid, loop = n//2, n//2
        count = 1
        # 左闭右开
        for offset in range(1, loop+1):   # 偏置??但是这个偏置和startx的关系?
            for i in range(start_y, n-offset):   # 左闭右开,n-1取不到
                matrix[start_x][i] = count
                count += 1
                print(matrix)
            for i in range(start_x, n-offset):
                matrix[i][n-offset] = count
                count += 1
                print(matrix)
            for i in range(n-offset, start_y, -1):
                matrix[n-offset][i] = count
                count += 1
            for i in range(n-offset, start_x, -1):
                matrix[i][start_y] = count
                count += 1
            start_x += 1
            start_y += 1
        
        if n%2 != 0: # n是奇数
            matrix[mid][mid] = count

        return matrix

数组总结

总的来说,数组部分题目使用到的一些trick也就:

  1. 二分查找
  2. 双指针
  3. 滑动窗口(变相双指针)
  • 5
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值