本文转载自:Java程序员面试需要注意啥?面试常见手撕模板题以及笔试模板总结
一. 目录
- 排序
- 二分
- 二叉树非递归遍历
- 01背包
- 最长递增子序列
- 最长公共子序列
- 最长公共子串
- 大数加法
- 大数乘法
- 大数阶乘
- 全排列
- 子集
- N皇后
- 并查集
- 树状数组
- 线段树
- 字典树
- 单调栈
- 单调队列
- KMP
- Manacher算法
- 拓扑排序
- 最小生成树
- 最短路
- 欧拉回路
- GCD和LCM
- 素数筛法
- 唯一分解定理
- 乘法快速幂
- 矩阵快速幂
二. 面试常见手撕模板题以及笔试模板总结
0. Java快速输入
先给一个干货,可能有些题用Java会超时(很少),下面是Petr刷题时的模板,一般用了这个就不会出现C++能过Java不能过的情况了。
import java.io.*;
import java.util.*;
public class Main {
static class FR {
BufferedReader br;
StringTokenizer tk;
FR(InputStream stream) {
br = new BufferedReader(new InputStreamReader(stream), 32768);
tk = null;
}
String next() {
while (tk == null || !tk.hasMoreElements()) {
try {
tk = new StringTokenizer(br.readLine());
}
catch (IOException e) {
e.printStackTrace();
}
}
return tk.nextToken();
}
int nextint() {
return Integer.parseint(next());
}
}
static void solve(InputStream stream, PrintWriter out) {
FR in = new FR(stream);
// start code.....
}
public static void main(String[] args) {
OutputStream os = System.out;
InputStream is = System.in;
PrintWriter out = new PrintWriter(os);
solve(is, out);
out.close();
// 不关闭就没有输出
}
}
1. 排序
先给出一个swap函数,代表交换数组两个位置的值,很多排序用到这个函数:
static void swap(int[] arr, int a, int b){
int t = arr[a];
arr[a] = arr[b];
arr[b] = t;
}
面试主要考察比较排序(O(N^2)、O(NlogN))排序(非比较排序可以看下面的详细总结)。
①. 冒泡
static void bubbleSort(int[] arr){
for (int end = arr.length - 1; end > 0; end--){
Boolean isSort = true;
for (int i = 0; i < end; i++){
if(arr[i] > arr[i+1]) {
swap(arr, i, i + 1);
isSort = false;
}
}
if(isSort) break;
}
}
②. 选择
static void selectSort(int[] arr){
for (int i = 0; i < arr.length; i++){
int minIdx = i;
for (int j = i + 1; j < arr.length; j++) minIdx = arr[j] < arr[minIdx] ? j : minIdx;
swap(arr, minIdx, i);
}
}
③. 插入
// 几个边界: i=1开始(不是必须)、j >= 0, arr[j+1] = key注意一下
static void insertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int key = arr[i], j;
for (j = i - 1; j >= 0 && arr[j] > key; j--) arr[j + 1] = arr[j];
arr[j + 1] = key;
}
}
第二种写法:
// 边界 j > 0
static void insertSort2(int[] arr) {
for (int i = 1; i < arr.length; i++) {
for (int j = i; j > 0 && arr[j - 1] > arr[j]; j--) swap(arr, j, j - 1);
}
}
二分插入排序:
// 注意 R = i-1,注意找第一个>=key的,注意arr[i]先用key保存
static void binaryInsertSort(int[] arr) {
for (int i = 1; i < arr.length; i++) {
int L = 0, R = i - 1;
// 找第一个大于的 二分边界搞不清的看下面的二分链接
int key = arr[i];
while (L <= R) {
int m = L + (R - L) / 2;
if (arr[m] > arr[i]) {
R = m - 1;
} else {
L = m + 1;
}
}
for (int j = i - 1; j >= L; j--) arr[j + 1] = arr[j];
arr[L] = key;
}
}
④. 希尔排序
采取的是增量序列每次减半的策略。
static void shellSort(int[] arr) {
for (int g = arr.length; g > 0; g /= 2) { // 增量序列 gap
for (int end = g; end < arr.length; end++) { // 每一个组的结束元素, 从数组第gap个元素开始
// 每组做插入排序
int key = arr[end], i;
for (i = end - g; i >= 0 && key < arr[i]; i -= g) arr[i + g] = arr[i];
arr[i + g] = key;
}
}
}
⑤. 快排
给出的是三路快排
static void quickSort(int[] arr){
if(arr == null || arr.length == 0) return;
quickRec(arr, 0, arr.length - 1);
}
static void quickRec(int[] arr, int L, int R) {
if (L >= R) return;
swap(arr, L, L + (int) (Math.random() * (R - L + 1)));
int[] p = partition(arr, L, R);
quickRec(arr, L, p[0] - 1);
quickRec(arr, p[1] + 1, R);
}
// 用arr[L]作为划分点
static int[] partition(int[] arr, int L, int R) {
int key = arr[L];
int less = L, more = R + 1;
int cur = L + 1;
while (cur < more) {
if (arr[cur] < key) {
swap(arr, ++less, cur++);
} else if (arr[cur] > key) {
swap(arr, --more, cur);
} else {
cur++;
}
}
swap(arr, L, less);
// 返回相等的两个下标, less位置是我最后交换过来的划分值,more位置是>的,所以返回more-1
return new int[]{less, more - 1};
}
⑥. 归并排序
static void mergeSort(int[] arr){
if(arr == null || arr.length == 0) return;
mergeRec(arr, 0, arr.length - 1);
}
//注意是mergeSort(arr, L, m); 不是mergeSort(arr, L, m-1)
static void mergeRec(int[] arr, int L, int R) {
if (L >= R) return;
int m = L + (R - L) / 2;
mergeRec(arr, L, m);
mergeRec(arr, m + 1, R);
merge(arr, L, m, R);
}
static void merge(int[] arr, int L, int mid, int R) {
int[] h = new int[R - L + 1];
int p1 = L, p2 = mid + 1;
int k = 0;
while (p1 <= mid && p2 <= R)
h[k++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++]; // 注意保证稳定性
while (p1 <= mid) h[k++] = arr[p1++];
while (p2 <= R) h[k++] = arr[p2++];
for (int i = 0; i < k; i++) arr[L + i] = h[i];
}
非递归归并排序:
static void mergeSortBU(int[] arr) {
for (int sz