数据结构与算法训练营

学习内容来自极客时间:极客大学—覃超


第一节:概述

学习过程

  • 打散知识点
  • 刻意练习
  • 反馈
  • 职业选手和业余选手的区别,一定要分解训练和反复练习
  • 不要一直待在舒适区
  • 反复练习苦练基本功
  • 反馈:GitHub、LeetCode多看别人的高质量代码
  • CodeReview:别人给你反馈
  • 四步思考问题的方式:
  1. Cliarifation:确定题目
  2. Possible solutions:尽可能的多思考解题方式
  3. Coding:多写
  4. Test Case:写测试用例

5遍刷题法

  • 第一遍
  1. 读题+思考
  2. 直接看解法,比较不同解法的优劣
  3. 背诵、默写
  • 第二遍
  1. 自己写
  2. 多种解法比较
  • 第三遍
  1. 一天之后反复练习
  • 第四遍
  1. 一周之后再练习
  • 第五遍
  1. 面试之前恢复训练

数据结构分类

  • 一维:
  1. 基础:数组(array),链表(linked list)
  2. 高级:栈(stack)队列(queue)双端队列(deque)集合(set)map(字典)
  • 二维:
  1. 基础:树(tree)图(graph)
  2. 高级:二叉搜索树、红黑树、堆、字典树
  • 特殊:
  1. 位运算
  2. 布隆过滤器
  3. 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的分治思想

你可能感兴趣的:(数据结构与算法训练营)