算法图解 学习笔记

基础部分

时间复杂度

  • 作用:指出(最糟糕/平均情况下)算法运行时操作数的增数
  • 时间复杂度表示的是运算次数。实际单次的执行时间可能不一致。(牺牲单次执行时间,减少次数;达到减少总时间的目的)
名称表示法 O(x)
常量时间1
对数时间log n
线性时间(linear time)n
n * log n
平方时间n^2
阶乘时间n!

PS: log 表示 log2 —— 每次过滤一半(1/2),经过几次可以只留下一个

  • 定义:模拟一组连接;如:x 与 y 的关系
  • 组成
    1. 节点 node —— x/y
    2. 边 edge —— 关系
  • 分类:
    1. 有向图
    2. 无向图
  • 加权图:边具有权重

NP完全问题 Non-deterministic Polynomial

  • 多项式复杂程度的非确定性问题
  • 简单定义:以难解著称的问题
  • 经典案例
    1. 集合覆盖问题:为了解决合集覆盖问题,必须计算所有可能的集合(2^n)
    2. 商旅问题:n!

傅里叶变换

  • 作用:压缩

数据结构

数组

  • 实现:内存中连续的一段地址
  • 优势:元素的定位

链表

  • 实现:内存中散列的地址,每个元素都会存储下一个元素的地址
  • 优势:元素的增删

栈 stack

  • 操作:压入/弹出(栈帧 frame)
  • 顺序:先进后出
  • 应用场景:函数调用(调用栈,在Java中一个线程拥有一个调用栈)
/*
step:
    1. greet("John") => push greet, name=John
    2. print "Hi " + name => Hi John
    3. greet2(name) => push greet2, name=John
    4. "How are you, " + name => How are you, John
    5. return => pop (greet2, name=John)
    6. buy() => push buy
    7. print "buy" => buy
    8. return => pop (buy)
    9. return => pop (greet, name=John)
 */
def greet(name):
    print "Hi " + name;
    greet2(name);
    buy();
    [return;]
    
def greet2(name):
    print "How are you, " + name;
    [return;]

def buy():
    print "buy";
    [return;]
  • 缺点:使用递归会占用大量内存,可使用以下方式进行优化
    1. 使用循环
    2. 使用尾递归(C实现了,Java没有实现)

队列 queue

  • 操作:入队/出队
  • 顺序:先进先出

散列表 hash table

  • 组成:键/值
  • 实际应用
    1. 查找
    2. 防重
    3. 缓存
  • 散列函数特性
    1. 将相同的输入生成相同的索引
    2. 将不同的输入生成不同的索引(理想)
    3. 已知数组的大小,不会返回无效索引
  • 散列函数:SHA函数
  • 冲突
    1. 解决:相同索引存储的是链表
    2. 避免:
      2.1 较低的填装因子:【一般情况下】大于0.7需要调整山列表(数组)长度resizing;每次调整长度扩大一倍。
      2.2 良好的散列函数

二叉查找树

  • 时间复杂度:
    1. 平衡状态 O(log n)
    2. 最差情况 O(n)

概念

  • 索引:元素的位置
  • 填装因子:散列表包含的元素/位置总数

相关思想

递归

  • 思想:分而治之
  • 目的:让解决方案更清晰(并没有性能上的优势)
  • 组成
    1. 基线条件 base case - 函数不再调用自己的条件,防止死循环
    2. 递归条件 recursive case - 函数调用自己的条件

分而治之 divide and conquer

  • 步骤
    1. 找出尽可能的简单的基线条件
    2. 不断将问题分解(缩小整体规模),直到符合基线条件

贪婪算法

  • 用于解决NP完全问题
  • 思想:企图通过寻求局部最优解来获得全局最优解

动态规划

  • 用于解决NP完全问题
  • 适用
    1. 约束条件 给定的情况下优化某种指标
    2. 问题可分解为彼此独立且离散的子问题(网格中的一个单元格就代表一个子问题)
    3. 涉及网格且每个单元格中的值就是需要的最优解
  • 思想:先解决子问题,再逐步解决大问题
  • 实现:
    1. 网格
    2. 公式:根据需要的指标自行推导

涉及算法

选择排序

  • 时间复杂度:O(n^2)
  • 说明:实际为(n * 1/2 * n),数字不计入因此为n^2
  • 过程:
    1. 第n次循环列表,获取最大的数max,将max放到新列表第n个位置
    2. 循环剩余的列表获取最大的,放到后面一个位置
    3. 重复2

快速排序

  • 时间复杂度:n * log n
    n - 每层需要的时间
    log n - 调用栈的高度(最优;最差为n)
  • 过程
    1. 选一个元素作为基准
    2. 比基准小的作为新的组,重复1
    3. 比基准大的作为新的组,重复1
    4. 如果新的组只有0个或1个则停止

二分法

  • 时间复杂度:O(log n)
  • 输入:有序的元素列表
  • 过程:
    1. 指针指向中间位置
    2. 获取指针位置的值并进行比较
      2.1 等于 返回指针位置
      2.2 大于 将上标指向指针位置-1(尽量缩小范围),重复执行
      2.3 小于 将下标指向指针位置+1(尽量缩小范围),重复执行
    3. 当下标大于上标时,表示无匹配元素

广度优先搜索

  • 适用:非权重图最短路径问题
  • 时间复杂度:V + E
    • V vertice 定点,出队&判断的时间消耗
    • E edge 边,入队的时间消耗
  • 理解
    1. 将起点入队
    2. 循环执行:从队列中出队,若不在搜索列表中则进行判断是否满足条件
      1. 若满足条件则得到结果
      2. 若不满足条件则将节点加入已搜索列表 & 将该结点的相关结点批量入队

狄克斯特拉算法 Dijkstra

  • 适用:权重图(要求:有向/无环/无负权)的最短路径搜索问题
    PS: 负权可能会导致已经处理过的节点再次更新

  • 步骤

    1. 找到 未处理 过的最便宜的节点
    2. 更新该结点的邻居开销
    3. 重复1,直到计算图中所有的节点
  • 理解

    1. 维护2个map,key均为节点名,map1 value为对应父节点用来追溯路径;map2 value为起点至该节点的最小花费
    2. 按顺序到达所有节点,并计算相应开销
  • 代码

graph["Start"] = {};
graph["Start"]["A"] = 6;
graph["Start"]["B"] = 2;

graph["A"] = {};
graph["A"]["End"] = 1;

graph["B"] = {};
graph["B"]["A"] = 3;
graph["B"]["End"] = 5;

graph["End"] = {}

K最近邻算法 k-nearest neighbors

  • 重点:挑选合适的特征进行比较
  • 步骤
    1. 将数据量化成一组数字
    2. 根据公式找到相邻的n个数据(该过程为分类,得到的结果是一个编组)
    3. 通过编组进行运算得到预测结果(回归)
  • 公式
  1. 距离公式
\sqrt{(a_1-a_2)^2 + (b_1-b_2)^2 + (c_1-c_2)^2 + \cdots + (z_1-z_2)^2}
  1. 余弦相似度
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值