学习内容来自极客时间:极客大学—覃超
第一节:概述
学习过程
- 打散知识点
- 刻意练习
- 反馈
- 职业选手和业余选手的区别,一定要分解训练和反复练习
- 不要一直待在舒适区
- 反复练习苦练基本功
- 反馈:GitHub、LeetCode多看别人的高质量代码
- CodeReview:别人给你反馈
- 四步思考问题的方式:
- Cliarifation:确定题目
- Possible solutions:尽可能的多思考解题方式
- Coding:多写
- Test Case:写测试用例
5遍刷题法
- 第一遍
- 读题+思考
- 直接看解法,比较不同解法的优劣
- 背诵、默写
- 第二遍
- 自己写
- 多种解法比较
- 第三遍
- 一天之后反复练习
- 第四遍
- 一周之后再练习
- 第五遍
- 面试之前恢复训练
数据结构分类
- 一维:
- 基础:数组(array),链表(linked list)
- 高级:栈(stack)队列(queue)双端队列(deque)集合(set)map(字典)
- 二维:
- 基础:树(tree)图(graph)
- 高级:二叉搜索树、红黑树、堆、字典树
- 特殊:
- 位运算
- 布隆过滤器
- LRU Cache
算法三大基石
- if-else、switch:跳转
- for、while:循环
- recursion:递归
五种高级算法
- 搜索:BFS、DFS
- 动态规划:DP
- 二分查找:BS
- 贪心算法:Greedy
- 数据、几何
脑图
- 数据结构
http://naotu.baidu.com/file/b832f043e2ead159d584cca4efb19703?token=7a6a56eb2630548c
- 算法
http://naotu.baidu.com/file/0a53d3a5343bd86375f348b2831d3610?token=5ab1de1c90d5f3ec
第二节:时间、空间复杂度
- 递归是指数级的时间复杂度
- 二分查找:log n
- 遍历二叉树:n
- 归并排序:n
- 搜索算法:n
自顶向下的编程方式
- 先写功能的主要逻辑
- 子功能都放在下面
- 最后完成子功能的业务逻辑
- 像写新闻稿一样写代码, 这样代码可读性强
第三节:数组、链表、跳表
- 内存管理器可以访问数组,可以进行随机的访问,O(1)
- 写链表一般都要定义一个类里面的成员有value、next
- value可以是任何类型,在其他语言可以设置城泛型
- 头:head,尾:tail,前继指针:prev
- 优化方式:升维、空间换时间
- 跳表:Redis
- 但是索引的维护成本很高
- 时间复杂度:O(log n)
- LRU Cache就是用多链表实现
- 为什么是用跳表而不是红黑树
实战题目
- Leetcode 283.移动零:Python中的列表copy的正确方式 num[:]
- Leetcode 15.三数之和:双指针问题,先排序,while i < l:左加右减
- 有些问题的双指针是不用排序的,双指针是交替向前移动
- 用dict:hashmap进行优化
- 两层循环全部扫到
- 先剪枝,遇到复杂问题往递归上想, 是一种由简到烦的过程
- 链表题做法固定,但是要多练:反转链表,判断是否有环
第四节:栈、队列
- 双端队列:deque都是插入删除O(1)查询O(n)
- 优先队列:插入:O(1)取出:O(logn),实现的数据结构较为多样和复杂
第五节:树
- 二叉树子节点有两个
- 树和图的差别看是否有环
- 树节点的定义
classTree Node:
def __init__(self, val):
self.value = value
self.left, self.right = None, None
- 为什么会出现树这种结构
- 因为人类的世界是三维的,用多维的数据模型会更好的优化
- 菲波那契数列是递归树
- 棋盘的形状也是树形结构,最后是叶子节点的终止状态
- 围棋是最难的,在树状结构寻求最优解,人生状态也是一棵树
- 树的遍历:
前序遍历:根左右
中序遍历:左根右
后序遍历:左右根
- 树的遍历也是根据顺序
- 二叉搜索树:左子树的节点都小于跟节点,右子树都大于跟节点
- 中序遍历是升序遍历
- 二叉搜索树查询的操作是logn的
- 二叉搜索树也是二分类似,删除根节点找第一个大于该节点的然后进行替换
- 二叉树的中序遍历(递归)
- 递归和循环在汇编是一样的
- 盗梦空间
分治和回溯
- 大问题分成子问题
- 回溯:
深度优先和广度优先
布隆过滤器
- 布隆过滤器和hashTable类似
- 不需要找到全部的数据,只需要告诉我有还是没有
- 布隆过滤器由一个很长的二进制和一系列随机映射函数,只是判断一个元素是否在一个集合中
- 缺点是有一定的误识别率和删除困难
- 一个元素只要有一个为0就认为不在布隆过滤器里面
- 但是都为1不代表他就在里面
- 大型分布式系统中很多用了布隆过滤器
from bitarray import bitarray
import mmh3
class BloomFilter:
def __init__(self, size, hash_num):
self.size = size # 二进制数组的大小
self.hash_num = hash_num # 每个对应多少位
self.bit_array = bitarray(size)
self.bit_array.setall(0)
def add(self, s):
for seed in range(self.hash_num):
result = mmh3.hash(s, seed) % self.size
self.bit_array[result] = 1
def lookup(self, s):
for seed in range(self.hash_num):
result = mmh3.hash(s, seed) % self.size
if self.bit_array[result] == 0:
return "Nope"
return "Probably"
bf = BloomFilter(500000, 7)
bf.add("cui")
print(bf.lookup("cui"))
print(bf.lookup("cu"))
LRU Cache
- 使用hashTable+doubleLinkedList
from collections import OrderedDict
class LRUCache:
def __init__(self, cap):
self.dic = OrderedDict()
self.cap = cap
def get(self, key):
if key not in self.dict:
return - 1
v = self.dic.pop(key)
self.key = v
return v
def put(self, key, value):
if key in self.dic:
self.dic.pop(key)
else:
if self.cap > 0:
self.cap += 1
else:
self.dic.popitem(last=False)
self.dic[key] = value
排序算法
- 插入排序,认为前面都有序,往里面插
- 选择排序:每次都选一个最小的
- 冒泡排序:两两交换,冒泡和选择排序相逆
- nlogn的分治思想