Python知识点整理(2) —— 数据结构与内置算法

数据结构与算法

内置的算法数据结构

数据结构/算法语言内置内置库
线性结构list/tuplearray/collections.nametuple
链式结构collections.deque(双端队列)
字典结构dictcollections.Counter(计数器)/orderedDict(有序字典)
集合结构set/frozenset
排序算法sorted
二分算法bisect模块
堆算法heapq模块
缓存算法functools.lru_cache(Least Recent Used, python3)

collections模块

  • namedtuple()
  • deque 提供了一个双端队列
  • Counter
  • OrderedDict(记录key插入的顺序 ,使用了循环双链表实现 )
  • defaultdict

python dict 底层结构

  • 哈希表
  • 时间复杂度 o(1)
  • 使用二次探查解决哈希冲突问题

哈希冲突

  • 链接法
  • 探查法

哈希表扩容

python list/tuple区别

  • 都是线性结构
  • list可变对象,tuple保存的引用不可变(指的是没法替换掉这个对象,但如果引用指向的是可变对象,是可以修改这个引用指向的可变对象的)
  • list没法作为字典的key,tuple可以(可变对象不可hash)

LRU cache

  • Least Recently Used替换掉最近最少使用的对象
  • 当缓存空间不够的时候需要一种方式剔除key
  • 常见的有LRU, LFU等
  • LRU通过使用一个循环双端队列不断把最新访问的key放到表头实现

实现LRU Cache

  • dict + collections.OrderedDict实现
  • dict来当作k/v键值对的缓存
  • orderdict用来实现更新最近访问的key

常考算法

排序算法最差时间分析平均时间复杂度稳定性空间复杂度
冒泡排序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(Nlog2N)不稳定O(log2N) ~O(N)
堆排序O(N^log2N)O(Nlog2N)不稳定O(1)

排序算法的稳定性

  • 相同大小的元素排序之后依然保持相对位置不变,就是稳定的

快速排序

  • 退化????
  • 分治法(devide and conquer)
def QuickSort(array):
	if len(array) <= 1: # 递归出口
		return array
	
	pivot_index = 0 # 选择第一个元素作为主元
	pivot = array[pivot_index]
	pivot_left = [
		i for i in array[pivot_index+1:] if array[i]<=pivot	
	]
	pivot_right = [
		i for i in array[pivot_index+1:] if array[i] > pivot
	]
	return QuickSort(pivot_left) + [pivot] + QuickSort(pivot_right)

归并排序

def MergeSortedList(sorted_a, sorted_b):
	len_a, len_b = len(sorted_a), len(sorted_b)
	a, b = 0, 0
	merge_list = []
	while a < len_a and b < len_b:
		if sorted_a[a] <= sorted_b[b]:
			merge_list.append(sorted_a[a])
			a += 1
		else:
			merge_list.append(sorted_b[b])
			b += 1
	# 把多余的放到有序数组里面
	if a < len_a:
		merge_list.extend(sorted_a[a:])
	else:
		merge_list.extend(sorted_b[b:])
	return merge_list

def MergeSort(array):
	# 定义递归出口
	if len(array) <= 1:
		return array
	
	mid = len(array) / 2
	left_array = MergeSort(array[:mid])
	right_array = MergeSort(array[mid:])
	return MergeSortedList(left_array, right_array)

堆排序

  • 借助heapq模块快速实现
def HeapSort(array):
	from heapq import heappush, heappop
	items = []
	for val in array:
		heappush(items, val)
	return [heappop(items) for i in range(len(array))]

二分查找

  • 需要注意边界情况的判定
def BinarySearch(sorted_array, target):
	if not sorted_array:
		return -1
	
	begin, end = 0, len(sorted_array)-1
	while begin <= end:
		mid = (begin + end) / 2
		if sorted_array[mid] == target:
			return mid
		if sorted_array[mid] < target: # 要去右边找
			begin = mid + 1
		else: # 要去左边找
			end = mid - 1
	return -1

常考数据结构

链表
删除一个节点
合并两个有序链表

队列

  • collection.deque双端队列 (左右边都可以)
from collections import deque
class Queue:
	def __init__(self):
		self.itmes = deque()
	def append(self, val):
		return self.itmes.append(val)
	def pop(self):
		return self.items.popleft()
	def empty(self):
		return len(self.items) == 0
  • 用list实现
    • 后面加方便
    • 前面,效率不如deque

  • collections.deque实现
  • 用list实现
    用栈实现队列
    使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

用一个栈没法实现 先进先出 的结构,用两个栈 实现

  1. push:加到stack1中
  2. pop:如果stack2不为空,就直接从stack2中pop出来;如果stack2为空,将当前stack1中的所有pop出来,按照pop的顺序push到stack2中,将stack2中最后添加到元素pop出来。
from collections import deque
class MyStack(object):
	"""用deque实现栈"""
	def __init__(self):
        self.items = deque()
       
    def push(self, x):
        return self.itmes.append(x)
        
    def pop(self):
        return self.items.pop()
    
    def empty(self):
        return len(self.items) == 0
        
    def top(self):
    	return self.items[-1]
        
class MyQueue(object):
	"""用栈实现队列"""
    def __init__(self):
        self.s1 = MyStack()
        self.s1 = MyStack()
       
    def push(self, x):
        self.s1.push()
        
    def pop(self):
        if not self.s2.empty():
        	return self.s2.pop()
        while not s1.empty():
        	val = self.s1.pop()
        	self.s2.push(val)
        return self.s2.pop()
        
    def peek(self):
        if not self.s2.empty():
        	return self.s2.top()
        while not s1.empty():
        	val = self.s1.pop()
        	self.s2.push(val)
        return self.s2.top()
        
    def empty(self):
       	return self.s1.empty() and self.s2.empty()

用两个栈实现一个队列
min stack

字典&集合

  • 底层:哈希表
  • 哈希函数快速定位一个元素
  • 不断加入元素会引起哈希表重新开辟空间,拷贝元素到新数组

哈希表解决冲突

  • 链接法:key冲突以后使用链表填充相同的key元素
  • 开放寻址法: 冲突后根据一种方式(二次探查)寻找下一个客用的槽

二叉树

  • 先序:根、左、右
  • 中序:左、根、右
  • 后序:左、右、根
  • 二叉树涉及到递归和指针的操作
  • 定义一个二叉树:
class TreeNode:
	def __init__(self, val, left, right):
	self.val, self.left, self.right = val, left, right

二叉树常见题型

  • 二叉树的镜像(翻转二叉树)
    • 递归的方法
def InvertTree(root):
	if root:
		root.left, root.right = root.right, root.left
		InvertTree(root.left)
		InvertTree(root.right)
	return root
  • 层序遍历二叉树(广度优先)
def levelOrder(self, root):
	if not root: # 注意:输入可能为空
		return []
	res = []
	cur_nodes = [root]
	next_nodes = []
	res.append([i.val for i in cur_nodes])
	# 当前、孩子节点不为空
	while cur_nodes or next_nodes:
		for node in cur_nodes:
			if node.left:
				next_nodes.append(node.left)
			if node.right:
				next_nodes.append(node.right)
		if next_nodes:
			res.append(
				[i.val for i in next_nodes]
			)
		cur_nodes = next_nodes
	return res	 
  • 右视图 (先加右孩子,再加左孩子)
class Solution(object):
    def rightSideView(self, root):
        if not root: # root == None
            return []
        
        res = []
        cur_nodes = [root]
        next_nodes = []
        res.append(cur_nodes[0].val)
        while cur_nodes or next_nodes:
            for node in cur_nodes:
                if node.right:
                    next_nodes.append(node.right)
                if node.left:
                    next_nodes.append(node.left)
            if next_nodes:
                res.append(next_nodes[0].val)
            cur_nodes = next_nodes
            next_nodes = []
        return res
  • 二叉树遍历
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) # 右子树
	def inorder_trav(self, subtree):
		"""中序遍历"""
		if subtree is not None:
			self.inorder_trav(subtree.left) # 左子树
			print (subtree.data) # 根
			self.inorder_trav(subtree.right) # 右子树

  • 完全二叉树
    • 最大堆:非叶子结点v,v的值都比他的两个孩子大(支持pop获取最大元素)
    • 最小堆:非叶子结 点v,v的值都比他的两个孩子小(支持pop获取最小元素)
  • 常见问题
    • topK问题(找出最大的k个)
    • merge K list
      • 思路1: 第一个和第二个合并,第二个和第三个合并, …
      • 思路2: 读取链表里面的值,构造最小堆,最后pop
import heapq
class TopK:
	"""
	获取最大的k个元素,应该用最小堆实现
	思路:
	1. 原始数组的前k个元素构成一个最小堆
	2. 从第k+1个元素开始:
		如果当前元素的值<=堆根节点的值(当前堆中的最小值),pass
		如果大于的话,就用这个元素的来代替当前的根节点,并调整堆
	"""
	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)
		else:
			heapq.heappush(self.minheap, val)
	def get_topk(self):
		for val in self.iteralbe:
			self.push(val)
		return self.minheap
from heapq import heappush, heappop
class Solution(object):
    def mergeKLists(self, lists):
        """
        利用堆来合并k个链表
        """
        minheap = []
        # 遍历,构造堆
        for node in lists:
            while node:
                heappush(minheap, node.val)
                node = node.next
                
        if not minheap:# 一定要考虑特殊的输入:[]和[[]]
            return None
        
        # 构造有序链表
        root = ListNode(heappop(minheap))
        cur_node = root
        while minheap:
            next_node = ListNode(heappop(minheap))
            cur_node.next = next_node
            cur_node = next_node
        return root

字符串常考算法题

常用操作

  • 内置的:split,upper,replace
  • 常考题目
    • 翻转字符串 s.reversed()
    • 回文字符串
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值