zoukankan      html  css  js  c++  java
  • 第4章 数据结构算法

    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(<class 'int'>, {'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或者<剑指offer>上的常见题

    常考数据结构之链表
    链表有单链表, 双链表, 循环双端链表
    如何使用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 <剑指offer> github题解
    如何准备
    刷题
    面试之前系统整理之前做过的题目,不要靠记忆而是真正理解掌握
    打好基础是重点,面试可以刷常见题突击, 保存手感

    面试前练习
    刷题(leetcode + 剑指offer + 面经)
    <剑指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
    
    

    字符串的双端遍历方法,可以解决类似问题

    练习题
    |--链表, 字符串,
    如何反转一个单链表
    使用循环的方式实现
    使用递归地方式实现

  • 相关阅读:
    数列分块入门 1-8
    最远点对 [线段树+树的直径]
    实验室外的攻防战 UOJ#180 [树状数组]
    二叉搜索树 [四边形不等式优化区间dp]
    树上的等差数列 [树形dp]
    序列 [树状数组+离散化]
    ATcoder1983 BBQ Hard
    8.18日报
    8.17日报
    8.16日报
  • 原文地址:https://www.cnblogs.com/xzpin/p/11616745.html
Copyright © 2011-2022 走看看