树与字典树#
二叉树遍历(递归/前中后序/层序)、最大深度、对称判定、路径、二叉搜索树操作、树状数组与字典树模板。
树#
二叉树的递归遍历#
vector<int> ans;
vector<int> traverse(TreeNode* root) {
if (root == nullptr) {
return;
}
// ans.push_back(root->val); // 前序遍历
traverse(root->left);
ans.push_back(root->val); // 中序遍历
traverse(root->right);
// ans.push_back(root->val); // 后序遍历
return ans;
}
void traverse(TreeNode root) {
if (root == null)
return;
// TODO: 前序遍历代码
traverse(root.left);
// TODO: 中序遍历代码
traverse(root.right);
// TODO: 后序遍历代码
return;
}
二叉树的前序遍历#
vector<int> preorderTraversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> ans;
stack<TreeNode*> stk;
TreeNode* curr = root;
stk.push(root);
while (!stk.empty()) {
curr = stk.top();
stk.pop();
ans.push_back(curr->val);
if (curr->right != nullptr) { // 先右后左
stk.push(curr->right);
}
if (curr->left != nullptr) {
stk.push(curr->left);
}
}
return ans;
}
void preorder(TreeNode root) {
if (root == null)
return;
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode cur = stack.peek();
stack.pop();
// ans.add(cur.val);
if (cur.right != null) { // 先右后左
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
二叉树的中序遍历#
vector<int> inorderTraversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> ans;
stack<TreeNode*> stk;
TreeNode* curr = root;
while (curr != nullptr || !stk.empty()) {
if (curr != nullptr) {
stk.push(curr);
curr = curr->left;
} else {
curr = stk.top();
stk.pop();
ans.push_back(curr->val);
curr = curr->right;
}
}
return ans;
}
void inorder(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while (cur != null || !stack.isEmpty()) {
if (cur != null) {
stack.push(cur);
cur = cur.left;
} else {
cur = stack.peek();
stack.pop();
// ans.add(cur.val);
cur = cur.right;
}
}
}
二叉树的后序遍历#
// 后序遍历代码和前序遍历代码几乎一样,有 2 点区别
vector<int> postorderTraversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> ans;
stack<TreeNode*> stk;
TreeNode* curr = root;
stk.push(root);
while (!stk.empty()) {
curr = stk.top();
stk.pop();
ans.push_back(curr->val);
if (curr->left != nullptr) { // 区别 1:先左后右
stk.push(curr->left);
}
if (curr->right != nullptr) {
stk.push(curr->right);
}
}
reverse(ans.begin(), ans.end()); // 区别 2:reverse
return ans;
}
void postorder(TreeNode root) {
if (root == null)
return;
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
stack.push(root);
while (!stack.isEmpty()) {
cur = stack.peek();
stack.pop();
// ans.add(cur.val);
if (cur.left != null) { // 先左后右
stack.push(cur.left);
}
if (cur.right != null) {
stack.push(cur.right);
}
}
// reverse(ans); // 记得 reverse
}
二叉树的层序遍历#
vector<vector<int>> levelOrder(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<vector<int>> ans;
deque<TreeNode*> dq;
TreeNode* curr = root;
dq.push_back(root);
while (!dq.empty()) {
int sz = dq.size();
vector<int> lvl;
while (sz--) {
curr = dq.front();
dq.pop_front();
lvl.push_back(curr->val);
if (curr->left != nullptr) {
dq.push_back(curr->left);
}
if (curr->right != nullptr) {
dq.push_back(curr->right);
}
}
ans.push_back(lvl);
}
return ans;
}
Queue<Integer> queue = new LinkedList<>();
void traverse(TreeNode root) {
if (root != null)
queue.offer(root);
else
return;
TreeNode p = queue.poll();
while (p != null) {
if (p.left != null) {
queue.offer(p.left);
}
if (p.right != null) {
queue.offer(p.right);
}
if (!queue.isEmpty()) {
p = queue.poll();
}
}
}
二叉树的最大深度#
int maxDepth(TreeNode* root) {
if (root == nullptr) {
return 0;
}
int left = maxDepth(root->left) + 1;
int right = maxDepth(root->right) + 1;
return left > right ? left : right;
}
public int maxDepth(TreeNode root) {
if (root == null)
return 0;
int left = maxDepth(root.left) + 1;
int right = maxDepth(root.right) + 1;
return left > right ? left : right;
}
对称二叉树的判定#
bool compare(TreeNode* left, TreeNode* right) {
if (left == nullptr && right == nullptr) {
return true;
} else if (left != nullptr && right == nullptr) {
return false;
} else if (left == nullptr && right != nullptr) {
return false;
} else if (left->val != right->val) {
return false;
}
bool oo = compare(left->left, right->right); // 对比外侧
bool ii = compare(left->right, right->left); // 对比内侧
return oo && ii;
}
bool isSymmetric(TreeNode* root) {
if (root == nullptr) {
return true;
}
return compare(root->left, root->right);
}
public boolean isSymmetric(TreeNode root) {
if (root == null)
return true;
return compare(root.left, root.right);
}
public boolean compare(TreeNode left, TreeNode right) {
if (left == null && right != null)
return false;
else if (left != null && right == null)
return false;
else if (left == null && right == null)
return true;
else if (left.val != right.val)
return false;
// 后序遍历代码如下:
// 比较外侧是否相同
boolean outside = compare(left.left, right.right);
// 比较内侧是否相同
boolean inside = compare(left.right, right.left);
return outside && inside;
}
二叉树的所有路径#
// path 变量的值,系统栈会自动帮我们维护,不管是压栈还是出栈
// paths 必须是引用,这个变量是需要我们手工维护的
void dfs(TreeNode* root, string path, vector<string>& paths) {
if (root == nullptr) {
return;
}
// 构造路径
path += to_string(root->val);
if (root->left == nullptr && root->right == nullptr) {
paths.push_back(path);
}
path += "->";
dfs(root->left, path, paths);
dfs(root->right, path, paths);
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> paths;
dfs(root, "", paths); // 为了把 paths 保存在栈里,所以把 paths 当做参数传递
return paths;
}
二叉搜索树中的插入操作#
TreeNode* insertIntoBST(TreeNode* root, int val) {
// 一定会到叶子结点上,因为树上的所有的值都不相等,而连接两个节点的值又是邻值
if (root == nullptr) {
return new TreeNode(val);
}
if (root->val > val) {
root->left = insertIntoBST(root->left, val);
} else {
root->right = insertIntoBST(root->right, val);
}
return root;
}
二叉搜索树中的删除操作#
TreeNode* deleteNode(TreeNode* root, int key) {
// 递归终止条件 1
if (root == nullptr) {
return nullptr;
}
// 递归:将其转为更小的问题(将递归放在递归终止条件 2 前面会提高效率)
if (root->val < key) {
root->right = deleteNode(root->right, key);
}
if (root->val > key) {
root->left = deleteNode(root->left, key);
}
// 递归终止条件 2:考虑删除节点相等的情况,如何调整树的结构
if (root->val == key) {
// case 1: 左子树为空
if (root->left == nullptr) {
return root->right;
}
// case 2: 右子树为空
if (root->right == nullptr) {
return root->left;
}
// case3: 左子树和右子树都为空的情况,包含在 case1 或 case2 了
// case4: 若左子树和右子树都不为空
// 那么,将要删除的节点的左子树挂在右子树的最左子节点的左子树上
TreeNode* p = root->right; // 找到右子树的最左子节点
while (p->left != nullptr) {
p = p->left;
}
p->left = root->left; // 重新挂载左子树
root = root->right;
return root;
}
return root;
}
裁剪二叉搜索树#
TreeNode* trimBST(TreeNode* root, int low, int high) {
if (root == nullptr) {
return nullptr;
}
if (root->val < low) { // 裁剪右子树
return trimBST(root->right, low, high);
}
if (root->val > high) { // 裁剪左子树
return trimBST(root->left, low, high);
}
// 下面两个递归其实并没有对节点进行改动,只是遍历
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
树状数组#
树状数组(Fenwick Tree)利用 lowbit(x) = x & -x 对下标分组,支持 \(O(\log n)\) 的单点更新与前缀和查询。与线段树相比代码更短、常数更小;不支持区间更新(需配合差分)与区间最值。
核心:tree[i] 维护区间 [i - lowbit(i) + 1, i] 的和。
struct Fenwick {
int n;
vector<int> tree;
Fenwick(int n) : n(n), tree(n + 1) {}
void add(int i, int v) { // 单点加 v(下标从 1 开始)
for (; i <= n; i += i & -i)
tree[i] += v;
}
int pre(int i) { // 前缀和 [1, i]
int s = 0;
for (; i > 0; i -= i & -i)
s += tree[i];
return s;
}
int rangeSum(int l, int r) { return pre(r) - pre(l - 1); } // 区间和
};
Note
下标必须从 1 开始,原数组
a[0..n-1]映射为i = idx + 1;求区间和用前缀和相减;配合差分数组可把”区间加、单点查”转为”单点加、前缀查”;
需要动态求第 k 小元素时,可在树状数组上做倍增/二分(权值树状数组)。
字典树#
字典树模板:
#include <bits/stdc++.h>
using namespace std;
class TrieNode {
public:
unordered_map<char, TrieNode*> children;
string word = ""; // 如果该节点是一个完整单词结尾,则保存该单词
};
class Trie {
public:
TrieNode* root = new TrieNode();
// 插入单词到 Trie 中
void insertWord(string& word) {
TrieNode* node = root;
for (char c : word) {
if (!node->children.count(c)) {
node->children[c] = new TrieNode();
}
node = node->children[c];
}
node->word = word;
}
};
int main() {
string words[] = {"oath", "pea", "eat", "rain"};
Trie trie;
// 构建 Trie
for (string& word : words) {
trie.insertWord(word);
}
return 0;
}