算法的重要性
系统的学习算法对一个程序员是十分有必要的。MIT 讲授《算法导论》的 Erik Demaine 教授说过:If you want to become a good programmer, you can spend 10 years programming, or spend 2 years programming and learning algorithms.
算法的学习
首先,
熟练掌握一到两门编程语言,这里推荐 C/C++ ,因为 C 是最贴近冯诺依曼模型的计算模式,最切合当今硬件的设计,有利于进一步了解底层和操作系统。
然后精读相对简单的《数据结构与算法分析——C语言描述》,之后再进一步精读《算法导论》。精读的时候在纸上写代码即可,一定要理解每一步是怎么执行的,画画中间状态,总结背会并能够默写常见的算法。业界一流的公司常会考白板编程,一定要练习纸上写代码,锻炼自己人肉查错人肉编译的能力。面试的时候,面试官出完题目后,不要立马去写代码,先确保理解需求后,然后思考一下,用笔在纸上画画,胸有成竹再去写。从有思路到写出源代码这个过程是需要大量练习的,没有捷径,这里刷题是很必要的,推荐 LeetCode ,刷三五遍后写代码自然有感觉,就如同学英语背课文培养语感一样。
算法的应用
机器学习、数据挖掘、自然语言处理、密码学、计算机图形学等,找工作常考:贪心、分治、动态规划、树、图等。许多复杂实际问题的解决往往都可以分解为许多小问题然后用经典算法进行解决的,只掌握库是不够的,有的问题还是需要自己做到对这些算法的灵活运用才行。
算法设计常用策略
- 穷举(递归回溯):“万能的“ 求 n 个数的全排列、8 皇后、有效地剪枝
- 变而治之: 变位词查找,两个词互为变为词,如果包含的全部字母相同,而顺序可能不同
- 分而治之(分而治之、减而治之): 二分查找——减而治之、归并排序——分而治之
- 迭代改进:最小生成树的局部调整 (局部最优即是全局最优)
- 贪心:最小生成树Prim, Krusal、单源最短路 dijkstra
- 动态规划: 背包、最短路 bellman-ford
时间复杂度
时间复杂度常用分析方法:
常见时间复杂度,由小到大依次为:
- O(1) 基本运算,+,-,* ,/ ,%,寻址,Hash的期望复杂度
- O(logn) 二分查找
- O(n^(1/2)) 枚举约数
- O(n) 线性查找
- O(nlogn) 归并排序、快速排序的期望复杂度、基于比较排序的算法下界
- O(n^2) 朴素最近点对、单源最短路
- O(n^3) Floyd最短路、普通矩阵乘法
- O(2^n) 枚举全部的子集
- O(n*(2^n)) TSP的动态规划算法
- O(n!) 枚举全排列
- O(n^n) 枚举[1..n]的n维数组的全部元素
通常面试往往会要求把时间复杂度优化到 O(n^2) 以下。常用的优化技巧:
推荐刷题网站
- Leetcode 国外找工作的题库
- Itint5 国内的leetcode
- Wikioi 比前两个难
- Codility 波兰的数学网站
- PAT 浙大主办
- Topcoder 编程比赛平台