collections
:
方法 | 作用 |
---|---|
namedtuple() | 创建命名元组子类的工厂函数 |
deque | 类似列表(list)的容器,实现了在两端快速添加(append)和弹出(pop) |
ChainMap | 类似字典(dict)的容器类,将多个映射集合到一个视图里面 |
Counter | 字典的子类,提供了可哈希对象的计数功能 |
OrderedDict | 字典的子类,保存了他们被添加的顺序 |
defaultdict | 字典的子类,提供了一个工厂函数,为字典查询提供一个默认值 |
UserDict | 封装了字典对象,简化了字典子类化 |
UserList | 封装了列表对象,简化了列表子类化 |
UserString | 封装了列表对象,简化了字符串子类化 |
import collections
# collections.namedtuple(typename, field_names, *, rename=False, defaults=None, module=None)
# 返回一个新的元组子类,名为typename ;简而言之,它让tuple更可读
_some = collections.namedtuple('Point', ['x', 'y']) # 这个Point不是关键,重点是让下标从01变为xy,更可读
p = _some(11, y=22)
p[0] + p[1] # 33
# 双端队列,两头插
de = collections.deque()
de.append(1)
de.append(2)
de.appendleft(0)
de # deque([0, 1, 2])
de.pop()
de.popleft()
# 计数器
c = collections.Counter('abababaccd')
c # Counter({'a': 4, 'b': 3, 'c': 2, 'd': 1})
c.most_common() # [('a', 4), ('b', 3), ('c', 2), ('d', 1)]
# OrderedDict
order = collections.OrderedDict() # 保存了插入时的顺序
order['a'] = 1
order['b'] = 2
order['c'] = 3
list(order.keys()) # ['a', 'b', 'c'] 方便实现LRU Cache
t = ([1],2,3)
t[2] = 4 # TypeError: 'tuple' object does not support item assignment
t[0].append(2)
init()
、get()
、put()
from collections import OrderedDict
# OrderedDict的特点是保存了插入时的顺序
class LRUCache:
def __init__(self,capacity=128):
self.capacity = capacity # 限制容量
self.order = OrderedDict()
def get(self, k):
if k in self.order:
val = self.order[k]
self.order.move_to_end(k) # 内置方法,kv移到尾部,改变顺序
# 注意:我们视字典尾部为队列表头,即踢出元素时从字典头部
return val
else:
return -1
def put(self, k, v):
if k in self.order:
del self.order[k]
self.order[k] = v # 更新kv,move_to_end()也可以的啦,但关键在于v可能不一样,
else:
if(self.capacity <= len(self.order)): # 满了
del self.order.popitem(last=False) # 踢出字典头部(最先进来的)
self.order[k] = v # 插到尾部(表头)
打开LeetCode
初始化结点还是会的吧:
# Definition for singly-linked list.
class ListNode:
def __init__(self, val=0, next=None):
self.val = val # 数据
self.next = None # 指针
代码实现(官方答案)
class Solution:
def reverseBetween(self, head: ListNode, left: int, right: int) -> ListNode:
# 设置头结点dummyNode 是这一类问题的一般做法
# 第一个数据节点我们叫首节点
dummy_node = ListNode(-1)
dummy_node.next = head
pre = dummy_node
for _ in range(left - 1): # 用户输入从1开始计数
pre = pre.next # 画个图帮助理解即可,pre指向left前一个
cur = pre.next
for _ in range(right - left): # range包左不包右哦!
next = cur.next
cur.next = next.next
next.next = pre.next
pre.next = next
return dummy_node.next
再来一个,给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点,打开leetcode
pre
和cur
# 力扣题解:
# 再强调一遍,链表的题带上头结点好做
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
# 如果删除首节点
if head.val == val:
return head.next
pre, cur = head, head.next
while cur and cur.val != val:
pre, cur = cur, cur.next
# while完毕,要么找着了,要么走完了
if cur:
pre.next = cur.next
return head
再来一个,删除链表指定节点,打开leetcode
# 官方答案
class Solution:
def deleteNode(self, node):
node.val = node.next.val
node.next = node.next.next
# 没了!
合并两个有序链表
class Solution:
def mergeTwoLists(self, l1, l2):
prehead = ListNode(-1) # 合并链表
prev = prehead
while l1 and l2:
if l1.val <= l2.val:
prev.next = l1
l1 = l1.next # l1指针前移
else:
prev.next = l2
l2 = l2.next
prev = prev.next
# 合并后 l1 和 l2 最多只有一个还未被合并完,我们直接将链表末尾指向未合并完的链表即可
prev.next = l1 if l1 is not None else l2 # python简洁之道
return prehead.next
再来,反转链表,上leetcode
# 官方答案
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre, cur = None, head
while cur:
nxt = cur.next
cur.next = pre
pre = cur
cur = nxt
return pre
from collections import deque
class Queue:
def __init__(self):
self.queue = deque() # 默认是栈
def append(self, value):
self.queue.append(value)
def pop(self):
return self.queue.popleft()
def empty(self)
return len(self.queue)==0
def test_queue():
q = Queue()
q.append(1)
q.append(2)
q.append(3)
print(q.pop())
print(q.pop())
print(q.pop())
# 同理,可以实现栈
# 声明:以下为官方解法
class MinStack:
# 辅助栈和数据栈同步
# 思路简单不容易出错
def __init__(self):
# 数据栈
self.data = []
# 辅助栈
self.helper = []
def push(self, x):
self.data.append(x)
# 辅助栈为空的时候,必须放入新进来的数
# 新来的数小于或者等于辅助栈栈顶元素的时候,才放入
if len(self.helper) == 0 or x <= self.helper[-1]: # [-1]表示最后一个值
self.helper.append(x)
# 否则放入辅助找顶元素
else:
self.helper.append(self.helper[-1]) # 为了数据同步
def pop(self):
if self.data:
self.helper.pop()
return self.data.pop()
# peek
def top(self):
if self.data:
return self.data[-1]
def getMin(self):
if self.helper:
return self.helper[-1]
# 队列:队尾进队头出
# 常见的思路是使用两个栈,倒换一下顺序
# 注,全程两个栈,再来个指针front指向出队元素
# 借鉴答案:
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.s1 = []
self.s2 = [] # 这里用列表模拟两个栈,先进后出结构即可
self.front = None
# 入队
# 只需往S1放即可
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
if not self.s1: self.front = x # s1最先放入的元素就是队头!!!
self.s1.append(x)
# 出队
# 必须从S2出队,但先要把S1的都移过来,一定要移干净
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.s2:
while self.s1:
self.s2.append(self.s1.pop())
self.front = None
return self.s2.pop()
# 取队头元素,这里就体现了front的作用,无需把s1都扔过来
def peek(self) -> int:
"""
Get the front element.
"""
if self.s2:
return self.s2[-1]
return self.front
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
if not self.s1 and not self.s2:
return True
return False
# 树节点
class BiTreeNode(object):
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
# 树及操作
class BiTree(object):
# 初始化树根
def __init__(self, root=None):
self.root = root
def pre_order(self, treeNode): # 一般传入根节点
if treeNode is not None:
print(treeNode.data)
self.pre_order(treeNode.left)
self.pre_order(treeNode.right)
# 中序和后序同理
# 思路很简单,遍历每层的值依次放入队列;使用两个数组保存当前节点和子节点,向下传递
class solution:
def __init__(self):
self.cur_node = []
self.next_node = []
self.res = [] #已遍历到的数组
def level_travel(self, node): # 起始点要把握好!
if not node:
return []
self.cur_node.append(node)
self.res.append([i.val for i in self.cur_node]) # 返回遍历结果是关键
while(self.cur_node or self.next_node):
for node in self.cur_node:
if node.left:
self.next_node.append(node.left)
if node.right:
self.next_node.append(node.right)
if self.next_node:
self.res.append([i.val for i in self.next_node])
self.cur_node = self.next_node # 准备遍历子层
self.next_node = [] # 再准备接收子层的子节点
return res
heapq
import heapq
# 直接使用python提供的堆结构,heapq,传参即可,无需初始化
# 维护最小堆是为了取堆顶元素,与新进入的元素比较
class TopK(object):
def __init__(self, i:list, capacity:int):
self.iterable = i # 待取列表
self.capacity = capacity # 取前几个,这里就是几
self.minheap = [] # 只需要准备一个列表即可,无需heapq()
def push(self, val):
if(len(self.minheap)>=self.capacity):
min_val = self.minheap[0] # 取最小值
if val < min_val:
pass # 太小了也,我要取最大的几个
else:
heapq.heapreplace(self.minheap, val) # 大的放进去,自动调整
else:
# 没存满,直接放入并维护
heapq.heappush(self.minheap, va)
def getTopK(self):
for val in self.iterable:
self.push(val)
return self.minheap
def test_heap():
import random
i = list(range(100000))
random.shuffle(i) # 乱序
_ = TopK(i, 15)
print(_.getTopK())
from heapq import heapify
def mergeKLists(self, lists: List[ListNode]) -> ListNode:: # 传入的是存储链表首节点的列表
# 遍历所有链表节点加入列表
# if not lists: # 空值判断,由于[[]]也会被认为是有值,所以直接判断node_list,这个要注意!
# return None
node_list = []
for linked in lists:
while linked:
node_list.append(linked.val)
linked = linked.next
# 形成最小堆
if not node_list:
return None
heapify(node_list)
# 构造新链表
root = ListNode(heappop(node_list)) # leetcode中直接定义了链表节点,也可自定义
cur = root
while node_list:
nextnode = ListNode(heappop(node_list))
cur.next = nextnode
cur = nextnode
return root
str
,并使用Unicode编码s.reverse()
,可以直接实现,但这样就没意思了吧def reverse_string(s:str):
begin = 0
end = len(s)-1
# 两个指针,两头开始,交换,靠拢
while begin<end: # 相等即止
s[begin], s[end] = s[end], s[begin] # swap
begin += 1 # 没有++
end -= 1
+=
代表改变了变量,相当于重新生成了一个变量++
代表改变了对象本身,而不是变量本身,但Python的数值对象是不可改变的class Solution(object):
def isPalindrome(self, num:int):
if num<0:
return False
s = str(x) # 这样就舒服了
begin = 0
end = len(s)-1
# 两个指针,两头开始,比较,靠拢
while begin<end: # 相等即止
if s[begin] != s[end]
return False
begin += 1 # 没有++
end -= 1
return True
import pytest
def test_isP():
# 测试用例
solver = Solution()
assert solver.isPalindrome(121) is True
assert solver.isPalindrome(12121) is True
assert solver.isPalindrome(12341) is False
assert solver.isPalindrome(0) is True
assert solver.isPalindrome(-1) is False
# pytest this.py
list_1 = [[0] * 3] * 3 # *3这是语言特性
# 但修改值时不行:
list_1[1][1] = 2
print(list_1)
# [[0, 2, 0], [0, 2, 0], [0, 2, 0]] 都改了! 因为浅拷贝
# 正确做法是:类似生成器:
list_2 = [[0 for i in range(3)] for j in range(3)]
# 当然,也可以数分三剑客:NumPy和Pandas、Matpotlib 结合一起使用
#但numpy的数组和列表不同,这里是指针数组,np是同类型数组