常用的算法和工具
using Array: 数组是最常用的工具
排序 : 选择排序, 插入排序, 归并排序; 快速排序
查找 : 二分查找
数据结构 : 栈, 队列, 堆
如何写出正确的程序—以二分查找为例
前提 : 有序数组才可以使用二分查找
1. 明确变量的含义 :
2. 循环不变量:改变了取值,但是不改变含义。
3. 注意边界所代表的含义,确保边界时有效的。
算法在思路上可以稍加记忆来加深理解
tip :
mid = ( left + right ) / 2; // 容易出现 left+right 的整型溢出问题
mid = ( left + right ) / 2; // 尽量使用减法
小数据集 : 巧妙的设计小数据集进行测试,调试程序的bug.
大数据集 : 测试程序的性能。
数组中常用技巧
- 有序:二分搜索
- 双索引技术 :
- 对撞指针
- 滑动窗口
- 元素个数有限 : 技术排序
- 滑动窗口和查找表的结合
查找相关的问题
- 查找有无:
set
; //集合 - 查找对应关系 :
map
; //字典,映射 - 哈希表的缺点: 失去了数据的顺序性, 插入和查找:
O(1)
- 二分搜索树(平衡): 插入和查找:
O(logn)
- 字符串中要考虑的两个点:
- 空串
- 字符集
链表相关
- 设置链表的虚拟头节点
- 穿针引线 : 设置工作指针,只改变指针所指的地址来完成算法。
- not just 穿针引线,有时候改变结点的值,效率更高。
- 双指针(滑动窗口): 线性表上的通用方法。
栈,队列,递归和二叉树
- 经典的递归算法 : 二叉树上的遍历算法。
- 递归算法的设计:(递归终止条件,递归过程)
- 空 是一棵二叉树,但不再有左右孩子。
- 三段式的设计: 递归终止条件,递归体,递归过程。
- 在写代码前,要定义清楚递归函数的递归意义是什么。
- Note : 注意递归的终止条件。
递归和回溯
- 树形问题 : 问题没有定义在二叉树的结构中,但是解决问题的思路本质上是一颗二叉树。
- 递归调用的一个重要特征是:return, 即回溯。
- 回溯是暴力解法的一个主要的实现手段,但是效率比较低。
- 回溯算法的改进:
- 动态规划
- 剪枝
- 回溯法的应用:排列问题,组合问题。
- 回溯,递归中一个重要的问题:如何传值的问题。
- 在类内定义变量。
- 剪枝操作 : 原有操作会尝试很多不需要尝试的可能。将这些不需要尝试的可能性去掉,即为剪枝。
- 二维平面上的回溯算法 : (e.g., Word Search)
- 规定寻找顺序,上下左右,顺时针。
- 找到树形结构,构造递归。
- 技巧:二维平面上的偏移量数组。在二维平面上的移动非常好用。
- 二维平面上的另一个经典算法 :
floodfill
算法- e.g., Number of Islands
- e.g., N-Queen
动态规划基础
什么是动态规划
- 斐波那契数列:天然的递归结构
// F(0) = 1; F(1) = 1; F(n) = F( n - 1 ) + F( n - 2 )
int fib( int n ) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1)+fib(n-2);
}
- 记忆化搜索 : 自上而下的方法
// F(0) = 1; F(1) = 1; F(n) = F( n - 1 ) + F( n - 2 )
vector<int> memo(n+1, -1);
memo[0] = 0;
memo[1] = 1;
int fib( int n ) {
if(n==0) return 0;
if(n==1) return 1;
if(memo[n] != -1)
return memo[n];
else
return fib(n-1)+fib(n-2);
}
- 动态规划 : 自下而上的方法
// F(0) = 1; F(1) = 1; F(n) = F( n - 1 ) + F( n - 2 )
int fib( int n ) {
vector<int> memo(n+1, -1);
memo[0] = 0;
memo[1] = 1;
for(int i =2; i <=n; i++) {
memo[i] = memo[i-1] + memo[i-2];
}
}
动态规划的解题思路
将原有问题拆解成若干子问题,同时保存了子问题的答案。使得每个子问题只求解一次,最终获得原问题的答案。 ps. 大多数动态规划问题都是递归问题。
最优子结构 :通过求解子问题的最优解,可以获得原问题的最优解。
思路 : 先自顶向下的思考问题,找到子问题;在自底向上的解决问题。
- 先用递归求解:递归体和递归结束条件。
- 将1. 转化为记忆化搜索。
- 将2,改写成动态规划。
动态规划——House Robber
解题思路 :
- 对状态的定义
考虑偷取[x ... n-1]
范围里的房子, 即f(x)
的定义 - 状态转移方程
f(0) = max ( v(0) + f(2), v(1)+f(3), v(2)+f(4), ... , v(n-3) + f(n-1), v(n-2), v(n-1) )
class Solution {
public int rob(int[] nums) {
int n = nums.length;
if(n == 0 ) return 0;
int[] memo = new int[n+1];
//初始化
memo[0] = 0;
memo[1] = nums[0];
for(int i = 2; i <= n; i++) {
memo[i] = Math.max(memo[i-2] + nums[i-1], memo[i-1]);
}
return memo[n];
}
}