【Leetcode】列表 list

文章详细介绍了Python中列表的基本操作,如append、extend、insert等,以及如何通过杨辉三角、数组旋转、数组乘积计算、生命游戏规则和旋转函数求解等问题。还讨论了如何优化算法以降低空间复杂度和时间复杂度。
摘要由CSDN通过智能技术生成
  • 题目汇总

能完成:48/54/59/118/274/283/303/414/442/485/495/598/697/2656/2744

要优化:119/189/238/289

不会做:396/453

  • 基础操作总结

插入元素:

1.list.append()           #在列表末尾添加一个元素
2.list.extend()           #在列表末尾添加至少一个元素
3.list.insert(idx,val)    #在列表任意位置添加一个元素
4.切片                    #通过替换的方式在列表任意位置添加至少一个元素

删除元素:

 1.del list[i]    del list[i:j]
 2.list.pop(idx)                           #无指定位置时默认删除最后一个元素
 3.list.remove(val)                        #删除和指定元素值相同的第一个元素
 4.list.clear()                            #删除列表所有元素

复制列表:

1.直接赋值:list2 = list1              #浅拷贝
2.切片赋值:list2 = list1[:]           #只对第一层实现深拷贝
3.list2 = list1.copy()                #只对第一层实现深拷贝
  list2 = copy.deepcopy(list1)        #完全深拷贝  需要import copy

#浅拷贝:list1和list2指向同一地址,同步改变 
#深拷贝:list1和list2指向不同地址,互不影响

获取指定值的索引:list.index(val)

列表翻转:

1.list.reverse()
2.list = list[::-1]

转置实现:L = list(map(list, zip(*L)))

#以[[1, 2, 3],[4, 5, 6]]为例:

# 1.拆解元素: 
     list = *L = [1, 2, 3] [4, 5, 6]
# 2.打包成元组: 
     zip(*L) = [(1,4),(2,5),(3,6)]
# 3.对指定序列的每个元素实施指定操作:
     map(list, zip(*L))) = [[1,4],[2,5],[3,6]   

对列表的每个元素值进行相同操作:

#每个元素+1
1.for i in range(len(list)): list[i] = list[i] + 1
2.for i,val in enumerate(list): val = val + 1
3.map(lambda x:x+1, list)
4.[val + 1 for val in list]

实现两个列表的对应值相加:

1.[x + y for x, y in zip(list1, list2)]
2.for i in range(len(list1)): list1[i] = list1[i] + list2[i]
3.map(lambda x, y: x + y, list1, list2)
#扩展lambda
from functools import reduce
reduce(lambda x, y: x + y, list)   #列表求和
#reduce元素累积
sorted(list, key=lambda list: list[1], reverse=True)   #根据列表元素的第二个元素进行降序排列
#sorted排序,key排序值,reverse是否降序排列
filter(lambda x: x % 3 == 0, list)  #取出列表中3的倍数的元素并组成新列表
#filter过滤

补充:list操作对应的时间复杂度

list-列表常用操作和时间复杂度分析_void alistremovevalue(alist *list, const lelement -CSDN博客

  • 基础数学知识

杨辉三角规律

①每个数等于它上方两数之和

②每一行的数据为组合数关系 Cnm = n!/m!(n-m)!

    可推导出单行递推公式:Cnm=Cnm-1 * (n-m+1) / m

  • 优化思路
119

给定一个非负索引 rowIndex,返回「杨辉三角」的第 rowIndex 行。

思路一:在规律①的基础上输出整个杨辉三角,取出指定的第 rowIndex 行;

        优化点 :对 第i+1行 的计算仅用到 第i行的数据,可以使用 !!滚动数组!! ,只保留上一行数据来优化空间复杂度。

                       对 第i行第j个 的计算仅用到 第i-1行第j-1和j个 的数据,可以使用 !!倒叙计算!! 因为需要保证 list[j-1] 未被更新。

class Solution(object):
    def getRow(self, rowIndex):
        """
        :type rowIndex: int
        :rtype: List[int]
        """
        res = [1] * (rowIndex + 1)
        for i in range(2, rowIndex + 1):
            for j in range(i - 1, 0, -1):
                res[j] += res[j - 1]
        return res

思路二:在规律②的基础上推导出单行递推公式,直接输出第 rowIndex 行;

class Solution(object):
    def getRow(self, rowIndex):
        res = [1]
        for i in range(1, rowIndex + 1):
            res.append(res[i - 1] * (rowIndex - i + 1) / i)
        return res
189

给定一个整数数组 nums,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

分析:轮转一共只有len(nums)种可能,先将k更新为对m取余的结果

思路一:通过切片再拼接得到输出结果

思路二:通过 for循环下的pop和insert得到输出结果

思路三(优化点):通过 !!三次翻转!! 得到输出结果

#以nums = [1,2,3,4,5,6,7], k = 3为例
#一次翻转:nums = [7,6,5,4,3,2,1]
#二次翻转:nums = [5,6,7,4,3,2,1]
#三次翻转:nums = [5,6,7,1,2,3,4]

# 1.对整个数组实行翻转,保证后面后面元素移到列表前端
# 2.从k处分隔数组,左右两数组各自进行翻转,保证子数组内部的元素顺序

class Solution:
    def rotate(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: None Do not return anything, modify nums in-place instead.
        """

        k %= len(nums)
        nums[:] = nums[::-1]
        nums[:k] = nums[:k][::-1]
        nums[k:] = nums[k:][::-1]
238

给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。不能使用除法!!!

思路一:双层for循环,将跳过 nums[i] 的乘积值放在 answer[i]

思路二:一次遍历储存前后缀,前缀由前往后更新,后缀由后往前更新

        优化点:一次遍历储存前缀,一次遍历用变量代替后缀,节省后缀的内存开销

class Solution(object):
    def productExceptSelf(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        
        out = [1]
        right = 1
        length = len(nums)

        for i in range(1, length):
            out.append(out[i-1]*nums[i-1])

        for i in range(length-1,-1,-1):
            out[i] *= right
            right *= nums[i] 
        
        return out
289

给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细胞。每个细胞都具有一个初始状态: 1 即为 活细胞 (live),或 0 即为 死细胞 (dead)。每个细胞与其八个相邻位置(水平,垂直,对角线)的细胞都遵循以下四条生存定律:

  1. 如果活细胞周围八个位置的活细胞数少于两个,则该位置活细胞死亡;
  2. 如果活细胞周围八个位置有两个或三个活细胞,则该位置活细胞仍然存活;
  3. 如果活细胞周围八个位置有超过三个活细胞,则该位置活细胞死亡;
  4. 如果死细胞周围正好有三个活细胞,则该位置死细胞复活;

下一个状态是通过将上述规则同时应用于当前状态下的每个细胞所形成的,其中细胞的出生和死亡是同时发生的。给你 m x n 网格面板 board 的当前状态,返回下一个状态。

分析

m=1/n=1时,一头一尾格子内细胞全部死亡,剩余格子内活细胞要求周围两个格子均为活细胞才能保持状态,死细胞保持状态;

m>1/n>1时,四顶角格子k<=3(k=3:0-1/1-1;k=2:0-0/1-1;k<2:0-0/1-0)

                     四边格子除顶角k<=5 (k=3:0-1/1-1;k=2:0-0/1-1;k<2 or k>3:0-0/1-0)

                     剩余格子k<=8(k=3:0-1/1-1;k=2:0-0/1-1;k<2 or k>3:0-0/1-0)

由于出生死亡同时发生,则更新细胞状态中需要维持当前状态,故用额外空间保存更新状态

        优化点: !!利用二进制定义复合状态!!(状态保持00=0,11=3,状态改变01=1 死亡,10=2 复活)

                       当前状态在低位下一状态在高位   是为了能统一在统计活细胞数量时改变的复合状态和初始状态的操作,比如邻域出现01(先活后死)和1(初始状态)时只要取低位就能获得细胞的当前状态;

思路:两层for循环遍历每个格子,统计每个格子周围的活细胞数量(&1  取低位),根据活细胞数量定义新状态(k<2ork>3 0-00/1-01,k=2 0-00/1-11,k=3 0-10/1-11);再次两层for循环遍历每个格子,将复合状态右移一位即得到下一个状态;

class Solution:
    def gameOfLife(self, board)
        """
        Do not return anything, modify board in-place instead.
        """
        if not board or not board[0]:
            return
        
        m, n = len(board), len(board[0])
        for i in range(m):
            for j in range(n):
                cnt = self.count_alive(board, i, j)
                if cnt == 2 and board[i][j]:
                    board[i][j] = 3
                if cnt == 3:
                    board[i][j] = 3 if board[i][j] else 2
        
        for i in range(m):
            for j in range(n):
                board[i][j] >>= 1
    
    def count_alive(self, board, i, j):              #定义相对位置
        m, n = len(board), len(board[0])
        cnt = 0
        directions = [(0, 1), (0, -1), (-1, 0), (1, 0),
                      (1, 1), (1, -1), (-1, 1), (-1, -1)]
        
        for dx, dy in directions:
            x, y = i + dx, j + dy
            if x < 0 or y < 0 or x == m or y == n:   #越界值不统计
                continue
            cnt += board[x][y] & 1
        
        return cnt
  • 解题思路
396

给定一个长度为 n 的整数数组 nums 。假设 arrk 是数组 nums 顺时针旋转 k 个位置后的数组,我们定义 nums 的旋转函数为:F(k) = 0*arrk[0]+ 1*arrk[1]+...+(n-1)*arrk[n -1]

返回 F(0), F(1), ..., F(n-1)中的最大值 

分析:以 nums = [4,3,2,6]  为例 

         F(0) = (0 * 4) + (1 * 3) + (2 * 2) + (3 * 6) = 0 + 3 + 4 + 18 = 25

         F(1) = (1 * 4) + (2 * 3) + (3 * 2) + (0 * 6) = 0 + 4 + 6 + 6 = 16

         F(2) = (2 * 4) + (3 * 3) + (0 * 2) + (1 * 6) = 0 + 6 + 8 + 9 = 23

         F(3) = (3 * 4) + (0 * 3) + (1 * 2) + (2 * 6) = 0 + 2 + 12 + 12 = 26 

         则F(1)-F(0) = 4+3+2-3*6,F(2)-F(1) = 4+3-3*2+6,F(3)-F(2) = 4-3*3+2+6

         通过错位相减发现F(m)-F(m-1) = 4+3+2+6-4*nums[-m] = sum(nums)-len(nums)*nums[-m]

思路:一次遍历计算F(0),然后通过递推公式依次计算F(1)...F(n-1),只需要用到两个变量(一个保存当前F值,一个保存历史最大值)

class Solution(object):
    def maxRotateFunction(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        length = len(nums)
        summ = sum(nums)

        F = 0
        for i in range(length):
            F += i * nums[i]
        
        maxx = F 
        for i in range(1, length):
            F = F + summ - length * nums[-i]
            maxx = max(maxx, F) 
        return maxx
453

给你一个长度为 n 的整数数组,每次操作将会使 n - 1 个元素增加 1 。返回让数组所有元素相等的最小操作次数。

分析 !! n - 1 个元素增加 1 等价于 1个元素减少 1 !! 将问题转化成数组中每个数减少到最小值的操作数

class Solution(object):
    def minMoves(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """

        time = 0
        for val in nums:
            time += val - min(nums)

        return time

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值