python逻辑讲解_python-10-set详解

集 set

约定

set 翻译为集

collection 翻译集合类型,是一个大概念

set

可变的、无序的、不重复的元素的集合

但是也有顺序集合,比如java当中的treeset,有排序的

集当中的元素是散放的,是一个散列值,是个哈希。

python当中所有数据结构,在其他语言当中都能够找到。

列表更适合尾部追加,两头要操作就要用queue。

前面学过的可变的,就是bytearray,list。

可变不可变,指的就是能不能就地修改。

集合当中的元素,本身就是不能重复的。

集合最大的一个用处,就是去重。

set定义 初始化

set() -> new empty set object

set(iterable) -> new set object

s1 = set()

s2 = set(range(5))

s3 = set(list(range(10)))

s4 = {} # dict

s5 = {9,10,11} # set

s6 = {(1,2),3,'a'}

s7 = {[1],(1,)1} # ?

s = set(enumerate(range(10)))

s = set(range(10))

s = {}

type(s) # dict,集合当中什么都不写,这个就是字典了

s = {1}

type(s) # set

s1 = set() # 这个才是定义一个空的字典。

s = {(1,2),2,None}

s = {(1,2),2,None,'abc',b'abc',[1]} # unhashable type : 'list'

# list是一个不可哈希类型的。

# set当中的元素,要么都是不可变类型,要么就是一个单值。

s = {(1,2),2,None,'abc',b'abc',bytearray(b'abc')}

集合很重要的一个特点,就是无序、去重。

在python当中有可变不可变的区别。

set的元素

set的元素必须可以hash

目前学过的不可hash的类型有list,set

元素不可以索引

set可以迭代

hash('abc')

hash(b'abc')

上面两个哈希值一样

hash(10000000)

hash(1)

s = set(['abc',b'abc']) # 'abc',b'abc'两个哈希值一样,但是可以存进去的。

set??

set(s)

无序是迭代的,不可以索引,成员运算符,是可以的。

set增加

add(element)

增加一个元素到set当中

如果元素存在,什么都不做

update(*others

合并其他元素到set集合当中来

参数others必须是可迭代对象

就地修改

s = add(2)

s = update({1,2,3}) # 这个就是在做集合运算了

s = update([1,2,3],[1,3,4]) # 给了两个集合,把多个集合合并到当前集合

s = update([1,2,3],[1,3,4],(11,23,45))

s = update([1,2,3],[1,3,4],[11,23,45])

set删除

remove(element)

从set中移除一个元素

元素不存在,抛出来keyError异常

discard(element)

从set中移除一个元素

元素不存在,什么都不做

pop() -> item

移除并返回任意的元素,为什么是任意元素

空集返回KeyError异常

clear()

移除所有元素

移除的方式,就是求了元素的哈希值。

顺序结构的移除,就是要遍历一遍的。用索引,找的过程很快。

找到这个值,就是O(1)的。

但是移除这个值之后,后面的元素都要向前补充一下,所以速度慢。

但是集合是通过值的hash来进行移除的,这个hash就是相当于值的key。

移除了之后,大家也不用移动位置。

所以效率还是很高的。

pop?

help(s.pop())

s.pop()当中不要传参数

a = s.pop()

remove和pop都是要产生异常的,用的时候要注意异常处理

discard是弃用的意思,元素不存在,什么都不做,应该是比较安全的方法,相对于remove

这里remove和pop都会面临到异常处理的问题,discard就没有这个问题。

总结:

1. remove() 有异常处理

2. pop() 有异常处理

3. discard() 无异常处理

4. clear() 无异常处理

移除元素,都会面临到一个垃圾回收的问题。

set修改、查询

修改

要么删除,要么加入新的元素,因为没有重复的元素,修改就是新的。

为什么没有修改?

查询

非线性结构,无法索引

遍历

可以迭代所有元素

成员运算符

in 和 not in判断元素是否在set当中

效率呢?

这个效率是相当高的,相当于用索引访问线性结构

非线性结构用哈希值来定位,相当于用索引遍历列表

时间复杂度,就是相当于O(1)

set成员运算符的比较

list和set的比较

lst1 = list(range(100))

lst2 = list(range(1000000))

-1 in lst1、-1 in lst2 看看效率,随着规模的增加,应该时间会很低。

set1 = set(range(100))

set2 = set(range(1000000))

-1 in set1、-1 in set2 看看效率,随着规模的增加,应该没有多大区别。

%%timeit lst1 = list(range(100))

-1 in lst1 # 这个就是true或者false的问题。-1必须遍历全部。

%%timeit set1 = set(range(100)) # set_up_code 不计时了

-1 in set1 # 这个就是true或者false的问题。-1必须遍历全部。

当我们判断或者迭代的时候,用set效率会更高。

set和线性结构

线性结构的查询时间复杂度是O(n),随着数据规模的增大而增加耗时

set、dict等结构,内部使用hash值作为key,时间复杂度可以做到O(1),查询时间和数据规模无关

可hash

数值型:int float complex # 字面常量

布尔值:True False # 整形的子类

字符串:String bytes # 字面常量

tuple

None

以上都是不可变类型,成为可哈希类型,hashable

set的元素必须是可以hash的。

这里不要和可迭代对象搞混了,只要可迭代对象当中的元素是可以哈希的,那么就可以

s = set(list)这种用法

集合

基本概念

全集

所有元素的集合,例如实数集,所有实数组成的集合,就是全集

子集subset和超集superset

一个集合A所有元素都在另外一个集合B内,A是B的子集,B是A的超集

真子集合真超集

A是B的子集,且A不等于B,A就是B的真子集,B就是A的真超集

并集:多个集合合并的结果

交集:多个集合的公共部分

差集:集合当中除去和其他集合公共部分

集合类型当中用的最多的,就是list,和dict。

循环和递归,刚释放变量,一定要释放一下,

尤其是用到大数据规模的情况下,一定要测试一下。

可能会出现内存被耗尽的情况。

本来是虚拟地址,内存不够开了,

就要将内存当中的数据,不常用的,导入到swap当中,

windows当中叫做虚拟内存。

本来磁盘都是机械装置,有一个寻道。

速度是很慢的。

所以从内存到swap,从swap到内存,是很慢的。

磁盘大多数都是随机访问的。

所以,磁盘比磁带的好处,在于把顺序变成随机的。

后来出现了电路,后来出现了固态硬盘。

后来不用机械式了。

还是有寻道的问题,还有生产的良品率的问题。

你还要求空间比较小。

所以磁盘还是大量应用的。

遇到磁盘的时候,一定要小心,一定有考虑效率。

尽量要放在内存当中用。

但是不用磁盘也不行,没有副本,就没办法冗余了。

一边计算一边落地,有很多问题是需要考虑的。

线性结构的查询是随着规模变大就很耗时了。

所以一般规模比较大的时候,就要用到排序,

然后我们再进行查询,这个就很好了。

就不用顺序查找了,就可以折半了。

对于非线性结构,列表类似于数组,但是包装的要多一点。

set和dict,这几个东西,一定要搞清楚。

有这三个东西,基本上可以走遍天下了。

但是set和dict可以理解为一种,set可以理解成为特殊的字典。

内部使用hash作为key,有一个hash空间的问题。

有的叫哈希桶的,用来指代很大的散列空间,叫做值域。

值是散列的,是稀疏的。

基于hash的,时间复杂度,可以做到big O(1)

现在没有提空间复杂度。

空间复杂度,比如冒泡法,就开辟了一个temp,就多了一个。

空间复杂度,杨辉三角的时候,就有很大差异。

算法,就要空间复杂度和时间复杂度,都要优化好。

append的,空间复杂度就很高。

集合运算

并集

将两个集合A和B的所有的元素合并到一起,组成的集合称作集合A与集合B的并集

union(*others)

返回和多个集合合并后的新的集合

| 运算符重载

等同 union

update(*others)

和多个集合合并,就地修改

|=

等同update

union就是联合,合并的意思,可以用|符号

update,多个集合的合并,是就地修改的。

|=,相当于+=

a |= {1,2} | {2,3}

交集

集合A和B,由所有属于A且属于B的元素组成的集合

intersection(*others)

返回和多个集合的交集

&

等同于intersection

intersection_update(*others)

获取和多个集合的交集,并就地修改

&=

等同intersection_update

差集

集合A和B,由所有属于A且不属于B的元素组成的集合

difference(*others

返回和多个集合的差集

等同于difference

difference_update(*others)

获取和多个集合的差集,并就地修改

-=

等同difference_update

对称差集

集合A和B,由所有不属于A和B的交集元素组成的集合,记作(A-B)U(B-A)

symmetric_differenc(other)

返回和另一个集合的差集

^

等同于symmetric_differece

symmetric_difference_update(other)

获取和另外一个集合的差集并就地修改

^=

等同symmetric_difference_update

issubset(other)、<=

判断当前集合是否是另一个集合的子集

set1 < set2

判断set1是否是set2的真子集

issuperset(other)、>=

判断当前集合是否是other的超集

set1 > set2

判断set1是否是set的真超集

isdisjoint(other)

当前集合和另一个集合没有交集

没有交集,返回True

集合应用

共同好友

你的好友是ABC,他的好友是CBD,你们的共同好友是谁? FOF算法。

有了共同好友,才可以推荐

在生产过程当中基本上就是在redis当中做集合运算的。

交集问题:{'A','B','C'}.intersection({'B','C','D'})

微信群提醒

XXX与群里其他人都不是微信朋友关系

你要把业务映射成为脑子当中的逻辑

每个人登录会加载自己的好友信息,如果用select语句去查数据库,就是相当的复杂

并集:user id(A|B|C|...) == False,A、B、C等是微信好友的并集,用户ID不在这个并集当中,说明他和任何人都不是朋友。

权限判断

有一个API,要求权限同时具备A、B、C才能访问,用户权限是B、C、D,判断用户是否能够访问该API

有一个API,要求权限具备A、B、C任意一项就可以访问,用户权限是B、C、D,判断用户是否能够访问该API

API集合A,权限集合P

A-P = {},A-P为空集,说明P包含A

A.issubset(P)也行,A是P的子集也行

A & P = A也行

API集合A,权限集合P

A & P !={} 就可以

A.isdisjoint(P)==False 表示有交集

一个总任务列表,存储所有任务,一个完成的任务列表,找出没有完成的任务。

业务中,任务ID一般不可以重复

所有任务ID放到一个set当中,假设为ALL

所有已完成的任务ID放到一个set当中,假设为COMPLETED,它是ALL的子集。

ALL - COMPLETED = UNCOMPLETED

集合练习

随机产生2组各10个数字的列表,如下要求

每个数字取值范围[10,20]

统计20个数字当中,一共有多少个不同的数字?

2组当中,不重复的数字有几个?分别是什么?

2组当中,重复的数字有几个?分别是什么?

import random

a = []

b = []

num = list(range(10,21))

for _ in range(10);

a.append(random.randrange(10,21))

b.append(random.randrange(10,21))

#a = [1,9,7,5,6,7,8,8,2,6]

#b = [1,9,0,5,6,4,8,3,2,3]

#--------------------------

s1 = set(a)

s2 = set(b)

print(s1)

print(s2)

print(s1.union(s2)) # 并集

print(s1.symmetric_differece(s2)) # 对称差集

print(s1.intersection(s2)) # 交集

集合运算做小集合,问题不大的。

两个集合,如果都是10万级别的,求并集,要么修改自身,要么产生新的集合。

如果三个、四个10万的集合交集、并集、差集呢?

这个时候最好就要测试一下。

内存有的情况,就是可以开辟。

内存没有的时候,就是要考虑垃圾回收,要考虑程序效率了。

这个时候更要考虑空间复杂度。

Java、Python都要考虑这个问题,虚拟机语言。

C++就没有垃圾回收,就会容易产生内存碎片。

内存空洞、碎片,那么如何开辟大内存。

减少数据库负担的时候,拿出来的数据就要常驻内存。

集合运算我们要用,字典运算很少听到。

你可能感兴趣的:(python逻辑讲解)