一文看完面试常用算法

常用的算法和工具

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 算法

动态规划基础

什么是动态规划
递归问题
记忆化搜索
动态规划
  1. 斐波那契数列:天然的递归结构
// 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);
 }
  1. 记忆化搜索 : 自上而下的方法
// 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);
 }
  1. 动态规划 : 自下而上的方法
// 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. 大多数动态规划问题都是递归问题。

A1
自顶向下
自底向上
递归问题
重叠子问题/最优子结构
记忆化搜索
动态规划

最优子结构 :通过求解子问题的最优解,可以获得原问题的最优解。

思路 : 先自顶向下的思考问题,找到子问题;在自底向上的解决问题。

  1. 先用递归求解:递归体和递归结束条件。
  2. 将1. 转化为记忆化搜索。
  3. 将2,改写成动态规划。
动态规划——House Robber

解题思路 :

  1. 对状态的定义
    考虑偷取[x ... n-1]范围里的房子, 即 f(x) 的定义
  2. 状态转移方程
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) )
偷取 0
偷取 1
偷取 2
偷取 3
偷取 ....
偷取 n-3
偷取 n-2
偷取 n-1
偷取 2
偷取 3
偷取 ...
考虑偷取 0 ... n-1 范围内的所有房子
2 ... n-1
3 ... n-1
4 ... n-1
5 .. n-1
....
n-1
4 ... n-1
5 ... 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];
    }
}
发布了32 篇原创文章 · 获赞 7 · 访问量 7572

猜你喜欢

转载自blog.csdn.net/Isaacddx/article/details/104032123
今日推荐