算法基础面试题(三)
二叉树
//迭代实现前序遍历
// 前序单层循环,要先压入右子节点,再压入左子节点
public static void preOrder1(TreeNode root) {
if (root == null) return;
Deque<TreeNode> stack = new LinkedList<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
System.out.print(node.val + " ");
if (node.right != null) stack.push(node.right);
if (node.left != null) stack.push(node.left);
}
}
//前序(迭代,双层循环) 内循环中压入子树的所有左子节点
public static void preOrder2(TreeNode root) { // 前序双层循环
if (root == null) return;
TreeNode node = root;
Deque<TreeNode> stack = new LinkedList<>();
while (!stack.isEmpty() || node != null) {
while (node != null) {
System.out.print(node.val + " ");
stack.push(node);
node = node.left;
}
node = stack.pop();
node = node.right;
}
}
//迭代实现后序遍历,先序遍历是中左右,后续遍历是左右中,那么我们只需要调整一下先序遍历的代码顺序,就变成中右左的遍历顺序,然后在反转result数组,输出的结果顺序就是左右中了
// 后序单层循环,对于单层循环方式,后序遍历可以采用一个讨巧的办法:逆序输出
public static void postOrder1(TreeNode root) { // 后序单层循环
if (root == null) return;
Deque<TreeNode> stack1 = new LinkedList<>();
Deque<TreeNode> stack2 = new LinkedList<>();
stack1.push(root);
while (!stack1.isEmpty()) {
TreeNode node = stack1.pop();
stack2.push(node);
if (node.left != null) stack1.push(node.left);
if (node.right != null) stack1.push(node.right);
}
while (!stack2.isEmpty()) {
System.out.print(stack2.pop().val + " ");
}
}
// 后序双层循环
public static void postOrder2(TreeNode root) { // 后序双层循环
if (root == null) return;
TreeNode node = root;
TreeNode prev = null;
Deque<TreeNode> stack = new LinkedList<>();
while (!stack.isEmpty() || node != null) {
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
//因为是后序,需要判断当前节点有没有右节点,或者当前节点的右节点已经遍历过了
if (node.right == null || node.right == prev) {
System.out.print(node.val + " ");
prev = node;
//设置为null,避免再次压入
node = null;
} else {
//如果右节点还没遍历,把中节点继续入栈。
stack.push(node);
node = node.right;
}
}
}
//迭代实现中序遍历
public static void inOrder(TreeNode root) { // 中序双层循环
if (root == null) return;
TreeNode node = root;
Deque<TreeNode> stack = new LinkedList<>();
while (!stack.isEmpty() || node != null) {
while (node != null) {
stack.push(node);
node = node.left;
}
node = stack.pop();
System.out.print(node.val + " ");
node = node.right;
}
}
前缀树
Trie,又称前缀树或字典树,是一棵有根树,其每个节点包含以下字段:
指向子节点的指针数组 children对于本题而言,数组长度为 26,即小写英文字母的数量。
布尔字段 isEnd 表示该节点是否为字符串的结尾。
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
public boolean search(String word) {
Trie node = searchPrefix(word);
return node != null && node.isEnd;
}
public boolean startsWith(String prefix) {
return searchPrefix(prefix) != null;
}
private Trie searchPrefix(String prefix) {
Trie node = this;
for (int i = 0; i < prefix.length(); i++) {
char ch = prefix.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
return null;
}
node = node.children[index];
}
return node;
}
}
class WordDictionary {
private Trie root;
public WordDictionary() {
root = new Trie();
}
public void addWord(String word) {
root.insert(word);
}
public boolean search(String word) {
return dfs(word, 0, root);
}
private boolean dfs(String word, int index, Trie node) {
if (index == word.length()) {
return node.isEnd();
}
char ch = word.charAt(index);
if (Character.isLetter(ch)) {
int childIndex = ch - 'a';
Trie child = node.getChildren()[childIndex];
if (child != null && dfs(word, index + 1, child)) {
return true;
}
} else {
for (int i = 0; i < 26; i++) {
Trie child = node.getChildren()[i];
if (child != null && dfs(word, index + 1, child)) {
return true;
}
}
}
return false;
}
}
class Trie {
private Trie[] children;
private boolean isEnd;
public Trie() {
children = new Trie[26];
isEnd = false;
}
public void insert(String word) {
Trie node = this;
for (int i = 0; i < word.length(); i++) {
char ch = word.charAt(i);
int index = ch - 'a';
if (node.children[index] == null) {
node.children[index] = new Trie();
}
node = node.children[index];
}
node.isEnd = true;
}
public Trie[] getChildren() {
return children;
}
public boolean isEnd() {
return isEnd;
}
}
图
图的遍历
并查集?
DFS、BFS 和 Floyd 算法
拓扑排序
拓扑排序常常应用于表示任务之间先后关系的有向无环图(DAG),例如任务调度、编译顺序等场景。
拓扑排序的经典算法有深度优先搜索(DFS)和广度优先搜索(BFS)。
DFS:
import java.util.*;
class Graph {
private int V;
private List<List<Integer>> adjList;
public Graph(int vertices) {
V = vertices;
adjList = new ArrayList<>(V);
for (int i = 0; i < V; i++) {
adjList.add(new LinkedList<>());
}
}
public void addEdge(int u, int v) {
adjList.get(u).add(v);
}
public List<Integer> topologicalSort() {
Stack<Integer> stack = new Stack<>();
boolean[] visited = new boolean[V];
for (int i = 0; i < V; i++) {
if (!visited[i]) {
topologicalSortUtil(i, visited, stack);
}
}
List<Integer> result = new ArrayList<>();
while (!stack.isEmpty()) {
result.add(stack.pop());
}
return result;
}
private void topologicalSortUtil(int v, boolean[] visited, Stack<Integer> stack) {
visited[v] = true;
for (Integer neighbor : adjList.get(v)) {
if (!visited[neighbor]) {
topologicalSortUtil(neighbor, visited, stack);
}
}
stack.push(v);
}
}
public class TopologicalSortExample {
public static void main(String[] args) {
Graph graph = new Graph(6);
graph.addEdge(5, 2);
graph.addEdge(5, 0);
graph.addEdge(4, 0);
graph.addEdge(4, 1);
graph.addEdge(2, 3);
graph.addEdge(3, 1);
List<Integer> result = graph.topologicalSort();
System.out.println("Topological Sort: " + result);
}
}
BFS:
public class BfsTopologicalSort {
static class Graph {
private int V;
private List<List<Integer>> adjList;
private int[] indeg;
public Graph(int vertices) {
V = vertices;
adjList = new ArrayList<>(V);
for (int i = 0; i < V; i++) {
adjList.add(new LinkedList<>());
}
indeg= new int[V];
}
public void addEdge(int u, int v) {
adjList.get(u).add(v);
indeg[v]++;
}
public List<Integer> bfsSort() {
Deque<Integer> qeue = new LinkedList<>();
for (int i = 0; i < V; i++) {
if(indeg[i]==0){
qeue.offer(i);
}
}
List<Integer> result=new ArrayList<>();
while(!qeue.isEmpty()){
int number= qeue.poll();
result.add(number);
for(Integer i: adjList.get(number)){
indeg[i]--;
if(indeg[i]==0){
qeue.offer(i);
}
}
}
return result;
}
}
public static void main(String[] args) {
Graph graph = new Graph(6);
//有向图,表示5在2之前
graph.addEdge(5, 2);
graph.addEdge(5, 0);
graph.addEdge(4, 0);
graph.addEdge(4, 1);
graph.addEdge(2, 3);
graph.addEdge(3, 1);
List<Integer> result = graph.bfsSort();
System.out.println("Topological Sort: " + result);
}
}
海量数据处理
Hash法
BIt-map法
Bloom filter法
数据库优化法
倒排索引法
外排序法
Trie树
堆
双层桶
Map-reduce法
TOP K问题
针对TOP K问题,通常使用分治+Trie树/hash+小顶堆。首先把数据集按照hash方法分解成多个小数据集,然后使用Tire树或者hash统计每个小数据集中的query词频,之后用小顶堆求出每个数据集中出现频率最高的前K个树,最后在所有的top K中求出最终的top K。