摘要

面试也是一门学问,在面试之前做好充分的准备是成功的必要条件。程序员在代码面试时,常会遇到编写算法的相关问题,比如排序、二叉树遍历等等。

在程序员的职业生涯中,算法亦算是一门基础课程。尤其是在面试环节,很多公司都会要求程序员编写一些算法实例,例如快速排序、二叉树查找等。

这篇文章总结了编码面试中的常见主题,包括:1) 字符串/数组/矩阵,2) 链表,3) 树,4) 堆,5) 图,6) 排序,7) 动态规划,8) 位操作,9) 组合和排列,以及 10) 数学。将问题归为一类总是不容易的,因为很多问题可能属于多个类别。

更新列表可 在此处获得。您可以下载 PDF 版本

1. 字符串/数组

算法问题的输入通常是字符串或数组。在没有 IDE 自动完成的情况下,应记住以下常用方法:

toCharArray() // 获取 String 的 char 数组
charAt(int x) // 获取特定索引处的 char
length() // 字符串长度
length // 数组大小 
substring(int beginIndex) 
substring(int beginIndex, int endIndex)
Integer.valueOf() // 字符串转整数
String.valueOf() // 整数转字符串
Arrays.sort()  // 数组排序
Arrays.toString(char[] a) // 转换为字符串
Arrays.copyOf(T[] original, int newLength)
System.arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

经典问题:

  1. 旋转数组字符串中的反向单词
  2. 评估反向波兰符号(堆栈)
  3. 同构字符串
  4. 字梯(BFS)字梯 II(BFS)
  5. 两个排序数组的中位数
  6. 数组中第 K 个最大的元素
  7. 通配符匹配正则表达式匹配
  8. 合并间隔插入间隔
  9. 两数之和两数之和 II两数之和 III3Sum4Sum
  10. 3Sum 最接近
  11. 字符串转整数 (atoi)
  12. 合并排序的数组
  13. 有效括号
  14. 最长有效括号
  15. 实现 strStr()
  16. 最小大小子数组总和
  17. 搜索插入位置
  18. 最长连续序列
  19. 有效回文
  20. 之字形转换
  21. 添加二进制数
  22. 最后一个字的长度
  23. 三角形
  24. 包含重复项:IIIIII
  25. 从已排序数组中删除重复项:IIIRemove Element移动零
  26. 最长的子字符串而不重复字符
  27. 包含 2 个唯一字符的最长的子字符串 [Google]
  28. 具有所有单词串联的子串
  29. 最小窗口子串
  30. 在旋转排序数组中找到最小值:III
  31. 在旋转数组中搜索:III
  32. 最小堆栈
  33. 多数元素:III
  34. 公牛和母牛
  35. 直方图中最大的矩形
  36. 最长的公共前缀 [Google]
  37. 最大的数字
  38. 简化路径
  39. 比较版本号
  40. 加油站
  41. 帕斯卡三角形:III
  42. 装满水的容器
  43. 糖果 [Google]
  44. 捕获雨水
  45. 数数并说
  46. 搜索范围
  47. 基本计算器基本计算器 II
  48. 组字母图
  49. 最短回文
  50. 矩形区域
  51. 摘要范围
  52. 增加三重子序列
  53. 使用目标数字列表和算术运算
  54. 字符串的反元音
  55. 翻转游戏翻转游戏 II
  56. 缺少数字找到重复的数字第一个缺失正数
  57. 有效字谜组移位的字符串
  58. 前 K 个常见元素
  59. 查找峰值元素
  60. 字模式字型 II
  61. H-IndexH-Index II
  62. 回文对
  63. 一个编辑距离
  64. 扰乱字符串
  65. 第一个不良版本
  66. 整数以英语单词
  67. 文本对齐方式
  68. 删除无效的括号
  69. 两个数组的交集两个数组的交集 II
  70. 滑动窗口最大值数据流的移动平均值
  71. 猜测数更高或更低

2. 矩阵

解决矩阵相关问题的常用方法包括 DFS、BFS、动态规划等。

经典问题:

  1. 设置矩阵零点
  2. 螺旋矩阵
  3. 螺旋矩阵 II
  4. 搜索 2D 矩阵
  5. 搜索 2D 矩阵 II
  6. 旋转图像 [Palantir]
  7. 有效数独
  8. 最小路径总和(DP) [Google]
  9. 唯一路径(DP) [Google]
  10. 唯一路径 II(DP)
  11. 岛屿数量(DFS / BFS)岛屿数量 II(不交集),无向图中连接的组件数
  12. 周围区域(BFS)
  13. 最大矩形
  14. 最大正方形
  15. 单词搜索(DFS)
  16. 单词搜索 II
  17. 范围总和查询 2D – 不可变
  18. 矩阵中最长的增加路径(DFS)
  19. 与所有建筑物的最短距离
  20. 生命游戏
  21. 油漆房油漆房 II
  22. 数独解算器(DFS)
  23. 墙和门(DFS / BFS)
  24. 井字游戏
  25. 最佳集合点

3. 链表

在 Java 中,链表的实现非常简单。每个节点都有一个值和到下一个节点的链接。

class Node {
    int val;
    Node next;
 
    Node(int x) {
        val = x;
        next = null;
    }
}

链表的两个流行应用是堆栈和队列。

Stack

class Stack{
    Node top; 
 
    public Node peek(){
        if(top != null){
            return top;
        }
        return null;
    }
 
    public Node pop(){
        if(top == null){
            return null;
        } else {
            Node temp = new Node(top.val);
            top = top.next;
            return temp;    
        }
    }
 
    public void push(Node n){
        if(n != null){
            n.next = top;
            top = n;
        }
    }
}

Queue

class Queue{
    Node first, last;
 
    public void enqueue(Node n){
        if(first == null){
            first = n;
            last = first;
        } else {
            last.next = n;
            last = n;
        }
    }
 
    public Node dequeue(){
        if(first == null){
            return null;
        } else {
            Node temp = new Node(first.val);
            first = first.next;
            return temp;
        }    
    }
}

Java 标准库包含一个名为 Stack 的类。Java SDK 中的另一个类是 LinkedList,它可用作队列(add()remove())。(LinkedList 实现 Queue 接口。)如果在面试过程中需要堆栈或队列来解决问题,则可以使用它们。

经典问题:

  1. 使用数组实现堆栈
  2. 添加两个数字
  3. 重新排序列表
  4. 链接列表循环
  5. 使用随机指针复制列表
  6. 合并两个排序的列表
  7. 奇偶链接列表
  8. 从排序中删除重复项列表:III
  9. 分区列表
  10. LRU 缓存
  11. 两个链接列表的交集
  12. 删除链接列表元素
  13. 成对交换节点
  14. 反向链接列表反向链接列表 II,打印链接列表以相反的顺序
  15. 从列表末尾删除第 N 个节点(快慢指针)
  16. 使用队列实现堆栈
  17. 使用堆栈实现队列
  18. 回文链接列表
  19. 使用数组实现队列
  20. 链接列表中的删除节点
  21. k 组中的反向节点

4. 树、堆与字典树

树通常是指二叉树。每个节点都包含一个左节点和一个右节点,如下所示:

class TreeNode{
    int value;
    TreeNode left;
    TreeNode right;
}

以下是一些与树相关的概念:

  1. 二叉搜索树 (Binary Search Tree):对于所有节点,左子节点 <= 当前节点 <= 右子节点。
  2. 平衡与不平衡:在平衡树中,每个节点的左右子树的深度相差 1 或更小。
  3. 完整的二叉树:除叶子外的每个节点都有两个孩子。
  4. 完美二叉树:完整的二叉树,其中所有叶子处于相同深度或相同级别,并且每个父级都有两个子级。
  5. 完全二叉树:一个二叉树,其中每个级别(除了最后一个级别)都已完全填充,并且所有节点都尽可能地靠左。

) 是满足堆属性的基于树的专用数据结构。其操作的时间复杂度很重要(例如,find-min,delete-min,insert 等)。在 Java 中,必须了解 PriorityQueue

4.1 树

  1. 二叉树的遍历:前序中序后序层序层序 II垂直顺序
  2. 反转二叉树
  3. 在 BST 中第 K 个最小元素
  4. 二叉树最长连续序列
  5. 确认二叉搜索树
  6. 平铺二叉树到链表
  7. 路径总和:I(DFS 或 BFS)II(DFS)
  8. 构造二叉树:从有序和后序遍历从前序和有序遍历
  9. 将排序的数组转换为二叉搜索树 [Google]
  10. 将排序后的列表转换为二叉搜索树 [Google]
  11. 二叉树的最小深度
  12. 二叉树的最大路径总和
  13. 平衡二叉树
  14. 对称树
  15. 二叉搜索树迭代器
  16. 二叉树右侧视图
  17. 二叉搜索树的最低共同祖先
  18. 二叉树的最低公共祖先
  19. 验证二叉树的前序序列化
  20. 在每个节点中填充下一个右指针
  21. 在每个节点 II 中填充下一个右指针
  22. 唯一的二叉搜索树:I(DP)II(DFS)
  23. 根到叶的总数(DFS)
  24. 计数完整树节点
  25. 最近的二叉搜索树值
  26. 二叉树路径
  27. 二叉树的最大深度
  28. 恢复二叉搜索树
  29. 相同树
  30. 对二叉树进行序列化和反序列化
  31. 在 BST 中顺序排序
  32. 查找二叉树的叶子
  33. 最大的 BST 子树

4.2 堆

  1. 合并 k 个排序数组 [Google]
  2. 合并 k 个排序列表
  3. 从数据流中查找中位数
  4. 会议室 II会议室
  5. 范围添加

4.3 字典树 (Trie)

  1. 实施 Trie(前缀树)
  2. 添加和搜索 Word-数据结构设计(DFS)

4.4 段树

  1. 范围总和查询 - 可变
  2. 天际线问题

5. 图

与图相关的问题主要集中在深度优先搜索 (DFS) 和广度优先搜索 (BFS)。深度优先搜索非常简单,您可以从根节点开始遍历所有邻居。

以下是图形和广度优先搜索的简单实现。关键是使用队列存储节点。

广度优先搜索

1) 定义 GraphNode

class GraphNode{ 
    int val;
    GraphNode next;
    GraphNode[] neighbors;
    boolean visited;
 
    GraphNode(int x) {
        val = x;
    }
 
    GraphNode(int x, GraphNode[] n){
        val = x;
        neighbors = n;
    }
 
    public String toString(){
        return "value: "+ this.val; 
    }
}

2) 定义 Queue

class Queue{
    GraphNode first, last;
 
    public void enqueue(GraphNode n){
        if(first == null){
            first = n;
            last = first;
        } else {
            last.next = n;
            last = n;
        }
    }
 
    public GraphNode dequeue(){
        if(first == null){
            return null;
        } else {
            GraphNode temp = new GraphNode(first.val, first.neighbors);
            first = first.next;
            return temp;
        }    
    }
}

3) 广度优先搜索使用队列

public class GraphTest {
 
    public static void main(String[] args) {
        GraphNode n1 = new GraphNode(1); 
        GraphNode n2 = new GraphNode(2); 
        GraphNode n3 = new GraphNode(3); 
        GraphNode n4 = new GraphNode(4); 
        GraphNode n5 = new GraphNode(5); 
 
        n1.neighbors = new GraphNode[]{n2,n3,n5};
        n2.neighbors = new GraphNode[]{n1,n4};
        n3.neighbors = new GraphNode[]{n1,n4,n5};
        n4.neighbors = new GraphNode[]{n2,n3,n5};
        n5.neighbors = new GraphNode[]{n1,n3,n4};
 
        breathFirstSearch(n1, 5);
    }
 
    public static void breathFirstSearch(GraphNode root, int x){
        if(root.val == x)
            System.out.println("find in root");
 
        Queue queue = new Queue();
        root.visited = true;
        queue.enqueue(root);
 
        while(queue.first != null){
            GraphNode c = (GraphNode) queue.dequeue();
            for(GraphNode n: c.neighbors){
 
                if(!n.visited){
                    System.out.print(n + " ");
                    n.visited = true;
                    if(n.val == x)
                        System.out.println("Find "+n);
                    queue.enqueue(n);
                }
            }
        }
    }
}

Output:

value: 2 value: 3 value: 5 Find value: 5
value: 4

经典问题:

  1. 复制图表
  2. 课程表课程表 II最小高度树
  3. 重建行程
  4. 图有效树

6. 排序

不同排序算法的时间复杂度。您可以转到 Wiki 来查看它们的基本概念。

AlgorithmAverage TimeWorst TimeSpace
Bubble sortn^2n^21
Selection sortn^2n^21
Insertion sortn^2n^21
Quick sortn log(n)n^2log(n)
Merge sortn log(n)n log(n)depends

注:BinSort、Radix Sort 和 CountSort 与其他假设使用不同的假设集,因此它们不是“常规”排序方法。(感谢 Fidel 指出这一点)

这是一些实现/演示,此外,您可能想了解 Java 开发人员在实践中的排序方式

  1. Mergesort
  2. Quicksort
  3. InsertionSort
  4. 最大间隙(桶排序)
  5. 颜色排序(计数排序)

7. 动态规划

动态规划是一种用于解决具有以下属性的问题的技术:

  1. 使用较小实例的解决方案来解决实例。
  2. 对于较小实例的解决方案可能需要多次。
  3. 较小实例的解决方案存储在表中,因此每个较小实例仅解决一次。
  4. 额外的空间用于节省时间。

攀登台阶的问题完全适合这 4 个属性。因此,可以通过使用动态规划来解决。

public static int[] A = new int[100];
 
public static int f3(int n) {
    if (n <= 2)
        A[n] = n;
 
    if(A[n] > 0)
        return A[n];
    else
        A[n] = f3(n-1) + f3(n-2); // 存储结果,因此只计算一次!
    return A[n];
}

经典问题:

  1. 编辑距离
  2. 不同的子序列总数
  3. 最长的回文子串
  4. 断字:III
  5. 最大子数组:I最大乘积子数组
  6. 回文分区:III
  7. 强盗 (House Robber):I [Google],IIIII
  8. 跳跃游戏:III
  9. 最佳买卖股票:IIIIIIIV
  10. 地下城游戏
  11. 最小路径总和
  12. 唯一路径
  13. 解码方式
  14. 最长的公共子序列
  15. 最长的公共子串
  16. 最长的增长子序列
  17. 硬币找零
  18. 完美正方形

8. 位操作

位运算符:

OR (\)AND (&)XOR (^)Left Shift (<<)Right Shift (>>)Not (~)
1\0=11&0=01^0=10010<<2=10001100>>2=0011~1=0

得到第 i 个给定数字 n。(我从 0 开始计数,从右开始)

public static boolean getBit(int num, int i){
    int result = num & (1<<i);
 
    if(result == 0){
        return false;
    } else {
        return true;
    }
}

例如,获取数字 10 的第二位。

i=1, n=10
1<<1= 10 
1010 & 10 = 10 
10 is not 0, so return true;

经典问题:

  1. 单个数字
  2. 单个数字 II
  3. 最大二进制间隙
  4. 1 个位数
  5. 反转位
  6. 重复的 DNA 序列
  7. 数字范围的按位与
  8. 两个整数的总和
  9. 计数位
  10. 字长的最大乘积
  11. 格雷码

9. 组合和排列

组合和排列之间的区别在于顺序是否重要。

范例 1:

给定 5 个数字 -1、2、3、4 和 5,打印出 5 个数字的不同顺序。4 不能是第三个,3 和 5 不能相邻。有多少种不同的组合?

范例 2:

给定 5 个 banaba,4 个梨和 3 个苹果,假设一种水果是相同的,那么有多少种不同的组合?

类问题:

  1. 排列
  2. 排列 II
  3. 排列序列
  4. 生成括号
  5. 组合和:I(DFS)II(DFS)III(DFS)IV(DP)
  6. 组合(DFS)
  7. 字母组合电话号码(DFS)
  8. 恢复 IP 地址
  9. 因素组合(DFS)

10. 数学

解决数学问题通常需要我们从观察中找到规律性或重复模式。如果您没有任何想法,请首先列出一小部分数字的结果。

  1. 反转整数
  2. 回文编号
  3. Pow(x, n):Pow(x, n)2 的幂3 的幂4 的幂
  4. 子集
  5. 子集 II
  6. 分数循环小数 [Google]
  7. Excel 工作表列号
  8. Excel 工作表列标题
  9. 阶乘尾随零
  10. 快乐数
  11. 计数素数
  12. 加一
  13. 除以两个整数
  14. 乘法字符串
  15. 一行上的最大点
  16. 数组乘积(自我除外)
  17. 整数中断
  18. 添加数字
  19. 丑数:III超级丑数求和最小的 K 对

11. HashMap

  1. 最短单词距离 II

附加问题:

  1. 自穿越
  2. 修补阵列
  3. 尼姆游戏
  4. 灯泡切换器
  5. 止痛栅栏
  6. 嵌套列表权重总和

其他资源

  1. 将您的代码共享到 Github / BitBucket

相关文章

  1. LeetCode – Word Ladder II(Java)
  2. 如何回答面试中的编码问题?
  3. LeetCode – LRU 缓存(Java)
  4. LeetCode – 自我后的小数计数(Java)

说明: 本文引用的部分题目链接及内容源自 2012-2016 年期间的整理,部分 LeetCode 题目编号或名称可能随平台更新有所变化,但核心算法思想与分类依然具有参考价值。