Python中使用list的一些不常见的操作

random

使用list之前先学习一下random的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import random
import string

# 随机整数:
print random.randint(1,50)

# 随机选取0到100间的偶数:
print random.randrange(0, 101, 2)

# 随机浮点数:
print random.random()
print random.uniform(1, 10)

# 随机字符:
print random.choice('abcdefghijklmnopqrstuvwxyz!@#$%^&*()')

# 多个字符中生成指定数量的随机字符:
print random.sample('zyxwvutsrqponmlkjihgfedcba',5)

# 从a-zA-Z0-9生成指定数量的随机字符:
ran_str = ''.join(random.sample(string.ascii_letters + string.digits, 8))
print ran_str

# 多个字符中选取指定数量的字符组成新字符串:
print ''.join(random.sample(['z','y','x','w','v','u','t','s','r','q','p','o','n','m','l','k','j','i','h','g','f','e','d','c','b','a'], 5))

# 随机选取字符串:
print random.choice(['剪刀', '石头', '布'])

# 打乱排序
items = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
print random.shuffle(items)

针对list的一些操作

判断两个list是否有重叠

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def get_overlap_list():
"""
判断两个list是否重叠
1. 转为list
2. 判断最大元素是否属于第一个tuple,并且最小元素不在第一个tuple,返回True,否则False
:return:
"""
s1 = (2, 6)
s2 = (1, 4)
s = list(s1 + s2)
s.sort(reverse=True)
return False if s[0] == s1[1] and s[1] != s1[0] else True
get_overlap_list()

# 输出
False

def get_overlap_tuple():
"""
判断两个tuple是否重叠;
分别判断第0个元素和第1个元素是否在另外一个tuple元素范围内
:return:
"""
s2 = (6, 6)
s1 = (1, 6)

return False if s1[0] <= s2[0] <= s1[1] or s1[0] <= s2[1] <= s1[1] else True

此处判断的是list,其实换成tuple也是一样的。

list去重

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
s = ['c', 'a', 't', 'd', 'o', 'g', 'r', 'a', 'b', 'b', 'i', 't']
sqlist = list()
for i in s:
if i not in sqlist:
sqlist.append(i)
print(sqlist)

# 输出

['c', 'a', 't', 'd', 'o', 'g', 'r', 'b', 'i']


# 优化后的写法

animal = ['c', 'a', 't', 'd', 'o', 'g', 'r', 'a', 'b', 'b', 'i', 't']
newlist = list()
sqlist = [newlist.append(i) for i in animal if i not in newlist]
print(newlist)

# 输出
['c', 'a', 't', 'd', 'o', 'g', 'r', 'b', 'i']

遍历原始list,如果元素不属于新的list则插入

生成26个小写字母的list

1
2
3
4
5
6
chars = [chr(i) for i in range(97,123)]
print(chars)

# 输出

['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', ' ']

使用chr(ASSII) 来定位字母

反转

1
2
3
4
5
6
7
8
9
10
11
12
# 反转
def get_reverse_list():
num_list = [random.choice(range(random.randint(1, 100))) for _ in range(10)]
tmp_list = list()
print(num_list)
for i in range(len(num_list)):
# 从下标为0开始插入
tmp_list.insert(0, num_list[i])
return tmp_list
# 输出
[46, 4, 6, 73, 0, 14, 33, 35, 60, 10]
[10, 60, 35, 33, 14, 0, 73, 6, 4, 46]

主要逻辑是使用insert插入到下标为0的元素,相当于遍历一遍从头开始插入。

判断对称

1
2
3
4
5
def get_symmetry_list():
num_list = [random.choice(range(random.randint(1, 100))) for _ in range(9)]
return False if len(num_list) % 2 == 0 else True
# 输出
True

此处判断是否对称主要取决于元素个数是否为奇数,也就是range的次数。

取出字符串最长的单词

1
2
3
4
5
6
7
8
9
10
def get_word_list():
chars = ['hello', 'ok', 'ha', 'e', 'books']
for i in range(len(chars)):
if len(chars[0]) < len(chars[i]):
chars[0] = chars[i]
return chars[0]
get_word_list()
# 输出

hello

列表按绝对值排序

1
2
3
4
5
6
7
8
9
10
num_list = [1, -6, 2, -5, 9, 4, 20, -3]
for i in range(len(num_list)):
for j in range(len(num_list) - 1 - i):
if abs(num_list[j]) < abs(num_list[j + 1]):
num_list[j], num_list[j + 1] = num_list[j + 1], num_list[j]
return num_list

# 输出

[20, 9, -6, -5, 4, -3, 2, 1]

在冒泡的方式排序的过程使用abs()获取绝对值。

生成列表解析式 1-10之间的偶数

1
[i for i in range(10) if i % 2 == 0]

元素除以2为0则是偶数。

找出最大元素的下标

1
2
3
4
5
6
7
8
9
10
11

def new():
num_list = [1, 2, 3, 11, 2, 5, 88, 3, 2, 5, 33]
tmp_list = num_list.copy()
for i in range(len(num_list)):
for j in range(len(num_list) - 1 - i):
if num_list[j] < num_list[j + 1]:
num_list[j], num_list[j + 1] = num_list[j + 1], num_list[j]
return tmp_list.index(num_list[0])
# 输出
6

list 组合为string

1
2
3
4
5
l = [1, 2, 3, 5, 6]
print(''.join([str(i) for i in l]))

# 输出
12356

str.join(item) join 函数是一个字符串操作函数。item表示一个成员,且只能有一个。上述代码中''.join(xxx)含义是将字符串中的xxx以字符分割后再拼接为一个字符串,代码中为空,因此输出字符串中也无间隔。

其他示例:

1
2
3
4
5
6
7
8
9
10
l = [1, 2, 3, 5, 6]
print(','.join([str(i) for i in l]))

# 输出
1,2,3,5,6

# 错误示例
','.join('a','b')

TypeError: str.join() takes exactly one argument (2 given)

合并两个list为dict

1
2
3
4
5
6
7
8
9
def list2dict():
a = ["a", "b", "c"]
b = [1, 2, 3]
s = dict()
for i in range(len(a)):
s[a[i]] = b[i]
return s
# 输出
{'a': 1, 'b': 2, 'c': 3}

zip的方式:

1
2
3
4
5
a = ["a", "b", "c"]
b = [1, 2, 3]
print(dict(zip(a,b)))
# 输出
{'a': 1, 'b': 2, 'c': 3}

zip 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成多个tuple,然后返回由这些tuple组成的list。但是要注意版本区别,Python 2.0 返回的是list,Python 3.0 返回的是一个对象,需要手动list()转换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# python 2.0+
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 打包为元组的列表
[(1, 4), (2, 5), (3, 6)]
>>> zip(a,c) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]
>>> zip(*zipped) # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
[(1, 2, 3), (4, 5, 6)]

# python 3.0+
>>> a = [1,2,3]
>>> b = [4,5,6]
>>> c = [4,5,6,7,8]
>>> zipped = zip(a,b) # 返回一个对象
>>> zipped
<zip object at 0x103abc288>
>>> list(zipped) # list() 转换为列表
[(1, 4), (2, 5), (3, 6)]
>>> list(zip(a,c)) # 元素个数与最短的列表一致
[(1, 4), (2, 5), (3, 6)]

dict()函数用于创建一个字典。

1
2
3
4
5
6
7
# 语法
class dict(**kwarg)
# **kwargs -- 关键字。
class dict(mapping, **kwarg)
# mapping -- 元素的容器,映射类型(Mapping Types)是一种关联式的容器类型,它存储了对象与对象之间的映射关系。
class dict(iterable, **kwarg)
# iterable -- 可迭代对象。

使用方法:

1
2
3
4
5
6
7
8
>>>dict()                        # 创建空字典
{}
>>> dict(a='a', b='b', t='t') # 传入关键字
{'a': 'a', 'b': 'b', 't': 't'}
>>> dict(zip(['one', 'two', 'three'], [1, 2, 3])) # 映射函数方式来构造字典
{'three': 3, 'two': 2, 'one': 1}
>>> dict([('one', 1), ('two', 2), ('three', 3)]) # 可迭代对象方式来构造字典
{'three': 3, 'two': 2, 'one': 1}

深浅拷贝

这里来讲述一下深拷贝和浅拷贝。

变量存储在栈内存,对象存储在堆内存。

1660127290005.png

浅拷贝只对源对象的引用进行拷贝,对象的内容不进行操作。实现原理:

  1. 对于源对象是可变数据类型,在堆内存中创建新空间;
  2. 对于源对象不是可变数据类型,则拷贝起引用。

可变数据类型包括:List、Dictionary、
不可变数据类型包括:String、Number、Tuple

浅拷贝

  1. 不拷贝子对象的内容,只拷贝子对象的引用
  2. 可以使用内置函数copy()
1
2
3
4
5
6
7
8
# 单层浅拷贝-源对象是可变数据类型
import copy
a = [1,2]
c = copy.copy(a)


print(id(a),id(c)) # 地址不相同
4543590848 4542857664

对于源对象是可变数据类型,在堆内存中创建新空间。

深拷贝

  1. 会连子对象的内存全部拷贝一份,对子对象的修改不会影响资源对象
  2. 可以使用内置函数deeocopy()

1660127456935.png

源对象是不可变数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 单层浅拷贝-源对象是不可变数据类型

import copy
a = 1
b = (1, 2)
c = copy.copy(a)
d = copy.copy(b)

print(id(a),id(c)) # 地址相同
print(id(b),id(d)) # 地址相同

# 输出
4474448176 4474448176
4541979776 4541979776

1660188093129.png

总结

深浅拷贝都是对源对象的复制。

冒泡排序

1
2
3
4
5
6
7
8
num_list = [23, 54, 56, 7, 12, 354, 56, 77, 12, 4, 2]
for i in range(len(num_list)): # 从第0个元素开始循环
for j in range(len(num_list) - i - 1): # range范围为 总长度-1-当前元素,因为元素下标从0开始
if num_list[j] < num_list[j + 1]: # 如果该元素小于 下一个元素
print(num_list[j], num_list[j + 1])
num_list[j], num_list[j + 1] = num_list[j + 1], num_list[j] # 那么当前排序就为 该元素的位置就和下一个元素调换
print(num_list)
print(num_list)
  1. 首先第一层循环list;
    1. 第二层循环list,范围为list总长度-1-当前元素(元素下标都是从0开始)
      1. 如果该元素小于下一个元素
        1. 赋值 元素=下一个元素,下一个元素=该元素 (当前元素和下一个元素进行调换)

最后输出新的list就是排序后的list,从大到小。并且该算法还有两个规律:

  1. 循环总次数等于元素个数之和,比如10个元素需要55次、11个元素需要66次等等。
  2. 切换一下判断条件可以直接改为从小大排序if num_list[j] > num_list[j + 1]:

二分法查找

所谓二分法查找针对的是一个有序的数据集合,每次通过跟区间中的元素对比,将待查找的区间缩小为之前的一半,知道找到要查找的元素,或者区间缩小为0.

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
array = [0, 0, 1, 1, 3, 3, 4, 4, 6, 7, 12, 14, 15, 17, 19, 25, 30, 32, 33, 43, 44, 47, 50, 54, 61, 62, 66, 72, 77, 80, 91, 97, 102, 107, 110, 113, 118, 123, 126, 130, 142, 143, 144, 145, 148, 152, 158, 162, 177, 179, 184, 196, 198, 198, 200, 212, 212, 218, 236, 259, 266, 290, 298, 304, 310, 311, 338, 347, 349, 350, 365, 377, 381, 389, 389, 395, 404, 408, 431, 446, 466, 480, 496, 503, 521, 528, 531, 555, 570, 576, 593, 625, 681, 687, 723, 818, 835, 844, 853, 854]

#函数递归
#定义一个函数,给三个形参:低位值,高位值,查找值
def binarySearch(low,height,findNum):
#计算出中位数
middle = low+(height-low)//2
#如果中位数小于查找值,则锁定后半段
if findNum >array[middle]:
#重置低位数
low = middle +1
#如果中位数大于查找值,则锁定前半段
elif findNum<array[middle]:
#重置高位值
height = middle - 1
else:
#找到该值并返回
return '该值下标为:%s,值为:%s'%(middle,array[middle])
#没有找到则调用自身继续查找
return binarySearch(low,height,findNum)

print(binarySearch(array[0],len(array)-1,19))

# 输出
该值下标为:14,值为:19

限制

容易出错的地方:

  1. mid的取值,mid=(low+high)/2 这种写法是有问题的。因为如果 low 和 high 比较大的话,两者之和就有可能会溢出。正确的方法可以改为 middle=low+(height-low)//2 或者 middle=low+((height-low)>>1) (除以2的1次方);
  2. low和height的更新要使用height = middle-1low = middle+1

>> 右移,右边的数字指定了移动的位数,除法 。print(a >> 3) # 相当于a 除 2的3次方
<< 左移,左边的数字指定了移动的位数,乘法。print(a << 3) # 相当于a 乘 2的3次方

局限性:

  1. 只能查找有序的数据集;
  2. 针对的是静态有序数据集;
  3. 不适合数据量太大或太小的场景。太大的情况会消耗太多内存,太小的场景和遍历差别不大。
------ 本文结束 ------

版权声明

Medivh's Notes by Medivh is licensed under a Creative Commons BY-NC-ND 4.0 International License.
Medivh创作并维护的Medivh's Notes博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证
本文首发于Medivh 博客( http://www.mknight.cn ),版权所有,侵权必究。