py内置数据结构算法常考
常用内置的算法和数据结构
sorted
list/set/dict/tuple
常用内置数据结构和算法
数据结构/算法 | 语言内置 | 内置库 |
---|---|---|
线性结构 | list(列表)/tuple(元组) | array(数组, 不常用)/collections.namedtuple |
链式结构 | collections.deque(双端队列) | |
字典结构 | dict(集合) | collections.Counte(计数器)/OrderedDict(有序字典) |
集合结构 | set(集合)/frozenset(不可变集合) | |
排序算法 | sorted | |
二分算法 | bisect模块 | |
堆算法 | heapq模块 | |
缓存算法 | functools.lru_cached(Least Recent Used, py3) |
有用过collections模块吗?
collections模块提供了一些内置数据结构的扩展
namedtuple
deque
Counter
OrderedDict
defaultdict
import collections
Point = collections.namedtuple('Point', 'x, y')
p = Point(1, 2)
print(p.x)
print(p.y)
print(p[0])
print(p[1])
#namedtuple让tuple属性可读
#deque可以方便地实现queue/stack
de = collections.deque()
de.append(1)
de.appendleft(0)
deque([0, 1])
de.pop()
de.popleft()
import collections
c = collections.Counter()
c = collections.Counter('abcab')
print(c)
print(c['a'])
print(c.most_common())
#需要计数器的地方使用Counter
# !< out:
Counter({'a': 2, 'b': 2, 'c': 1})
2
[('a', 2), ('b', 2), ('c', 1)]
import collections
od = collections.OrderedDict()
od['c'] = 'c'
od['a'] = 'a'
od['b'] = 'b'
print(list(od.keys()))
# !< out:
['c', 'a', 'b']
#LRU cache
#带有默认值的字典
import collections
dd = collections.defaultdict(int)
print(dd['a'])
dd['b'] += 1
print(dd)
# !< out:
0
defaultdict(, {'a': 0, 'b': 1})
底层原理实现
py dict 底层结构
- dict底层使用的哈希表
为了支持快速查找使用了哈希表作为底层结构
哈希表平均查找时间复杂度O(1)
CPython解释器使用二次探查解决哈希冲突问题
哈希冲突和扩容是常考题
py list/tuple 区别
list vs tuple
都是线性结构 支持下标访问
list是可变对象, tuple保存的引用不可变
list没法作为字典的key, tuple可以(可变对象不可hash)
t = ([1], 2, 3)
t[0].append(1)
#保存的引用不可变指的是你没法替换掉这个对象
#但是如果对本身是一个可变对象,是可以修改
#这个引用指向的可变对象的
什么是LRUCache?
Least-Recently-Used替换掉最近最少使用的对象
缓存剔除策略,当缓存空间不够用的时候需要一种方式剔除key
常见的有 LRU,LFU等
LRU通过使用一个循环双端队列不断把最新访问的key放到表头实现
如何实现LRUCache?
字典用来缓存,循环双端链表用来记录访问顺序
利用py内置的dict+collections.OrderedDict实现
dict用来当做k/v键值对的缓存
OrderedDict用来实现更新最近访问的key
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity=128):
self.od = OrderedDict()
self.capacity = capacity
def get(self, key): #每次访问更新最新使用的key
if key in self.od:
val = self.od[key]
self.od.move_to_end(key)
return val
else:
return -1
def put(self, key, value): #更新k/v
if key in self.od:
del self.od[key]
self.od[key] = value #更新key到表头
else: #insert
self.od[key] = value
#判断当前容量是否已经满了
if len(self.od) > self.capacity:
self.od.popitem(last=False)
#请实现LRUCache并编写单元测试
算法常考点
排序+查找 重中之重
常考排序算法:冒泡排序, 快速排序, 归并排序, 堆排序
线性查找, 二分查找等
能独立实现代码(手写) 能够分析时间空间复杂度
常用排序算法的时空复杂度
排序算法 | 最差时间分析 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | 稳定 | O(1) |
选择排序 | O(n^2) | O(n^2) | 不稳定 | O(1) |
插入排序 | O(n^2) | O(n^2) | 稳定 | O(1) |
快速排序 | O(n^2) | O(n*log2n) | 不稳定 | O(log2n) - O(n) |
堆排序 | O(n*log2n) | O(n*log2n) | 不稳定 | O(1) |
py数据结构常考题
常考题型
py web后端常考数据结构
常见的数据结构链表, 队列, 栈, 二叉树, 堆
使用内置结构实现高级数据结构, 比如内置的list/deque实现栈
Leetcode或者 上的常见题
常考数据结构之链表
链表有单链表, 双链表, 循环双端链表
如何使用Py来表示链表结构
实现链表常见操作, 比如插入节点, 反转链表, 合并多个链表等
Leetcode练习常见链表题目
数据结构之链表
#leetcode
# Definition for singly-linked list.
class ListNode:
def __init__(self, x):
self.val = x
self.next = None
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
pre = None
cur = head
while cur:
nextnode = cur.next
cur.next = pre
pre = cur
cur = nextnode
return pre
常考数据结构之队列
队列(queue)是先进先出结构
如何使用py实现队列?
实现队列的apend和pop操作,如何做到先进先出
使用py的list或者collections.deque实现队列
Collections.deque 双端队列
#实现队列, 使用deque
from collections import deque
class Queue:
def __init__(self):
self.items = deque()
def append(self, val):
return self.items.append(val)
def pop(self):
return self.items.popleft()
def empty(self):
return len(self.items) == 0
def test_queue():
q = Queue()
q.append(0)
q.append(1)
q.append(2)
print(q.pop())
print(q.pop())
print(q.pop())
test_queue()
常考数据结构之栈
栈是后进先出结构
如何使用py实现栈?
实现栈的push和pop操作,如何做到后进先出
同样可以用py list或者collections.deque实现栈
#请实现一个Stack
#借助内置的数据结构非常容易实现一个栈Stack, 后进先出
from collections import deque
class Stack(object):
def __init__(self):
self.deque = deque() #或者用list
def push(self, value):
self.deque.append(value)
def pop(self):
return self.deque.pop()
#如何使用两个stack 实现队列 先进先出
#实现获取最小值栈minstack
常考数据结构之字典与集合
py dict/set底层都是哈希表
哈希表的实现原理, 底层其实就是一个数组
根据哈希函数快速定位一个元素, 平均查找O(1), 非常快
不断加入元素会引起哈希表重新开辟空间, 拷贝之前元素到新数组
哈希表如何解决冲突
链接法和开放寻址法
元素key冲突之后使用一个链表填充相同key的元素
开放寻址法是冲突之后根据一种方式(二次探查)寻找下一个可用的槽
cpython使用的是二次探查
常考数据结构之二叉树
先序, 中序, 后序遍历
先(根)序:先处理根, 之后是左子树, 然后是右子树
中(根)序:先处理左子树, 然后是根, 然后是右子树
后(根)序:先处理左子树, 然后是右子树, 最后是根
#数据结构之二叉树:
#树的遍历方式
#先序遍历 递归代码里先处理根
class BinTreeNode(object):
def __init__(self, data, left=None, right=None):
self.data, self.left, self.right = data, left, right
class BinTree(object):
def __init__(self, root=None):
self.root = root
def preorder_trav(self, subtree):
"""先(根)序遍历"""
if subtree is not None:
print(subtree.data) #递归函数里先处理根
self.preorder_trav(subtree.left) #递归处理左子树
self.preorder_trav(subtree.right) #递归处理右子树
#中序遍历 调整下把print(subtree.data)放中间就好
def inorder_trav(self, subtree):
if subtree is not None:
self.preorder_trav(subtree.left)
print(subtree.data) #中序遍历放到中间就好
self.preorder_trav(subtree.right)
常考数据结构之堆
堆其实是完全二叉树, 有最大堆和最小堆
最大堆:对于每个非叶子节点V, V的值都比它的孩子大
最大堆支持每次pop操作获取最大的元素, 最小堆获取最小元素
最大堆支持每次pop操作获取最大的元素, 最小堆获取最小元素
常见问题:用堆来完成topk问题, 从海量数字找寻找最大的k个
"""
class Topk:
def __init__(self, iterable, k):
self.minheap = []
self.capacity = k
self.iterable = iterable
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) #返回并且pop堆顶最小值,
#推入新的val值并调整堆
else:
heapq.heappush(self.minheap, val) #前面k个元素直接放入minheap
def get_topk(self):
for val in self.iterable:
self.push(val)
return self.minheap
def test():
import random
i = list(range(1000)) #这里可以是一个可迭代的元素, 节省内存
random.shuffle(i)
_ = Topk(i, 10)
print(_.get_topk())
test()
# !< out:
[990, 991, 992, 995, 993, 998, 994, 996, 997, 999]
使用堆解决TOPK问题
py白板编程(手写代码)
手写算法题
刷题, Leetcode github题解
如何准备
刷题
面试之前系统整理之前做过的题目,不要靠记忆而是真正理解掌握
打好基础是重点,面试可以刷常见题突击, 保存手感
面试前练习
刷题(leetcode + 剑指offer + 面经)
上常见题目用py是实现
把leetcode上常见分类题目刷一遍(github搜索leetcode分类)
常见排序算法和数据结构能手写
常考数据结构题--链表
链表涉及到指针操作较为复杂,容易出错, 经常用作考题
熟悉链表的定义和常见操作
常考题:删除一个链表节点 leetcode 237
合并两个有序链表 leetcode 21
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
class Solution:
def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
root = ListNode(None)
cur = root
while l1 and l2:
if l1.val < l2.val:
node = ListNode(l1.val)
l1 = l1.next
else:
node = ListNode(l2.val)
l2 = l2.next
cur.next = node
cur = node
if l1 is None:
cur.next = l2
else:
cur.next = l1
return root.next
二叉树
二叉树涉及到递归和指针操作, 常结合递归考察
二叉树的操作涉及到递归和指针操作
二叉树的镜像(反转二叉树)
如何层序遍历二叉树(广度优先) 102题
输出左右视角的二叉树结果
class TreeNode:
def __init__(self, val, left, right):
self.val, self.left, self.right = val, left, right
#leetcode 237
栈与队列
后进先出 vs 先进先出
熟练掌握用py的list 或者 collections.deque()实现栈和队列
常考题:用栈实现队列(使用两个栈实现)
leetcode:implement - queue - using - stacks 232题
|__| |__|
|__| |__|
|__| |__|
|__| |__|
S1 S2
栈2不为空直接pop 否则把栈1的所有元素放到栈2
然后执行栈2 pop操作
deque() 双端队列
from collections import deque
class Stack:
def __init__(self):
self.items = deque()
def push(self, val):
return self.items.append(val)
def pop(self):
return self.items.pop()
def top(self): #return stack top value
return self.items[-1]
def empty(self):
return len(self.items) == 0
class MyQueue:
def __init__(self):
"""
Initialize your data structure here.
"""
self.s1 = Stack()
self.s2 = Stack()
def push(self, x: int) -> None:
"""
Push element x to the back of queue.
"""
self.s1.push(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if not self.s2.empty():
return self.s2.pop()
while not self.s1.empty():
val = self.s1.pop()
self.s2.push(val)
return self.s2.pop()
def peek(self) -> int:
"""
Get the front element.
"""
if not self.s2.empty():
return self.s2.top()
while not self.s1.empty():
val = self.s1.pop()
self.s2.push(val)
return self.s2.top()
def empty(self) -> bool:
"""
Returns whether the queue is empty.
"""
return self.s1.empty() and self.s2.empty()
def test():
q = MyQueue()
q.push(1)
q.push(2)
q.push(3)
print(q.pop())
print(q.pop())
print(q.pop())
test()
数据结构之堆
堆的常考题基本围绕在合并多个有序(数组/链表) Topk问题
|-理解堆的概念 堆是完全二叉树 有最大堆和最小堆
|-会使用py内置的heapq模块实现堆的操作
|-常考题:合并k个有序链表leetcode merge-k-sorted-list 23 title
合并两个有序链表..
|--1.读取所有链表值
|--2.构造一个最小堆heapq实现
|--3.根据最小堆构造链表
# Definition for singly-linked list.
class ListNode(object):
def __init__(self, x):
self.val = x
self.next = None
from heapq import heapify,heappop
class Solution(object):
def mergeKLists(self, lists):
"""
:type lists: List[ListNode]
:rtype: ListNode
"""
h = []
for node in lists:
while node:
h.append(node.val)
node = node.next
if not h:
return None
heapify(h)
root = ListNode(heappop(h))
curnode = root
while h:
nextnode = ListNode(heappop(h))
curnode.next = nextnode
curnode = nextnode
return root
字符串常考题
字符串题
了解常用的字符串操作
翻转一个字符串 334
判断是否是回文串 9
py内置字符串操作split, upper, replace etc
s.reverse()
def reverseString(self, s):
beg = 0
end = len(s) - 1
while beg < end:
s[beg], s[end] = s[end], s[beg]
beg += 1
end -= 1
def isPalindrome(x):
if x < 0:
return False
s = str(x)
beg, end = 0, len(s) - 1
while beg < end:
if s[beg] == s[end]:
beg += 1
end -= 1
else:
return False
return True
字符串的双端遍历方法,可以解决类似问题
练习题
|--链表, 字符串,
如何反转一个单链表
使用循环的方式实现
使用递归地方式实现