不定时更新,收藏即可
遍历
层序遍历即宽搜,前中后序遍历即深搜
二叉树的前序遍历
递归
vector<int> res;
vector<int> preorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if(!root) return;
res.push_back(root->val);
dfs(root->left);
dfs(root->right);
}
非递归
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
if (!root) return res;
stack<TreeNode*> stk;
stk.push(root);
while (stk.size()) {
root = stk.top(); stk.pop();
res.push_back(root->val);
if (root->right) stk.push(root->right);
if (root->left) stk.push(root->left);
}
return res;
}
二叉树的中序遍历
递归
vector<int> res;
vector<int> inorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if(!root) return;
dfs(root->left);
res.push_back(root->val);
dfs(root->right);
}
非递归
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
while (root || stk.size()) {
while (root) stk.push(root), root = root->left;
if (stk.size()) {
root = stk.top(); stk.pop();
res.push_back(root->val);
root = root->right;
}
}
return res;
}
二叉树的后序遍历
递归
vector<int> res;
vector<int> postorderTraversal(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if(!root) return;
dfs(root->left);
dfs(root->right);
res.push_back(root->val);
}
非递归
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
if (!root) return res;
stack<TreeNode*> s1, s2;
s1.push(root);
while (s1.size()) {
root = s1.top(); s1.pop();
if (root->left) s1.push(root->left);
if (root->right) s1.push(root->right);
s2.push(root);
}
while (s2.size()) res.push_back(s2.top()->val), s2.pop();
return res;
}
二叉树的层序遍历
自顶向下层序遍历
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (q.size()) {
vector<int> level;
int len = q.size();
while (len--) {
auto x = q.front(); q.pop();
level.push_back(x->val);
if (x->left) q.push(x->left);
if (x->right) q.push(x->right);
}
res.push_back(level);
}
return res;
}
自底向上层序遍历
vector<vector<int>> levelOrderBottom(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while (q.size()) {
vector<int> level;
int len = q.size();
while (len--) {
auto x = q.front(); q.pop();
level.push_back(x->val);
if (x->left) q.push(x->left);
if (x->right) q.push(x->right);
}
res.push_back(level);
}
reverse(res.begin(), res.end());
return res;
}
锯齿形层序遍历
vector<vector<int>> zigzagLevelOrder(TreeNode* root) {
vector<vector<int>> res;
if (!root) return res;
queue<TreeNode*> q; q.push(root);
bool st = false;
while (q.size()) {
vector<int> level;
int len = q.size();
while (len--) {
auto x = q.front(); q.pop();
level.push_back(x->val);
if (x->left) q.push(x->left);
if (x->right) q.push(x->right);
}
if (st) reverse(level.begin(), level.end());
st = !st;
res.push_back(level);
}
return res;
}
N 叉树的层序遍历
跟二叉树的层序遍历一毛一样,本质都是把所有儿子塞进去
-
二叉树只有两个子节点,所以只要塞左右儿子就行
-
N 叉树具有多个子节点,所以要塞入 N 个儿子
vector<vector<int>> levelOrder(Node* root) {
vector<vector<int>> res;
if (!root) return res;
queue<Node*> q;
q.push(root);
while (q.size()) {
vector<int> level;
int len = q.size();
while (len--) {
auto x = q.front(); q.pop();
level.push_back(x->val);
for (auto child : x->children) q.push(child); // 塞入所有儿子
}
res.push_back(level);
}
return res;
}
验证
验证二叉搜索树
法一:定义
利用二叉搜索树定义判断,即左小、右大、左右均是二叉搜索树
typedef long long LL;
bool isValidBST(TreeNode* root) {
return dfs(root, LONG_MIN, LONG_MAX);
}
bool dfs(TreeNode* root, LL l, LL r) {
if (!root) return true;
LL x = root->val;
if (x < l || x > r) return false;
return x > l && x < r
&& dfs(root->left, l , x) && dfs(root->right, x, r);
}
法二:性质
利用二叉搜索树性质判断,即二叉搜索中序遍历是一个升序序列
long long pre = LONG_MIN;
bool res = true;
bool isValidBST(TreeNode* root) {
dfs(root);
return res;
}
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
if (root->val <= pre) res = false;
pre = root->val;
dfs(root->right);
}
相同的树
同时对两颗树进行遍历
- 如果一棵树遍历完了,另外一棵还有节点。那么一定不相同
- 遍历时两个数的节点的值不同。那么一定不相同
bool isSameTree(TreeNode* p, TreeNode* q) {
if (!p || !q) return q == p;
if (p->val != q->val) return false;
return isSameTree(p->left, q->left) && isSameTree(p->right, q->right);
}
对称二叉树
将一棵树当作两棵树进行上一题的操作
bool isSymmetric(TreeNode* root) {
return isSameTree(root, root);
}
bool isSameTree(TreeNode* p, TreeNode* q) {
if (!p || !q) return q == p;
if (p->val != q->val) return false;
return isSameTree(p->left, q->right) && isSameTree(p->right, q->left); // 注意,两个二叉树是相反的
}
平衡二叉树
// 按照定义去做,如果不满足定义直接 -1 标记,满足则返回高度即可
bool isBalanced(TreeNode* root) {
return dfs(root) != -1;
}
int dfs(TreeNode* root) {
if (!root) return 0;
int left = dfs(root->left), right = dfs(root->right);
if (left == -1 || right == -1 || abs(left - right) > 1) return -1;
return max(left, right) + 1;
}
奇偶树
bool isEvenOddTree(TreeNode* root) {
queue<TreeNode*> q;;
q.push(root);
bool odd = false;
while (q.size()) {
auto x = q.front();
int pre = odd ? INT_MAX : INT_MIN;
for (int i = q.size(); i; i--) {
x = q.front(); q.pop();
if (x->left) q.push(x->left);
if (x->right) q.push(x->right);
if (odd) {
if (pre <= x->val || x->val & 1)
return false;
} else {
if (pre >= x->val || !(x->val & 1))
return false;
}
pre = x->val;
}
odd = !odd;
}
return true;
}
统计
二叉树的最大深度
int maxDepth(TreeNode* root) {
if (!root) return 0;
return max(maxDepth(root->left), maxDepth(root->right)) + 1; // 左子树和右子树最大深度+1
}
二叉树的最小深度
int minDepth(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return 1;
int res = INT_MAX;
if (root->left) res = min(res, minDepth(root->left));
if (root->right) res = min(res, minDepth(root->right));
return res + 1;
}
完全二叉树的节点个数
二分
- 左右是完全二叉树,直接算 2 h − 1 2^h - 1 2h−1
- 左右都是满二叉树,直接算 左 + 右 + 1 左+右 +1 左+右+1
- 左右有一个不是满二叉树,则只需要递归非满二叉树部分
int countNodes(TreeNode* root) {
if (!root) return 0;
auto left = root->left, right = root->right;
int l = 1, r = 1;
while (left) left = left->left, l++;
while (right) right = right->right, r++;
if (l == r) return (1 << l) - 1;
return countNodes(root->left) + countNodes(root->right) + 1;
}
二叉搜索树中第K小的元素
左小右大的二叉搜索树,中序遍历一定是升序的,所以只要返回中序遍历的第 K 个元素即可
int kthSmallest(TreeNode* root, int k) {
dfs(root, k);
return res;
}
int res;
bool dfs(TreeNode* root, int& k) { // 返回类型标记是否找到,找到了直接剪掉即可
if (!root) return false;
if (dfs(root->left, k)) return true;
if (--k == 0) {
res = root->val;
return true;
}
if (dfs(root->right, k)) return true;
return false;
}
左叶子之和
直接遍历统计,当为左叶子时才计数
左叶子:是左子树,且无左右子树
int sumOfLeftLeaves(TreeNode* root) {
dfs(root);
return res;
}
int res;
void dfs(TreeNode* root) {
if (!root) return;
if (root->left)
if (!root->left->right && !root->left->left) res += root->left->val;
dfs(root->left), dfs(root->right);
}
二叉搜索树中的众数
二叉搜索中序遍历是有序的,所以相同的数会聚集在一块
vector<int> findMode(TreeNode* root) {
dfs(root);
return res;
}
vector<int> res;
int pre, cnt, maxv;
void dfs(TreeNode* root) {
if (!root) return;
dfs(root->left);
if (!cnt || pre == root->val) cnt++;
else cnt = 1;
pre = root->val;
if (cnt > maxv) maxv = cnt, res = {pre};
else if (cnt == maxv) res.push_back(pre);
dfs(root->right);
}
找树左下角的值
宽搜,按行拿第一个
int findBottomLeftValue(TreeNode* root) {
int res;
queue<TreeNode*> q;
q.push(root);
while(!q.empty()){
auto x = q.front();
res = x->val;
for(int i = q.size(); i; i--){
x = q.front(); q.pop();
if(x->left) q.push(x->left);
if(x->right) q.push(x->right);
}
}
return res;
}
深搜,先左再右,则左绝对是第一个在该层第一个搜索到的
int findBottomLeftValue(TreeNode* root) {
dfs(root, 1);
return res;
}
int res, maxv;
void dfs(TreeNode* root, int d) {
if (!root) return;
if (d > maxv) {
maxv = d;
res = root->val;
}
dfs(root->left, d + 1), dfs(root->right, d + 1);
}
在每个树行中找最大值
宽搜,和上题一毛一样的思路
vector<int> largestValues(TreeNode* root) {
vector<int> res;
if (!root) return res;
queue<TreeNode*> q;
q.push(root);
while(q.size()) {
auto x = q.front();
int v = INT_MIN;
for (int i = q.size(); i; i--) {
x = q.front(); q.pop();
v = max(v, x->val);
if (x->left) q.push(x->left);
if (x->right) q.push(x->right);
}
res.push_back(v);
}
return res;
}
不同的二叉搜索树的数量
这是个结论,卡特兰数,记住就好
// 卡特兰数通项公式:C0 = 1, Cn+1 = 2 * (2n + 1) / (n + 2) * Cn
int numTrees(int n) {
int C = 1;
for (int i = 0; i < n; ++i) {
C = 1L * C * 2 * (2 * i + 1) / (i + 2);
}
return C;
}
打家劫舍 III
int rob(TreeNode* root) {
auto res = dfs(root);
return max(res.first, res.second);
}
// 方案一:偷了 -> 下层不能偷
// 方案二:不偷 -> 下层能偷,按偷最多钱的方案偷
typedef pair<int, int> PII;
PII dfs(TreeNode* root) {
if (!root) return {0, 0};
auto l = dfs(root->left);
auto r = dfs(root->right);
// 方案一
int first = root->val + l.second + r.second;
// 方案二
int second = max(l.first, l.second) + max(r.first, r.second);
return {first, second};
}
路径
路径总和
遍历,每次目标值减去当前值。当没有子节点时,判断目标值是否刚好被剪光。只要有一条路径满足即可
bool hasPathSum(TreeNode* root, int t) {
if (!root) return false;
t -= root->val;
if (!root->left && !root->right) return !t;
return hasPathSum(root->right, t) || hasPathSum(root->left, t);
}
路径总和 II
深搜即可
vector<int> path;
vector<vector<int>> res;
vector<vector<int>> pathSum(TreeNode* root, int t) {
dfs(root, t);
return res;
}
void dfs(TreeNode* root, int t) {
if (!root) return;
path.push_back(root->val);
t -= root->val;
if (!root->left && !root->right && !t) res.push_back(path);
dfs(root->left, t), dfs(root->right, t);
path.pop_back(); // 恢复现场
}
路径总和 III
这个路径是不拐弯的
前缀和 + 哈希表优化。和这道题一毛一样的思想 和为K的子数组
前缀和 s[i] - s[j] = k
等价变形 s[j] = s[i] - k
,只需要将 s[j]
作为 key
存入哈希表中,其 value
就是满足 s[j] = s[i] - k
等式的个数
unordered_map<int, int> map;
int res, sum;
int pathSum(TreeNode* root, int targetSum) {
map[0] = 1, sum = targetSum;
dfs(root, 0);
return res;
}
void dfs(TreeNode* root, int cur) {
if (!root) return;
cur += root->val;
res += map[cur - sum];
map[cur]++;
dfs(root->left, cur), dfs(root->right, cur);
map[cur]--;
}
二叉树中的最大路径和
每个节点都会产生最大值,但是最大值一定是左边最大+右边最大+当前值,所以只需要遍历到每个节点时抓一下。后序遍历是先到两个子节点再到根节点,所以用后序遍历汇总
int maxPathSum(TreeNode* root) {
dfs(root);
return res;
}
int res = INT_MIN; // 抓答案
int dfs(TreeNode* root) {
if (!root) return 0;
int left = max(dfs(root->left), 0);
int right = max(dfs(root->right), 0);
res = max(left + right + root->val, res);
return max(left, right) + root->val;
}
构造
二叉树展开为链表
// 有左儿子,跑到左儿子的最右记为 p
// 1. p->right = root->right
// 2. root->right = p->left
// 3. root->left = nulll
// 跳到下一个左儿子继续
void flatten(TreeNode* root) {
while (root) {
auto p = root->left;
if (p) {
while (p->right) p = p->right;
p->right = root->right;
root->right = root->left;
root->left = nullptr;
}
root = root->right;
}
}
将有序数组转换为二叉搜索树
TreeNode* sortedArrayToBST(vector<int>& a) {
int n = a.size();
if (!n) return nullptr;
return dfs(a, 0, n - 1);
}
TreeNode* dfs(vector<int>& a, int l, int r) {
if (l > r) return nullptr;
int mid = l + r + 1>> 1;
auto root = new TreeNode();
root->left = dfs(a, l, mid - 1);
root->val = a[mid];
root->right = dfs(a, mid + 1, r);
return root;
}
有序链表转换二叉搜索树
中序建树 + 二分找中间节点
TreeNode* sortedListToBST(ListNode* head) {
if (!head) return nullptr;
return dfs(head, 0, getLen(head) - 1);
}
int getLen(ListNode* head) {
int len = 0;
while (head) head = head->next, len++;
return len;
}
TreeNode* dfs(ListNode* &head, int l, int r) {
if (l > r) return nullptr;
int mid = l + r + 1>> 1;
auto root = new TreeNode();
root->left = dfs(head, l, mid - 1);
root->val = head->val, head = head->next;
root->right = dfs(head, mid + 1, r);
return root;
}
翻转二叉树
利用后序遍历特性,即先访问子再访问根
TreeNode* invertTree(TreeNode* root) {
if (!root) return nullptr;
auto l = invertTree(root->left), r = invertTree(root->right);
root->left = r, root->right = l;
return root;
}
填充每个节点的下一个右侧节点指针
给的是一个满二叉树,所以只需要从每一层的最左节点开始将 next
指向好就行
Node* connect(Node* root) {
if (!root) return root;
for (auto cur = root; cur->left; cur = cur->left) {
for (auto p = cur; p; p = p->next) {
p->left->next = p->right;
if(p->next) p->right->next = p->next->left;
}
}
return root;
}
填充每个节点的下一个右侧节点指针 II
遍历到这一层时自己维护下一层的链表
Node* connect(Node* root) {
if (!root) return root;
auto cur = root;
while (cur) {
auto head = new Node(), tail = head;
for (auto p = cur; p; p = p->next) {
if (p->left) tail = tail->next = p->left;
if (p->right) tail = tail->next = p->right;
}
cur = head->next;
}
return root;
}
序列化
从前序与中序遍历序列构造二叉树
前序第一个元素一定是头。然后跑到中序序列找到头的位置,在其头的左边是左子树所有元素,在其头的右边是右子树所有元素
unordered_map<int, int> pos;
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
int n = inorder.size(), m = preorder.size();
for (int i = 0; i < n; i++) pos[inorder[i]] = i;
return dfs(preorder, inorder, 0, m - 1, 0, n - 1);
}
TreeNode* dfs(vector<int>& preorder, vector<int>& inorder, int pl, int pr, int il, int ir) {
if (pl > pr) return nullptr;
auto root = new TreeNode(preorder[pl]);
int k = pos[root->val];
root->left = dfs(preorder, inorder, pl + 1, pl + 1 + k - 1 - il, il, k - 1);
root->right = dfs(preorder, inorder, pl + 1 + k - 1 - il + 1, pr, k + 1, ir);
return root;
}
从中序与后序遍历序列构造二叉树
后序最后一个元素一定是头。然后跑到中序序列找到头的位置,在其头的左边是左子树所有元素,在其头的右边是右子树所有元素
unordered_map<int, int> pos;
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
int n = inorder.size(), m = postorder.size();
for (int i = 0; i < n; i++) pos[inorder[i]] = i;
return dfs(inorder, postorder, 0, n - 1, 0, m - 1);
}
TreeNode* dfs(vector<int>& inorder, vector<int>& postorder, int il, int ir, int pl, int pr) {
if (pl > pr) return nullptr;
auto root = new TreeNode(postorder[pr]);
int k = pos[root->val];
root->left = dfs(inorder, postorder, il, k - 1, pl, pl + k - 1 - il);
root->right = dfs(inorder, postorder, k + 1, ir, pl + k - 1 - il + 1, pr - 1);
return root;
}
验证二叉树的前序序列化
消耗插槽法
bool isValidSerialization(string s) {
int n = s.length(), i = 0, cnt = 1;
while (i < n) {
if (!cnt) return false;
if (s[i] == ',') i++;
else if (s[i] == '#') cnt--, i++;
else {
while (i < n && s[i] != ',') i++;
cnt++;
}
}
return !cnt;
}
用图的方式思考:出度入度法
#define check(c) c <= '9' && c >= '0'
bool isValidSerialization(string s) {
int out = 0, in = -1, n = s.size();
for(int i = 0; i < n; i++){
if(s[i]==',') continue;
in++;
if(out < in) return false;
if(check(s[i])){
out += 2;
while(i < n - 1 && check(s[i + 1])) i++;
}
}
return out == in;
}
序列化和反序列化二叉搜索树
拿前序遍历为例,前序拿到的元素必然是某棵子树的头,但是左右子树是多少你不知道。但 BST 的可以划分为两个区间,通过不停的收缩区间来确定值。
class Codec {
public:
// Encodes a tree to a single string.
string serialize(TreeNode* root) {
string res;
dfs_s(root, res);
return res;
}
void dfs_s(TreeNode* root, string& res) {
if (!root) return;
res += to_string(root->val) + '#';
dfs_s(root->left, res), dfs_s(root->right, res);
}
// Decodes your encoded data to tree.
TreeNode* deserialize(string data) {
vector<int> pre = split(data, '#');
int u = 0;
return dfs_d(pre, u, INT_MIN, INT_MAX);
}
TreeNode* dfs_d(vector<int>& pre, int& u, int l, int r) {
if (u == pre.size() || pre[u] < l || pre[u] > r) return nullptr;
auto root = new TreeNode(pre[u++]);
root->left = dfs_d(pre, u, l, root->val);
root->right = dfs_d(pre, u, root->val + 1, r);
return root;
}
vector<int> split(string data, char c) {
vector<int> res;
int n = data.size();
for (int i = 0; i < n; i++) {
int num = 0;
while (data[i] != c) {
num *= 10;
num += data[i] - '0';
i++;
}
res.push_back(num);
}
return res;
}
};