【python】python算法笔记(b站视频)

一、算法复杂度

1、时间复杂度:
用来评估算法运行效率(时间)的一个式子(单位)。
(1)一般来说,时间复杂度高的算法比复杂度低的算法慢。
(2)常见的时间复杂度(按效率排序)
O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<0(n²logn)<O(n³)
(3)快速判断算法复杂度(适用于绝大多数简单情况):

  • 确定问题规模n
  • 循环减半过程→logn
  • k层关于n的循环→n^k
  • 复杂情况:根据算法执行过程判断
    2、空间复杂度:
    用来评估算法内存占用大小的式子
    空间复杂度的表示方式与时间复杂度完全一样
  • 算法使用了几个变量: O(1)
  • 算法使用了长度为n的一维列表: O(n)
  • 算法使用了m行n列的二维列表: O(m*n)
  • “空间换时间”

二、递归

1、递归的两个特点:

  • 终止条件
  • 调用自身
    2、递归的位置不同,得到的结果会不同【程序自上而下打印】
    例:以长横条表示print;以方框表示递归;最外面的方框表示程序框架
    (1)
def fun1(x):
    if x>0:
        print(x)
        fun1(x-1)
fun1(3)       # 输出 3 2 1

在这里插入图片描述

(2)

def fun1(x):
    if x>0:
        fun1(x-1)
        print(x)
fun1(3)       # 输出 1 2 3

在这里插入图片描述

3、汉诺塔问题:
将A柱上从小到大排的所有盘子移动到C柱上,移动过程可借助B柱,但要保证所有柱子上的盘子每个时刻都是按从小到大排的
(1) n=2时:
▶1.把小圆盘从A移动到B
▶2.把大圆盘从A移动到C
▶3.把小圆盘从B移动到C
(2)n个盘子时:【将前n-1个小圆盘看作一个整体】
▶1.把n-1个小圆盘从A经过C移动到B # h(n-1)
▶2.把第n个大圆盘从A移动到C # 1
▶3.把n-1小圆盘从B经过A移动到C # h(n-1)
(3)代码实现:

def hanoi(n,a,b,c): # 参数:n个盘子,盘子从a经过b,移动到c
    if n > 0:
        hanoi(n-1,a,c,b) # 
        print("moving from %s to %s"%(a,c))
        hanoi(n-1,b,a,c)
hanoi(3,'A','B','C')

(4)结果输出:

moving from A to C
moving from A to B
moving from C to B
moving from A to C
moving from B to A
moving from B to C
moving from A to C

(5)汉诺塔移动次数的递推式:h(n) = h(n-1) +1 +h(n-1) = 2h(n-1)+1

三、查找

  • 查找:在一些数据元素中,通过一定的方法找出与给定关键字相同的数据元素的过程。
  • 列表查找(线性表查找):从列表中查找指定元素
    ▷输入:列表、待查找元素
    ▷输出:元素下标(未找到元素时一般返回None或-1)
  • 内置列表查找函数:index()

1、顺序查找(Linar Search)
顺序查找:也叫线性查找,从列表第一个元素开始,顺序进行搜索,直到找到元素或搜索到列表最后一个元素为止。
▷时间复杂度:O(n)

# 顺序查找
def linear_search(li,val):
    for i,v in enumerate(li): # i:index.  v:value
        if v == val:
            return i
    return None # 注意None的位置
    
##测试 :输出 1
li = [1,2,3,4,5]
print(linear_search(li,2)) 

2、二分查找
⚠️前提是有序序列
二分查找(Binary Searh):又叫折半查找
从有序列表的初始候选区li[0:n]开始,通过对待查找的值与候选区中间值的比较,可以使候选区减少一半。
▷时间复杂度:O(logn)

# 二分查找
def binary_search(li,val):
    left = 0 # 左指针
    right = len(li)-1 # 右指针
    while left <= right: # 候选区有值
        mid = (left+right)//2 # 取整数
        if li[mid] < val: # 待查找值在mid右侧
            left = mid + 1 # 左指针右移
        elif li[mid] > val: # 待查找值在mid左侧
            right = mid -1 # 右指针左移
        else: # li[mid] == val
            return mid
    return None # val不在li中,则返回None
    
##测试 :输出 2
li = [1,2,3,4,5]
print(binary_search(li,3))

四、列表排序

🐷【比较排序】
要求:将从大到小的排序改成从小到达的排序,或者反过来;
操作:直接将程序中所有比较的地方换成相反的符号

排序:将一组“无序”的记录序列调整为“有序”的记录序列。
▷列表排序:将无序列表变为有序列表
输入:列表
输出:有序列表
▷升序与降序
▷内置排序函数:sort()

常见排序算法
1、排序lowB三人组:
▷冒泡排序
▷选择排序
▷插入排序

2、排序NB三人组
▷快速排序
▷堆排序
▷归并排序

3、其他排序
▷希尔排序
▷技术排序
▷同排序

1、冒泡排序
冒泡排序(Bubble Sort):原地排序【不需要添加新的列表】
※列表每两个相邻的数,如果前面比后面大,则交换这两个数。(从小到达排序:大的数往后排)
※一趟排序完成后,则无序区(靠前)减少一个数,有序区(靠后)增加一个数。
代码关键点:趟、无序区范围
▷时间复杂度:O(n^2)

import random
# 冒牌排序
def bubble_sort(li):
    n = len(li)
    for i in range(n-1): #共n-1趟,因为最后一趟只剩一个数,不用排序
        for j in range(n-i-1):# 第i趟中,指针可移动的最大位置(无序区的范围)
           	if li[j] > li[j+1]: # 从小到大排序
          # if li[j] < li[j + 1]: # 从大到小排序
               li[j],li[j+1] = li[j+1],li[j] # 交换li[j],li[j+1]
  	# return li 
    #参数是列表,列表是可变对象,如果传进函数中作了修改,默认自身修改,就像指针一样,所以不用return

##测试 :输出 排序前:[21, 99, 34, 96, 14]
		    #排序后:[14, 21, 34, 96, 99]
li = [random.randint(0,100) for i in range(5)]
print("排序前:%s" % li)
bubble_sort(li)
print("排序后:%s" % li)

冒泡排序的改进
因为在排序趟数未达到n-1时,就有可能已经排好序,所以可以在每一趟添加一个标志位,一旦排好序后,就不用继续排序,可以减少排序趟数。

# 冒牌排序的改进
def bubble_sort(li):
    n = len(li)
    for i in range(n-1): # 共n-1趟,因为最后一趟只剩一个数,不用排序
        exchange = False # 设立标志
        for j in range(n-i-1): # 第i趟中,指针可移动的最大位置(无序区的范围)
            if li[j] > li[j+1]:
                li[j],li[j+1] = li[j+1],li[j] # 交换li[j],li[j+1]
                exchange = True # 发生交换就置为True
        print("排序中:%s" % li)
        if not exchange: # 每一趟排序后都判断一次,如果没有发生交换,证明已经排好序了
            return 

2、选择排序
选择排序(Select Sort):【记录每一趟排序中最小的数,放到每一趟的第一个位置】
▶一趟排序记录最小的数,放到第一个位置(最小值)
▶再一趟排序记录列表无序区最小的数,放到第二个位置(次小值)
▶……
▶算法关键点:有序区和无序区、无序区最小数的位置
▶时间复杂度:O(n^2)

# 选择排序
def select_sort(li):
    n = len(li)
    for i in range(n-1): # 共n-1趟,因为最后一趟只剩一个数,不用排序
        min_loc = i # 每一趟都将无序区的第一个数先当作最小值
        for j in range(i+1,n): # 无序区的范围
            if li[j] < li[min_loc]:
                min_loc = j  # 确定最小值的位置
        li[i],li[min_loc] = li[min_loc],li[i] # 交换最小值与无序区的第一个数

li = [9, 8, 7, 1, 2, 3, 4, 5, 6]
select_sort(li)
print(li)

选择排序的改进
在每一趟中,确定了最小值的位置后,将最小值的位置与该趟无序区第一个数的位置 i 比较,如果相等,则不用交换,可以减少交换次数。

# 选择排序的改进
def select_sort(li):
    n = len(li)
    for i in range(n-1): # 共n-1趟,因为最后一趟只剩一个数,不用排序
        min_loc = i # 每一趟都将无序区的第一个数先当作最小值
        for j in range(i+1,n): # 无序区的范围
            if li[j] < li[min_loc]:# 不等要交换;相等不用交换
                min_loc = j  # 确定最小值的位置
        if min_loc != i: 
            li[i],li[min_loc] = li[min_loc],li[i] # 交换最小值与无序区的第一个数

3、插入排序
▶初始时手里(有序区)只有一张牌
▶每次(从无序区)摸一张牌,插入到手里已有牌的正确位置
▶时间复杂度:O(n^2)
在这里插入图片描述

# 插入排序
def insert_sort(li):
    n = len(li)
    for i in range(1,n): # # 共n-1趟,因为初始时手里(有序区)已有一张牌,所以从1开始摸牌(无序区的范围)
        tmp = li[i] # 因为每趟手里的牌先要后移一位(为插入牌tmp腾位置),会覆盖摸出那张牌tmp的位置,所以先暂存摸出牌tmp的值
        j = i -1 # j指针是最靠右的手里牌的下标
        while j >=0 and li[j] > tmp: # 当手里牌的值>摸出牌的值时,但摸出牌的值不能超出手里牌的左边界0:
            li[j+1] = li[j] # 先将手里牌li[j]后移一位,到j+1位置上
            j -= 1 # 摸出牌的值与手里牌的值逐个比较(j指针左移)
        li[j+1] = tmp # 因为循环中j左移了一位,所以tmp放在j+1的位置

## 测试:输出[ 3, 4, 5, 6, 7]
li = [4, 5,7,6,3]
insert_sort(li)
print(li)

4、快速排序:
快速排序思路:
▶取一个元素p,(第一个元素),使元素p归位;
▶列表被p分成两部分,左边都比p小,右边都比p大;
递归完成排序。
▶算法关键点: 双指针;只要 left== right ,循环就结束。
▶时间复杂度:O(nlogn)
🐷n个元素可分成logn层(类似二叉树划分),每层patition函数的时间复杂度是O(n)
▶最坏情况:列表逆序时
🐷解决方案:随机取第一个待归位元素
在这里插入图片描述

# 4、快速排序
## 框架
def quick_sort(li,left,right):
    if left < right:  # 至少两个元素,若只有0个或1个元素,则不用递归
        mid = partition(li,left,right)  # 第一个元素归位,将列表分成两部分
        quick_sort(li,left,mid-1)  # 左边递归
        quick_sort(li,mid+1,right)  # 右边递归
        
## 每一趟归位一个元素的过程(指针一右一左移动)
def partition(li,left,right): 
    tmp = li[left]  # 从左边开始取出待归位元素tmp
    while left < right:  # 只要 left== right ,循环就结束
        while left < right and li[right] >= tmp:  # 从右侧找比tmp小的值 (因为循环中right值在减小,一旦 left== right ,循环就结束。)
            right -= 1  # 往左找
        li[left] = li[right] # 把在右侧找到的值写到左边的空位上
        # print(li,"right")
        while left < right and li[left] <= tmp:  # 从左侧找比tmp大的值 (因为循环中left值在增大,一旦 left== right ,循环就结束。)
            left += 1  # 往右找
        li[right] = li[left] # 把在左侧找到的值写到右边的空位上
        # print(li, "left")
    li[left] = tmp  # 当left==right 时,tmp归位
    return left  # 返回tmp元素归位的位置

## 测试 输出[1, 2, 3, 4, 5, 6, 7, 8, 9] 排序完成
li = [5,7,4,6,3,1,2,9,8]
quick_sort(li,0,len(li)-1)
print(li,"排序完成")

树与二叉树


▶树是一种数据结构 比如:目录结构
▶树是一种可以递归定义的数据结构
▶树是由n个节点组成的集合
▶如果n=0,那这是一棵空树;
▶如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
▶一些概念
根节点、叶子节点
树的深度(高度):节点的层数
节点的度:该节点的分叉数
树的度:整棵树中最大节点的度(这棵树最多分几个叉)
孩子节点/父节点:
子树:类似于树杈

二叉树
▶二叉树:度不超过2的树
▶每个节点最多有两个孩子节点
▶两个孩子节点被区分为左孩子节点和右孩子节点
▶满二叉树
▶完全二叉树:拿掉满二叉树的最后几个节点
在这里插入图片描述
(a)是满二叉树 (b) 是完全二叉树
▶二叉树的存储方式(表示方式)

  • 链式存储方式
  • 顺序存储方式(用列表存储)

在顺序存储方式(用列表存储)下:
▶父节点下标为i: 则左孩子节点下标为2i+1;右孩子节点下标为2i+2
▶孩子节点下标为i: 则父节点下标为(i-1) // 2。

堆:一种特殊的完全二叉树结构(还要看节点数值)
▶大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
▶小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小

堆的向下调整性
▶假设根节点的左右子树都是堆,但根节点不满足堆的性质
▶可以通过一次向下的调整来将其变成一个堆。

5、堆排序
思想:挨个出数
过程:
▶1.建立堆。(从最后一个非叶子节点开始调整)
▶2.得到堆顶元素,为最大元素
▶3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序。
▶4.堆顶元素为第二大元素。
▶5.重复步骤3,直到堆变空。
▶时间复杂度:O(nlogn)
🐷sift函数的时间复杂度:O(logn)
⚠️堆排序没有用到递归

#  第3步:向下调整
def sift(li,low,high):
    """

    :param li: l列表
    :param low: 堆顶位置
    :param high: 堆最后一个元素的位置
    :return:
    """
    tmp = li[low]  # 取出堆顶位置
    i = low  # i指向堆的根节点
    j = 2 * i + 1  # j先指向左孩子,移动过程中指向 max{左孩子,右孩子} 的位置
    while j <= high:  # j位置有数
        if j+1 <= high and li[j+1] > li[j]:  # 如果有右孩子并且右孩子>左孩子
            j = j + 1  # j指向右孩子
        if li[j] > tmp:  # 此时j指向 max{左孩子,右孩子} 的位置
            li[i] = li[j]  # li[j]放到 i 这个空位上
            i = j  # 此时j位置为空,所以往下看一层,将i指向j
            j = 2 * i + 1 # j更新
        else:  # li[j] < tmp
            li[i] = tmp  # 把tmp放到 i 位置上
            break
    li[i] = tmp   # 此时i指向空位,j>high,越界,跳出循环,将tmp值放到空位上(叶子节点)

def heap_sort(li):
    ## 第1步:建堆(从最后一个非叶子节点构成的小堆开始调整)
    ## 最后一个叶子节点的下标是n-1 ,因为最后一个非叶子节点是其父节点,所以最后一个非叶子节点的下标是(n-2)//2
    n = len(li)
    for i in range((n-2)//2,-1,-1): # i表示建堆过程中,构成的小堆的堆顶下标 逆序(range不包括-1)
        sift(li,i,n-1)  # 由于小堆在不断变化,它的high很难确定,可以用整个大堆的high来保证不会越界
        ## 建堆完成

    for j in range(n-1,-1,-1):  # j先指向整个堆的最后一个元素
        li[0],li[j] = li[j],li[0] # 交换整个堆的堆顶和堆的最后一个元素(原本应该是去掉堆顶,将堆最后一个元素放到堆顶,现在是为了节省存储空间)
        sift(li,0,j-1)  # j-1是要调整的堆(不包括原本要去掉的堆顶值)的high

## 测试:输出[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
li = [9,8,7,6,5,0,1,2,4,3]
heap_sort((li))
print(li)

堆排序:top k问题
问题描述:现在有n个数,设计算法得到前k大的数。
解决思路:
▶1.排序后切片: O(nlogn)
▶2.排序lowB三人组: O(kn)
▶3.堆排序: O(nlogk) 【n很大时最快】

🐷sift函数不动,将heap_sort函数改写成topk函数
算法流程:
▶取列表前k个元素建立一个小根堆。堆顶就是目前第k大的数。
▶依次向后遍历原列表,对于列表中的元素,如果小于堆顶,则忽略该元素;
如果大于堆顶,则将堆顶更换为该元素,并且对堆进行一次调整;
▶遍历列表所有元素后,倒序弹出堆顶。

6、归并排序
归并
假设现在的列表分两段有序,如何让其合成为一个有序列表,这种操作称为一次归并。

归并流程:
▶分解:将列表越分越小,直至分成一个元素。
▶终止条件:一个元素是有序的。
▶合并:将两个有序列表归并,列表越来越大。
▶时间复杂度:O(nlogn)
🐷每次合并merge函数的时间复杂度:O(n),总共有logn层(分解)。
▶空间复杂度:O(n) 【开辟了ltmp变量空间进行存储】

# 合并
def merge(li,left,mid,right): # 切片li[left,right+1]
    i = left # i:指向左有序列表的第一个数
    j = mid + 1 # j:指向右有序列表的第一个数
    ltmp = [] # 存放已排序部分
    while i <= mid and j <= right:  # 左、右两个有序列表都有数
        if li[i] < li[j]:
            ltmp.append(li[i])
            i += 1 # 指针向后一位
        else:  # li[i] >= li[j]
            ltmp.append(li[j])
            j += 1  # 指针向后一位
    ## while执行完,肯定有一侧列表没数了,把
    while i <= mid: # 左有序列表还有数
        ltmp.append(li[i])
        i += 1
    while j <= right: # 右有序列表还有数
        ltmp.append(li[j])
        j += 1
    li[left:right+1] = ltmp # 放回到从li中切出来的位置
# 测试
# li = [2,4,5,7,1,3,6,8]
# merge(li,0,3,7)
# print(li)

def merge_sort(li,left,right):
    if left < right: # 至少有两个元素,递归
        mid = (left + right)//2  ## mid:划分点
        merge_sort(li,left,mid) # 递归左边
        merge_sort(li,mid+1,right) # 递归右边
        merge(li,left,mid,right) # 合并

## 测试 输出[1, 2, 3, 4, 5, 6, 7, 8, 9]
li = [2,5,7,8,9,1,3,4,6]
merge_sort(li,0,8)
print(li)

NB三人组小结
▶三种排序算法的时间复杂度都是O(nlogn)
▶一般情况下,就运行时间而言:
快速排序<归并排序<堆排序
▶三种排序算法的缺点:

  • 快速排序:极端情况下排序效率低
  • 归并排序:需要额外的内存开销
  • 堆排序:在快的排序算法中相对较慢
    在这里插入图片描述
    🎍1、冒泡排序的最好情况就是已经排好序。
    🎍2、快速排序的最坏情况是完全逆序。
    🎍3、快速排序的空间复杂度是递归所占用的空间,每层递归占用O(1),平均情况下可划分成logn层;最坏情况下可划分成n层。
    🎍4、归并排序开辟了ltmp变量空间,为O(n)。
    🎍5、空间复杂度为O(1)的说明是原地排序。
    🎍6、稳定性:当遇到相同元素时,能保证这两个数的相对位置不变。
    辨别方式:交换时,挨个交换的就是稳定排序;跳跃着交换的就是不稳定排序。

7、希尔排序
▶希尔排序(Shell Sort)是一种分组插入排序算法
▶首先取一个整数d1=n/2,将元素分为d1个组,每组相邻量元素之间距离为d1在各组内进行直接插入排序;
▶取第二个整数d2=d1/2,重复上述分组排序过程,直到di=1,即所有元素在同一组内进行直接插入排序。
▶希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趋排序使得所有数据有序。
▶希尔排序的时间复杂度讨论比较复杂,并且和选取的gap序列(d1,d2,……,di)有关。

方法一
将插入排序insert_sort函数增加一个参数gap,并且将代码中的1全部改为gap,然后再写一个函数,定义gap序列的取值,并调用insert_sort函数。

def insert_sort_gap(li,gap):
    for i in range(gap,len(li)):
        tmp = li[i]
        j = i-gap
        while j>= 0 and li[j] > tmp:
            li[j + gap] = li[j]
            j -= gap
        li[j+gap] = tmp

def insert_shell_sort(li):
    d = len(li)//2
    while d >= 1:
        insert_sort_gap(li,d)
        d //= 2

方法二
完整的希尔排序

def shell_sort(li):
    gap = len(li)//2
    while gap >= 1:
        for i in range(gap, len(li)):
            tmp = li[i]
            j = i - gap
            while j >= 0 and li[j] > tmp:
                li[j + gap] = li[j]
                j -= gap
            li[j + gap] = tmp
        gap //= 2

8、计数排序
对列表进行排序,已知列表中的数范围都在0到100之间。设计时间复杂度为O(n)的算法。

def count_sort(li,count_max):
    """

    :param li:
    :param count_max:  数的最大范围
    :return:
    """
    count = [0 for _ in range(count_max+1)] # count列表用来存放每个值出现的次数,从1开始对应,所以count_max+1个0
    for val in li:
        count[val] += 1
    li.clear()  #  清空li,用来存放要输出的列表
    for ind,val in enumerate(count):
        for i in range(val):
            li.append(ind)  # count列表中有val个ind值

# 测试 输出[1, 1, 1, 2, 2, 3, 3, 3, 4, 5]
li = [1,3,2,4,1,2,3,1,3,5]
count_sort(li,10)
print(li)

在这里插入图片描述
🐷弊端:要先知道数的范围,并且数越大,越占用空间。

9、桶排序
桶排序(Bucket Sort)是对计数排序的改进.
▶当元素的范围非常大时,首先将元素分在不同的桶中,再对每个桶中的元素排序。
▶桶排序的时间复杂度取决于数据的分布,也就是需要对不同数据排序时采取不同的分桶策略。

def bucket_sort(li,n,max_num):
    """

    :param li:
    :param n: 桶的数量
    :param max_num: 元素的最大范围
    :return:
    """
    buckets = [[] for _ in range(n)]  #创建桶
    for val in li:
        i = min(val//(max_num//n),n-1) # i: 表示val要放的桶位置,防止最后一个数max_num越界,把它放到最后一个桶中
        buckets[i].append(val)  # 把val放到桶里面
        ## 保持桶内有序
        for j in range(len(buckets[i])-1,0,-1): # 逆序比较,因为除了刚插进来的这个数,桶内其它数应该是有序的
            if buckets[i][j] < buckets[i][j-1]:
                buckets[i][j],buckets[i][j - 1] = buckets[i][j-1], buckets[i][j] # 比较交换
            else:
                break  # 否则就是已经放到了正确的位置上了
    sorted_li = []
    for buc in buckets:
        sorted_li.extend(buc) # 在sorted_li列表末尾一次性追加buc列表里的所有数(用新列表扩展原来的列表)
    return sorted_li

## 测试 输出[3, 9, 21, 25, 29, 37, 43, 49]
li = [29,25,3,49,9,37,21,43]
print(bucket_sort(li,5,49))

在这里插入图片描述
10、基数排序
🐷多关键字排序
🎍桶有序;
🎍有几个关键字,就做几次进桶、出桶操作;
🎍每完成一次进桶、出桶操作,对应的关键字就排好序了
🎍先比较小的关键字

例:对32,13,94,52,17,54,93排序
(1)个位最小,作为第一个关键字,进桶
在这里插入图片描述
(2)依次出桶,得到32,52,13,93,94,54,17【个位数已排好序】
(3)十位数作为关键字,进桶
在这里插入图片描述
(4)依次出桶,得到13,17,32,52,54,93,94【十位数也已排好序】

复杂度分析:
时间复杂度:O(kn)
空间复杂度:O(k+n)
k表示数字位数

def radix_sort(li,base): # base为桶的个数
    ## 根据最大值确定关键字位数
    max_num = max(li)
    it = 0 # it 为关键字位数,初始为0
    while it ** base <= max_num: # 当base=10时,9->1; 88->2;1000->4
        ## 创建桶
        buckets = [[] for _ in range(base)]
        for val in li:
            digit = (val // (base ** it)) % base  # 取出当前关键字位数上的数字
            # 如:it = 1, 987 //(10**1)->98, 98 %10->8
            buckets[digit].append(val) # 放到对应的桶里
        # 分桶完成
        ## 把数重新写回li
        li.clear()
        for buc in buckets:
            li.extend(buc) # 在li列表末尾一次性追加buc列表里的所有数(用新列表扩展原来的列表)
        it += 1 # 有几个关键字,入桶、出桶就操作几次

## 测试 输出[13, 17, 32, 52, 54, 93, 94]
li = [32,13,94,52,17,54,93]
radix_sort(li,10)
print(li)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值