Py学习  »  Python

python3算法[ 数据结构和算法 ](一)

Menffy • 3 年前 • 183 次点击  
阅读 15

python3算法[ 数据结构和算法 ](一)

1.1 将序列分解为单独的变量

p = [4,5,(1010,2,1)]
x, z, (y, m, d)=p
print(x, z, y, m, d)
>>4 5 1010 2 1

s = 'hello'
a, b, c, d, e = s
print(a,b,c,d,e)
>>h e l l o
复制代码
  • 不仅是元组或列表,只要是可迭代对象就可以执行分解操作,包括元组、列表、字符串、文件、迭代器或生成器。
  • 分解过程中,若需要丢弃某些值,可以选择一个用不到的变量名来重复接收,例如
a, _, c, _, d = 'hello'
复制代码

1.2 从任意长度的可迭代对象中分解元素

records = ['dave','dave@qq.com','138934594567','34565647567','234234-45645-567']
name, email, *phnoe_numbers = records
print(phone_numbers)
>>['138934594567','34565647567','234234-45645-567']
复制代码
  • 无论是否有值,变量phone_numbers始终是一个列表 。
  • 如下写法,也是可以的
*others, last_phone_numbers = record
name, *others, last_phone_numbers = record
复制代码
  • 若想分解出某些值,然后丢弃,同意可以选择一个用不到的变量名来重复接收,比如 _ 或者 ign(ignored),例如
records=['ace','1',2,3,(12,56,21)]
name,*_(*_,age)=records
复制代码
  • 这种分解操作可以假设是一种精巧的递归算法,但递归不是python的强项, 且有递归次数限制,默认是1000(可以修改)。
扩展使用
records=[
    ('foo',1,2),
    ('bar','hello'),
    ('foo',3,4)
]

def do_foo(x,y):
    print('foo',x,y)
def do_bar(s):
    print('bar',s)
    
for tag,*args in records:
    if tag == 'foo':
        do_foo(*args)
    elif tag == 'bar':
        do_bar(*args)
复制代码

1.3 保存最后N个元素

  • 问题:希望在迭代或其他形式处理中,对最后几个记录做一个有限的历史记录统计。

  • 解决:collections.deque队列的完美应用场景。列表也可以实现,但队列更优雅,速度更快。

#使用示例:
#指定队列大小为3
q = deque(maxlen = 3)
q.append(1)
q.append(2)
q.append(3)
print(q)
>>deque([1,2,3],maxlen=3)

q.append(4)
print(q)
>>deque([2,3,4],maxlen=3)

#不指定队列大小==无界限队列,可在队列两端执行添加/弹出操作
q2=deque()
q2.append(1)
q2.append(2)
print(q2)
>>deque([1,2])

q2.appendleft(4)
print(q2)
>>deque([4,1,2])

print(q2.pop())
>>2

print(q2)
>>deque([4,1])

print(q2.popleft())
>>4

复制代码

从队列两端添加或弹出元素的复杂度都是O(1)。这和列表不同,当从列表的头部插入或移除元素时,复杂度是O(N)

  • 使用案例:对一系列文本行做简单的文本匹配操作,当发现有匹配时就输出当前的匹配行以及最后检查过的N行文本(这里的N就是下面的history)
from collections import deque

#这是个生成器
def search(lines,pattern,history=5):
    #创建一个最多只能存储5个元素的队列(固定长度队列),当有新记录加入而队列已满时会自动移除最老的那条记录
    previous_lines=deque(maxlen=history)
    for line in lines:
        if pattern in line:
            #遇到yield就会返回,同时也是下一次循环进入的位置
            yield line,previous_lines
        previous_lines.append(line)
    
if __name__=='__main__':
    with open('somefile.txt') as f :
        #传入可迭代对象f,匹配字符串python,保留匹配行之前的5个元素
        for line,previous_lines in search(f,'python',5):
            #循环打印匹配行之前的5个元素
            for p in previous_lines:
                print(p,end='')
            #打印匹配行
            print(line,end='')
            print('-'*20)
复制代码

1.4 找到最大或最小的N个元素

  • 问题:在某个集合或列表中找出最大或最小的N个元素
  • 解决:包含一些堆操作函数的模块--heapq模块,其中有2个函数--nlargest()和nsmallest()

模块heapq中一些重要的函数

函 数 描 述
heappush(heap, x) 将x压入堆中(若heap是空列表,则使得heap列表具备堆特征,免除heapify()方法),复杂度O(logN),效率高
heappop(heap) 从堆中弹出最小的元素,复杂度O(logN),效率高
heapify(heap) 让列表具备堆特征,也是上述两个方法的先决条件
heapreplace(heap, x) 弹出最小的元素,并将x压入堆中
nlargest(n, iter) 返回iter中n个最大的元素
nsmallest(n, iter) 返回iter中n个最小的元素
#使用示例
import heapq
nums=[1,8,2,23,7,-4,18,23,42,37,2]
print(heapq.nlargest(3,nums))
>>[42,37,23]
print(heapq.nsmallest(3,nums))
>>[-4,1,2]
复制代码
  • 扩展:这两个函数都可以接收一个参数key,从而工作在更加复杂的数据结构之上。
p=[
{'name':'AAPL','price':20},
{'name':'FB','price':3},
{'name':'QQ','price':56},
{'name':'WX','price':43},
{'name':'WX','price':43}
]

#使用匿名函数,重新指定比较对象,这里的d是列表p里的某个元素
cheap=heapq.nsmallest(2,p,key=lambda d: d['price'])
expensive=heapq.nlargest(2,p,key=lambda d: d['price'])

复制代码
  • 讨论 :如果正在寻找最大或最小的N个元素,同集合中的元素的总数相比,N很小,那么使用下面的函数性能会更好。
  1. 工作原理:这些函数首先会在底层将数据转化成列表,且元素会以堆的顺序排列。
  2. 特性:堆最重要的特性就是h[0]总是最小的那个元素。接下来的元素可依次通过heapq.heappop()方法找到,该方法会将第一个元素(最小的)弹出,然后以第二小的元素取而代之(复杂度O(logN),N代表堆大小)
import heapq
nums=[1,8,2,23,7,-4,18,23,42,37,2]
h=list(nums)
heapq.heapify(h)
print(h)
>>[-4,2,1,23,7,2,18,23,42,37,8]

#要找到第3小的元素可以这样
heapq.heappop(h)
>>-4
heapq.heappop(h)
>>1
heapq.heappop(h)
>>2
复制代码
  • 总结
  1. 当所要找的元素数量相对较小时,用nlargest()和nsmallest()才是最适合的。注意,这两个方法的实际实现会根据使用它们的方式而有所不同,会做一些优化措施(比如,当N的大小同输入大小很接近时,就会采用排序的方法)
  2. 如果只想找出最小或最大的某个元素,用min()或max()。
  3. 如果要找的元素N和集合本身的大小差不多,通常更快的方法是先对集合排序,再做切片(sorted(items)[:N]或者sorted(items)[-N:])

1.5 实现优先级队列

  • 问题:实现一个队列,它能够以给定的优先级来对元素排序,且每次pop操作时都会返回优先级最高的那个元素。
  • 解决:利用 堆heapq模块 实现简单的优先级队列
import heapq
class PriorityQueue:
    def __init__(self):
        self._queue=[]
        self._index=0
    
    def push(self,item,priority):
        #堆的特性是self._queue[0]总是最小的那个元素,加个-号就是为了把最大值变最小值
        #变量self._index在具有相同优先级的元素间,作为比较操作的第二选择
        heapq.heappush(self._queue,(-priority,self._index,item))
        self._index+=1
    
    def pop(self):
        # heapq.heppop(self._queue) == 最小元素(-priority,self._index,item),
        # heapq.heppop(self._queue)[-1] == item
        return heapq.heppop(self._queue)[-1]
        

class Item:
    def __init__(self,name):
        self.name=name
        
    #打印对象的时候调用该函数
    def __repr__(self):
        return 'Item({!r})'.format(self.name)

#str()函数: 主要面向用户,其目的是可读性,返回形式为用户友好性和可读性都较强的字符串类型;
#repr()函数:面向的是python解释器,或者说开发人员,其目的是准确性,其返回值表示python解释器内部的含义,常作为编程人员debug用途。

q=priority()
q.push(Item("foo"),1)
q.push(Item("bar"),5)
q.push(Item("spam"),4)
q.push(Item("grok"),1)

q.pop()
>>Item('bar')
q.pop()
>>Item('spam')
q.pop()
>>Item('foo')
q.pop()
>>Item('grok')
复制代码

请注意,如果有相同优先级的两个元素(foo和bar),返回的顺序和它们插入到队列时的顺序相同。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/71045
 
183 次点击