最近面试被问到平衡二叉树的性质及手撕构建平衡二叉树。当时无从下手,翻看牛客网面经,发现是一个常考点。虽然代码量相对较多,但确实是必须要掌握的知识。记录如下:
性质:
1、平衡二叉树其左右子树都为平衡二叉树,且树的深度的绝对值不会超过1;
2、将平衡因子(BF)定义为,其左子树的深度减去右子树的深度,则只会有-1,0,1三种情况;
3、平衡二叉树属于二叉搜索树(BST),也满足二叉搜索树的一些性质:左子树和右子树都是二叉搜索树。左子树所有节点值都小于等于其根节点值,右子树所有节点的值都大于等于其根节点的值;
引出平衡二叉树的原因是想让树的深度尽可能的小,并将查询时间复杂度稳定控制在logn,避免因节点全在树的一边,导致树的深度为n,从而导致的查询时间复杂度为n;
代码实现:
#include<iostream>
#include<stack>
using namespace std;
constexpr auto RH = 1;
constexpr auto EH = 0;
constexpr auto LH = -1;
struct tree {
int val;
int bf;
tree* left;
tree* right;
tree(int n) {
val = n; bf = 0;
left = nullptr; right = nullptr;
}
};
typedef tree* TreeNode;
void R_Roate(TreeNode& P) {
TreeNode L = P->left;
P->left = L->right;
L->right = P;
P = L;
}
void L_Roate(TreeNode& P) {
TreeNode R = P->right;
P->right = R->left;
R->left = P;
P = R;
}
void LeftBalance(TreeNode& T) {
TreeNode L = T->left;
switch(L->bf) {
case LH:
T->bf = L->bf = EH;
R_Roate(T);
break;
case RH:
TreeNode Lr = L->right;
switch (Lr->bf) {
case LH:
T->bf = RH;
Lr->bf = EH;
break;
case EH:
T->bf = Lr->bf = EH;
break;
case RH:
T->bf = EH;
Lr->bf = LH;
break;
}
Lr->bf = EH;
L_Roate(T->left);
R_Roate(T);
break;
}
}
void RightBalance(TreeNode& T) {
TreeNode R = T->right;
switch (R->bf) {
case RH:
T->bf = R->bf = EH;
L_Roate(T);
break;
case LH:
TreeNode Rl = R->left;
switch (Rl->bf) {
case LH:
T->bf = EH;
Rl->bf = RH;
break;
case EH:
T->bf = Rl->bf = EH;
break;
case RH:
T->bf = LH;
Rl->bf = EH;
break;
}
Rl->bf = EH;
R_Roate(T->right);
L_Roate(T);
break;
}
}
bool InsertAVL(TreeNode& T, int e, bool& taller) {
if (T == nullptr) {
T = new tree(e);
taller = true;
}
else {
if (e == T->val) {
taller = false;
return false;
}
if (e < T->val) {
if (!InsertAVL(T->left, e, taller))
return false;
if (taller) {
switch (T->bf) {
case LH:
LeftBalance(T);
taller = false;
break;
case EH:
T->bf = LH;
taller = true;
break;
case RH:
T->bf = EH;
taller = false;
break;
}
}
}
else {
if (!InsertAVL(T->right, e, taller))
return false;
if (taller) {
switch (T->bf) {
case RH:
RightBalance(T);
taller = false;
break;
case EH:
T->bf = RH;
taller = true;
break;
case LH:
T->bf = EH;
taller = false;
break;
}
}
}
}
return true;
}
void preTraver(TreeNode& root) {
if (root == nullptr) return;
cout << root->val << " ";
preTraver(root->left);
preTraver(root->right);
return;
}
void preTraverCru(TreeNode& root) {
stack<TreeNode> data;
data.push(root);
while (!data.empty()) {
TreeNode temp = data.top();
data.pop();
cout << temp->val << " ";
if (temp->right) data.push(temp->right);
if (temp->left) data.push(temp->left);
}
cout << endl;
return;
}
void midTraver(TreeNode& root) {
if (root == nullptr) return;
midTraver(root->left);
cout << root->val << " ";
midTraver(root->right);
return;
}
void midTraverCru(TreeNode& root) {
TreeNode cur = root;
stack<TreeNode> data;
while (!data.empty() || cur != nullptr) {
while (cur!=nullptr) {
data.push(cur);
cur = cur->left;
}
cur = data.top();
data.pop();
cout << cur->val << " ";
cur = cur->right;
}
cout << endl;
return;
}
void posTraver(TreeNode& root) {
if (root == nullptr) return;
posTraver(root->left);
posTraver(root->right);
cout << root->val << " ";
return;
}
void posTraverCru(TreeNode& root) {
stack<TreeNode> data1;
stack<TreeNode> data2;
data1.push(root);
while (!data1.empty()) {
TreeNode temp = data1.top();
data1.pop();
data2.push(temp);
if(temp->left)data1.push(temp->left);
if(temp->right)data1.push(temp->right);
}
while (!data2.empty()) {
TreeNode temp = data2.top();
data2.pop();
cout << temp->val << " ";
}
cout << endl;
return;
}
int main() {
int data[] = { 3,2,1,4,5,6,7,10,9,8 };
TreeNode root = nullptr;
bool taller;
for (int i = 0; i < 10; i++)
InsertAVL(root, data[i], taller);
cout << "Preorder traversal:" << endl;
preTraver(root);
cout << endl;
preTraverCru(root);
cout << "Inorder traversal:" << endl;
midTraver(root);
cout << endl;
midTraverCru(root);
cout << "Postorder traversal:" << endl;
posTraver(root);
cout << endl;
posTraverCru(root);
return 0;
}