二叉树的操作模板
- 使用递归进行遍历、查找、插入、删除、判断合法性
二叉树的遍历
先序遍历
void preorder(TreeNode* root){
if(!root) return ;
nums.push_back(root->val);
preorder(root->left);
preorder(root->right);
}
中序遍历
void inorder(TreeNode* root){
if(!root) return ;
inorder(root->left);
nums.push_back(root->val);
inorder(root->right);
}
后序遍历
void postorder(TreeNode* root){
if(!root) return;
postorder(root->left);
postorder(root->right);
nums.push_back(root->val);
}
层次遍历
- 二叉树是无环的图,层次遍历使用BFS
- 可以使用 BFS+ Queue 来实现,也可以使用递归来实现
class Solution {
void levelOrder(TreeNode* node,vector<vector<int>>& res,int level){
if(!node) return ;
if(res.size() <= level) res.push_back({});
res[level].push_back(node->val);
levelOrder(node->left,res,level+1);
levelOrder(node->right,res,level+1);
}
typedef pair<TreeNode*,int> Pair;
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root) return {};
vector<vector<int>> res;
levelOrder(root,res,0);
return res;
}
};
class Solution {
typedef pair<TreeNode*,int> Pair;
public:
vector<vector<int>> levelOrder(TreeNode* root) {
if(!root) return {};
vector<vector<int>> res;
queue<Pair> vertex;
vertex.push({root,0});
while(vertex.size()){
auto cur = vertex.front();
vertex.pop();
auto node = cur.first;
if(!node) continue;
if(res.size()<=cur.second) res.push_back({});
res[cur.second].push_back(node->val);
vertex.push({node->left,cur.second+1});
vertex.push({node->right,cur.second+1});
}
return res;
}
};
宽度遍历
- 设节点的索引为i,则其左节点的索引为 ,右节点的索引为
- 所谓宽度遍历就是在层次遍历的时候,额外携带父节点的索引信息,则可以对每层的节点进行操作
class Solution {
vector<vector<double>> table;
int Height(TreeNode* root){
if(!root) return 0;
return max(Height(root->left),Height(root->right)) + 1;
}
void helper(TreeNode* root,int level,double index){
if(!root) return;
//记录每层中最左和最右节点的索引
if(table[level][0] == -1) table[level][0] = index;
else{
table[level][1] = index;
}
double m = index*2;
helper(root->left,level + 1,m);
helper(root->right,level + 1,m+1);
}
public:
int widthOfBinaryTree(TreeNode* root) {
int h = Height(root);
int size = pow(2,h);
table = vector<vector<double>>(h,vector<double>(2,-1));
helper(root,0,0);
double ans = 0;
//统计
for(auto& it : table){
if(it[1] == -1){
ans = max<double>(ans,1);
}else{
ans = max(ans,it[1] - it[0] + 1);
}
}
return ans;
}
};
- 可以发现,先/中/后序遍历其实就是什么时候对当前节点进行操作
- 先序: 先记录当前节点,在遍历左子树、右子树
- 中序: 先遍历左子树,再记录当前节点,再遍历右子树
- 后续: 先遍历左子树,再遍历右子树,最后记录当前节点
查找给定数字是否存在二叉树中
普通的二叉树
bool isExist(TreeNode* root,int target){
if(!root) return false;
if(root->val == target) return true;
return isExist(root->left,target) || isExist(root->right,target);
}
二叉搜索树
- 使用BST的性质,搜索时候进行二分
bool isExist(TreeNode* root,int target){
if(!root) return false;
if(root->va == target) return true;
else if(root->val < target)
return isExist(root->right,target);
else return isExist(root->left,left);
}
二叉搜索树中第K小元素
class Solution {
// cnt 来记录当前遍历的节点数目
int cnt = 0;
//val保存第k小的结果
int val;
void inorder(TreeNode* root,int k){
if(!root) return ;
inorder(root->left,k);
cnt++;
if(cnt == k){
val = root->val;
return;
}
inorder(root->right,k);
}
public:
int kthSmallest(TreeNode* root, int k) {
inorder(root,k);
return val;
}
};
BST的最小/最大值
TreeNode* Mininum(TreeNode* root){
if(!root) return nullptr;
if(!root->left) return root;
return Mininum(root->left);
}
TreeNode* Maxinum(TreeNode* root){
if(!root) return nullptr;
if(!root->right) return root;
return Maxinum(root->right);
}
BST 前驱/后继
TreeNode* Successor(TreeNode* root,int val){
if(!root) return nullptr;
auto* l = Successor(root->left,val);
if(l) return l;
if(root->val > val) return root;
auto* r = Successor(root->right,val);
if(r) return r;
return nullptr;
}
TreeNode* Precursor(TreeNode* root,int val){
if(!root) return nullptr;
auto* r = Precursor(root->right,val);
if(r) return r;
if(root->val < val) return root;
auto* l = Precursor(root->left,val);
if(l) return l;
return nullptr;
}
相同的树
- 使用先序遍历模板
- 如果当前节点都是null 或者值相同,则当前节点相同,继续比较左右子树节点
class Solution {
public:
bool isSameTree(TreeNode* p, TreeNode* q) {
if(!p && !q) return true;
if(!p || !q || p->val != q->val) return false;
return isSameTree(p->left,q->left) && isSameTree(p->right,q->right);
}
};
对称二叉树
class Solution {
public:
bool ismirror(TreeNode* p,TreeNode* q){
if(!p && !q) return true;
if(!p || !q || p->val != q->val) return false;
return ismirror(p->left,q->right) && ismirror(p->right,q->left);
}
bool isSymmetric(TreeNode* root) {
return ismirror(root,root);
}
};
合并二叉树
class Solution {
public:
TreeNode* mergeTrees(TreeNode* t1, TreeNode* t2) {
// 使用先序遍历框架,返回当前节点
if(!t1 && !t2) return nullptr;
TreeNode* root = new TreeNode(0);
TreeNode* l1 = nullptr,*l2 = nullptr,*r1 = nullptr,*r2 = nullptr;
if(t1){
root->val += t1->val;
l1 = t1->left;r1 = t1->right;
}
if(t2){
root->val += t2->val;
l2 = t2->left;r2 = t2->right;
}
root->left = mergeTrees(l1,l2);
root->right = mergeTrees(r1,r2);
return root;
}
};
反转二叉树
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(!root) return nullptr;
auto* l = invertTree(root->left);
root->left = invertTree(root->right);
root->right = l;
return root;
}
};
判断合法性
判断是否是二叉搜索树
- 节点的左子树只包含小于当前节点的数
- 节点的右子树只包含大于当前节点的数
- 所有左子树和右子树自身必须也是二叉搜索树
class Solution {
// 携带额外信息,从上层节点传入的最大值和最小值
bool isValidBST(TreeNode* root,TreeNode* min,TreeNode* max){
if(!root) return true;
if(min && root->val <= min->val){
return false;
}
if(max && root->val >= max->val){
return false;
}
return isValidBST(root->left,min,root) && isValidBST(root->right,root,max);
}
public:
bool isValidBST(TreeNode* root) {
return isValidBST(root,nullptr,nullptr);
}
};
判断是否是平衡二叉树
class Solution {
pair<bool,int> helper(TreeNode* root){
if(!root) return {true,-1};
auto l = helper(root->left);
auto r = helper(root->right);
if(!l.first || !r.first) return {false,0};
return make_pair(abs(l.second - r.second) <2,max(l.second,r.second)+1);
}
public:
bool isBalanced(TreeNode* root) {
return helper(root).first;
}
};
验证二叉树
class Solution {
public:
bool validateBinaryTreeNodes(int n, vector<int>& leftChild, vector<int>& rightChild) {
// a.不能有环,b.不能逆向,c.不能有多棵树
//每一个节点只能有一个父节点,使用一个unordered_map来记录每个节点的信息,每次插入新节点前,先查询父节点是否存在(解决c),再查询map中是否有新节点(解决a和b)
unordered_map<int,int> map;
int i = 0;
while(i < n &&leftChild[i] == -1 && rightChild[i] == -1) i++;
map[i] = i;
while(i < n){
// 该行验证父节点是否存在
if(map.count(i) == 0) return false;
int l = leftChild[i],r = rightChild[i];
if(l != -1){
//该行验证节点是否已经被使用,如果被使用过,可能会形成逆向和有环
if(map.count(l) != 0) return false;
map[l] = i;
}
if(r != -1){
if(map.count(r) != 0) return false;
map[r] = i;
}
i++;
}
return true;
}
};
插入
二叉搜索树的插入操作
- 递归插入
- 使用后续遍历的模板,找到新值的位置插入
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
if(!root) return new TreeNode(val);
if(root->val > val){
root->left = insertIntoBST(root->left,val);
}else{
root->right = insertIntoBST(root->right,val);
}
return root;
}
};
- 非递归插入
class Solution {
public:
TreeNode* insertIntoBST(TreeNode* root, int val) {
auto* node = new TreeNode(val);
TreeNode* x=nullptr,*y = root;
while(y){
x = y;
if(y->val > val) y = y->left;
else y = y->right;
}
if(!x) root = node;
else if(x->val > val) x->left = node;
else x->right = node;
return root;
}
};
BST 删除节点
- 设要删除的节点为Z
- 如果 Z 没有孩子节点,则直接删除Z
- 如果Z只有一个孩子节点,用孩子节点替代Z
- 如果Z有2个孩子节点,则需要找到Z的后继节点X和X的父节点
用X的right节点替代X,再用X替代Z
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root) return nullptr;
//找到该节点
if(root->val == key){
// 无孩子节点和只有一个孩子节点的情况
if(!root->left) return root->right;
if(!root->right) return root->left;
// 2个孩子节点
TreeNode* x = root,*y = root->right;
// 找到后继节点和后继节点的父节点
while(y->left){
x = y;
y = y->left;
}
// 后继节点y不是root->right节点时,需要先把y->right替代y,再把y替代root->right;
if(x != root){
x->left = y->right;
y->right = root->right;
}
y->left = root->left;
delete root;
return y;
}
if(root->val > key)
root->left = deleteNode(root->left,key);
else if(root->val < key)
root->right = deleteNode(root->right,key);
return root;
}
};