【读书笔记】算法的乐趣

什么是算法

  • 《算法导论》:定义良好的计算过程,它取一个或一组值作为输入,并产生一个或一组值作为输出
  • 《计算机程序设计艺术》:从一个步骤开始,按照既定的顺序执行完所有的步骤,最终结束(得到结果)
    -《数据结构与算法分析》:一系列的计算步骤,将输入数据转换成输出的结果。
  • Knuth 总结了算法的四大特征
    • 确定性:算法的每个步骤都是明确的,对结果的预期也是确定的
    • 有穷性:算法步骤数量有限
    • 可行性:算法每一步都可以执行
    • 输入和输出:有输入和输出

算法设计基础

程序基本结构

顺序执行、循环执行、分支跳转

基本数据结构(线性表)

  • 数组:(定长、可变)查找、遍历
  • 链表:插入、删除
  • 栈:LIFO,只能表的一端插入和删除数组元素
  • 队列:(单,双向、环形、双端)FIFO

复杂数据结构(非线性)

  • 树:层次数据(N叉树 { 连通无环图、B树、字典树、堆, 二叉树 [二叉查找树 (线段树),红黑树(区间树),AVL树 ] })
    • B树:多路分支且有序的层次结构
    • 区间树:区间查询相关的问题,比如判断区间之间是否存在重叠区域等
    • 线段树:需要查询在一个大的区间上,某个小范围内的统计值
    • 字典树:利用字符串的公共前缀或后缀,节约存储空间;匹配前缀或后缀
    • 堆:排序
  • 集合:
    • 一部分是对集合元素的操作,包括插入和删除集合元素、判断一个元素是否属于集合等;
    • 另一部分是集合之间的关系运算,包括集合的求交集、并集运算以及求差运算等
  • hash:查找
  • 图:邻接矩阵(二维数组方式)或邻接表(链表或可变长数组)
    • 有向/无向,连通/非连通,带权/不带权
    • 深度优先/广度优先搜索

算法设计常用思想

算法设计范式

贪婪法、分治法和递归法、动态规划法、线性规划法、搜索和枚举(包括穷尽枚举)

贪婪法

定义子问题,求解局部最优解

分治法和递归法

拆解问题为小问题,将子问题解决的结果合并

动态规划法

定义最优子问题、定义状态、定义转移方程
优化技巧:状态标志参数、备忘录

枚举

解空间的穷举搜索
优化技巧:

  • 启发式:利用搜索过程的信息跳过一些状态
  • 剪枝:已经不可能是最优解的节点不再递归;已经处理过的节点不再重复处理
  • 收敛:定义递归最大深度

例子

三个水桶等分 8 升水

问题描述
3,5,8升三个水桶,分8升水桶的8升水为2份4升水
方法:穷举 + 剪枝优化

type Status [3]int // 3,5,8升三个水桶 
type Action [3]int // from, to, amount
type HasStatus map[Status]int // 经过的State,是第几步执行到的
type StatusAt map[int]Status // 第n步结束时的State

妖怪与和尚过河

有三个和尚和三个妖怪过河,船可载2人,无论是在河的两岸还是在船上,只要妖怪的数量大于和尚的数量,妖怪们就会将和尚吃掉。求安全过河方案
方法:穷举 + 剪枝优化


稳定匹配与舞伴问题


爱因斯坦的思考题

爱因斯坦的思考题

groups[2].itemValue[type_drink]=drink_milk

项目管理与图的拓扑排序

利用 AOV 网表示有向图,可以对活动进行拓扑排序,根据排序结果对工程中活动的先后顺
序做出安排。但是寻找关键路径,估算工程活动的结束时间,则需要使用 AOE 网表示有向图
AOV描述依赖关系
AOE描述关键路径


RLE压缩算法与PCX图像


大整数运算

加、减、乘、除、幂


RSA加密解密


常用技巧

数组下标处理

控制好数组越界问题,可以简化数据模型,优化代码结构

var ChineseInt [10]string{ "零", "一", "二", "三”", "四", "五", "六", "七", "八", "九"}
ChineseInt[5] = "五" // 数字转汉字

一重循环实现两重循环的功能

用一重循环遍历二维表

// 一维=>二维
int row = i / M 
int col = i % N
// 二维=>一维
int i = row * N + col

棋盘(迷宫)类算法方向遍历

表驱动 > switch-case > if-else

type IdxOffset struct {
	int row
	int col
}
 dirOffset = [4]IdxOffset{{0,-1},{-1,0},{0,1},{1,0}}; // 可扩展到斜对角等
 for _, offset := range dirOffset {
	curRow = x + offset.row
	curCol = y + offset.col
}

代码的一致性处理技巧

数据操作的边界是最容易出错的地方, 因为边界数据的处理往往和内部数据的处理不太一样.
解决办法,就是尽量保持一致,减少if-else判断。上面表驱动也是一致性处理的案例

tail = (tail + 1) % N

链表和数组的配合使用

可以利用数组定义一次性分配好链表需要的内存

var blocks = make([]Block, 0, 64) // 8x8 黑白棋,64个格子

“以空间换时间”

比如动态规划的备忘录方法、剪枝的经过状态记录

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值