目录
1.介绍
在C++中,树(Tree)是一种常见的数据结构,用于表示层次关系和分层数据。树由节点(Node)组成,每个节点包含数据和指向其子节点的指针。树的基本概念包括:
1.根节点(Root):树的顶层节点,没有父节点。
2.子节点(Child):一个节点的直接下级节点。
3.父节点(Parent):一个节点的直接上级节点。
4.叶节点(Leaf):没有子节点的节点。
5.深度(Depth):从根节点到当前节点的路径长度。
6.高度(Height):从当前节点到最远叶节点的路径长度。
2.常见类型
(1)二叉树
二叉树是一种特殊的树,每个节点最多有两个子节点,分别成为左子节点和右子节点。
1.二叉树的节点定义
struct TreeNode {
int val;
TreeNode* left;
TreeNode* right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
2.二叉树的遍历
二叉树的遍历主要有三种:
前序遍历:根 -> 左 -> 右
中序遍历:左 -> 根 -> 右
后续遍历:左 -> 右 -> 根
// 前序遍历
void preorder(TreeNode* root) {
if (root == nullptr) return;
std::cout << root->val << " ";
preorder(root->left);
preorder(root->right);
}
// 中序遍历
void inorder(TreeNode* root) {
if (root == nullptr) return;
inorder(root->left);
std::cout << root->val << " ";
inorder(root->right);
}
// 后序遍历
void postorder(TreeNode* root) {
if (root == nullptr) return;
postorder(root->left);
postorder(root->right);
std::cout << root->val << " ";
}
(2)二叉搜索树(BST)
二叉搜索树是一种特殊的二叉树,其中每个节点的左子树包含的值都小于该节点的值,右子树包含的值都大于该节点的值。 二叉搜索树支持高效的查找、插入和删除操作,时间复杂度为O(h),其中h是树的高度。在平衡的情况下(AVL或红黑树),h = log₂n,其中n为节点数。
1.插入节点
插入节点的过程是从根节点开始,递归找到合适的位置插入新节点。
TreeNode* insert(TreeNode* root, int val) {
if (root == nullptr) {
return new TreeNode(val); // 如果树为空,创建新节点
}
if (val < root->val) {
root->left = insert(root->left, val); // 递归插入左子树
} else if (val > root->val) {
root->right = insert(root->right, val); // 递归插入右子树
}
return root; // 返回当前节点
}
2.查找节点
查找节点的过程是从根节点开始,递归地比较节点的值。
TreeNode* search(TreeNode* root, int val) {
if (root == nullptr || root->val == val) {
return root; // 找到节点或树为空
}
if (val < root->val) {
return search(root->left, val); // 递归查找左子树
} else {
return search(root->right, val); // 递归查找右子树
}
}
3.删除节点
删除节点是二叉搜索树种最复杂的操作,分为三种情况:
1.叶子结点:直接删除
2.节点有一个子节点:用子节点替换当前节点。
3.节点有两个子节点:找到右子树的最小节点(或左子树的最大节点),用其值替换当前节点的值,然后删除该最小节点。
TreeNode* deleteNode(TreeNode* root, int val) {
if (root == nullptr) {
return root; // 树为空,直接返回
}
if (val < root->val) {
root->left = deleteNode(root->left, val); // 递归删除左子树
} else if (val > root->val) {
root->right = deleteNode(root->right, val); // 递归删除右子树
} else {
// 找到要删除的节点
if (root->left == nullptr) {
TreeNode* temp = root->right; // 只有一个右子节点
delete root;
return temp;
} else if (root->right == nullptr) {
TreeNode* temp = root->left; // 只有一个左子节点
delete root;
return temp;
} else {
// 有两个子节点,找到右子树的最小节点
TreeNode* temp = root->right;
while (temp->left != nullptr) {
temp = temp->left;
}
root->val = temp->val; // 用最小节点的值替换当前节点的值
root->right = deleteNode(root->right, temp->val); // 删除最小节点
}
}
return root;
}
二叉搜索树中序遍历二叉搜索树会得到一个升序排列的序列。(中序遍历实现代码见二叉树遍历)
(3)平衡二叉树(AVL)
二叉平衡树是一种自平衡的二叉搜索树,确保树的高度差不超过1,以保持高效的查找、插入和删除操作。
平衡因子:平衡因子是节点的左子树高度减去右子树高度。平衡因子的绝对值不超过1.
旋转操作:为了保持平衡,AVL树在插入或删除节点时可能需要进行旋转操作。包括:
左旋、右旋、左右旋和右左旋。
1.节点定义
struct AVLNode {
int val;
int height; // 节点的高度
AVLNode* left;
AVLNode* right;
AVLNode(int x) : val(x), height(1), left(nullptr), right(nullptr) {}
};
2.计算节点高度
int getHeight(AVLNode* node) {
if (node == nullptr) return 0;
return node->height;
}
3.计算平衡因子
int getBalanceFactor(AVLNode* node) {
if (node == nullptr) return 0;
return getHeight(node->left) - getHeight(node->right);
}
4.更新节点高度
void updateHeight(AVLNode* node) {
if (node == nullptr) return;
node->height = std::max(getHeight(node->left), getHeight(node->right)) + 1;
}
5.旋转
(1)右旋(根节点往左子树节点移)
AVLNode* rightRotate(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
// 旋转
x->right = y;
y->left = T2;
// 更新高度
updateHeight(y);
updateHeight(x);
return x;
}
(2)左旋(根节点往右子树节点移)
AVLNode* leftRotate(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
// 旋转
y->left = x;
x->right = T2;
// 更新高度
updateHeight(x);
updateHeight(y);
return y;
}
6.插入节点
插入节点,检查平衡因子并进行旋转操作以恢复平衡。
AVLNode* insert(AVLNode* root, int val) {
// 1. 插入节点
if (root == nullptr) {
return new AVLNode(val);
}
if (val < root->val) {
root->left = insert(root->left, val);
} else if (val > root->val) {
root->right = insert(root->right, val);
} else {
return root; // 不允许重复节点
}
// 2. 更新高度
updateHeight(root);
// 3. 计算平衡因子
int balanceFactor = getBalanceFactor(root);
// 4. 平衡调整
// 左子树不平衡
if (balanceFactor > 1) {
if (val < root->left->val) {
// 左左情况,右旋
return rightRotate(root);
} else {
// 左右情况,先左旋再右旋
root->left = leftRotate(root->left);
return rightRotate(root);
}
}
// 右子树不平衡
if (balanceFactor < -1) {
if (val > root->right->val) {
// 右右情况,左旋
return leftRotate(root);
} else {
// 右左情况,先右旋再左旋
root->right = rightRotate(root->right);
return leftRotate(root);
}
}
return root;
}
7.删除节点
删除节点后,检查平衡因子并进行旋转操作以恢复平衡。
AVLNode* deleteNode(AVLNode* root, int val) {
// 1. 删除节点
if (root == nullptr) {
return root;
}
if (val < root->val) {
root->left = deleteNode(root->left, val);
} else if (val > root->val) {
root->right = deleteNode(root->right, val);
} else {
// 找到要删除的节点
if (root->left == nullptr || root->right == nullptr) {
AVLNode* temp = root->left ? root->left : root->right;
if (temp == nullptr) {
temp = root;
root = nullptr;
} else {
*root = *temp;
}
delete temp;
} else {
// 有两个子节点,找到右子树的最小节点
AVLNode* temp = root->right;
while (temp->left != nullptr) {
temp = temp->left;
}
root->val = temp->val;
root->right = deleteNode(root->right, temp->val);
}
}
if (root == nullptr) {
return root;
}
// 2. 更新高度
updateHeight(root);
// 3. 计算平衡因子
int balanceFactor = getBalanceFactor(root);
// 4. 平衡调整
// 左子树不平衡
if (balanceFactor > 1) {
if (getBalanceFactor(root->left) >= 0) {
// 左左情况,右旋
return rightRotate(root);
} else {
// 左右情况,先左旋再右旋
root->left = leftRotate(root->left);
return rightRotate(root);
}
}
// 右子树不平衡
if (balanceFactor < -1) {
if (getBalanceFactor(root->right) <= 0) {
// 右右情况,左旋
return leftRotate(root);
} else {
// 右左情况,先右旋再左旋
root->right = rightRotate(root->right);
return leftRotate(root);
}
}
return root;
}
(4)红黑树
红黑树是一种自平衡的二叉搜索树,它通过额外的颜色标记和旋转操作来保持树的平衡。红黑树确保了最坏情况下的查找、插入和删除时间复杂度为O(logn)。
红黑树必须满足一下性质:
1.节点是红色或黑色。
2.根节点是黑色。
3.所有叶子结点是黑色。
4.如果一个节点是红色,则它的两个子节点都是黑色。(没有两个连续的红色节点)。
5.从任意节点到其每个叶子的所有路径都包含相同数量的黑色节点(黑色高度相同)。
红黑树基本操作
1.节点定义
enum Color { RED, BLACK };
struct RBNode {
int val;
Color color;
RBNode* left;
RBNode* right;
RBNode* parent;
RBNode(int x) : val(x), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};
2.左旋
void leftRotate(RBNode*& root, RBNode* x) {
RBNode* y = x->right;
x->right = y->left;
if (y->left != nullptr) {
y->left->parent = x;
}
y->parent = x->parent;
if (x->parent == nullptr) {
root = y;
} else if (x == x->parent->left) {
x->parent->left = y;
} else {
x->parent->right = y;
}
y->left = x;
x->parent = y;
}
3.右旋
void rightRotate(RBNode*& root, RBNode* y) {
RBNode* x = y->left;
y->left = x->right;
if (x->right != nullptr) {
x->right->parent = y;
}
x->parent = y->parent;
if (y->parent == nullptr) {
root = x;
} else if (y == y->parent->right) {
y->parent->right = x;
} else {
y->parent->left = x;
}
x->right = y;
y->parent = x;
}
4.插入节点
插入节点后,通过颜色调整和旋转操作来恢复红黑树的性质。
void insertFixup(RBNode*& root, RBNode* z) {
while (z->parent != nullptr && z->parent->color == RED) {
if (z->parent == z->parent->parent->left) {
RBNode* y = z->parent->parent->right;
if (y != nullptr && y->color == RED) {
// Case 1: 叔叔节点是红色
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->right) {
// Case 2: 叔叔节点是黑色,且当前节点是右孩子
z = z->parent;
leftRotate(root, z);
}
// Case 3: 叔叔节点是黑色,且当前节点是左孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
rightRotate(root, z->parent->parent);
}
} else {
RBNode* y = z->parent->parent->left;
if (y != nullptr && y->color == RED) {
// Case 1: 叔叔节点是红色
z->parent->color = BLACK;
y->color = BLACK;
z->parent->parent->color = RED;
z = z->parent->parent;
} else {
if (z == z->parent->left) {
// Case 2: 叔叔节点是黑色,且当前节点是左孩子
z = z->parent;
rightRotate(root, z);
}
// Case 3: 叔叔节点是黑色,且当前节点是右孩子
z->parent->color = BLACK;
z->parent->parent->color = RED;
leftRotate(root, z->parent->parent);
}
}
}
root->color = BLACK; // 根节点必须是黑色
}
void insert(RBNode*& root, int val) {
RBNode* z = new RBNode(val);
RBNode* y = nullptr;
RBNode* x = root;
// 找到插入位置
while (x != nullptr) {
y = x;
if (z->val < x->val) {
x = x->left;
} else {
x = x->right;
}
}
z->parent = y;
if (y == nullptr) {
root = z;
} else if (z->val < y->val) {
y->left = z;
} else {
y->right = z;
}
z->left = nullptr;
z->right = nullptr;
z->color = RED;
// 修复红黑树性质
insertFixup(root, z);
}
5.删除节点
删除节点后,通过颜色调整和旋转操作来恢复红黑树的性质。
void deleteFixup(RBNode*& root, RBNode* x) {
while (x != root && (x == nullptr || x->color == BLACK)) {
if (x == x->parent->left) {
RBNode* w = x->parent->right;
if (w->color == RED) {
// Case 1: 兄弟节点是红色
w->color = BLACK;
x->parent->color = RED;
leftRotate(root, x->parent);
w = x->parent->right;
}
if ((w->left == nullptr || w->left->color == BLACK) &&
(w->right == nullptr || w->right->color == BLACK)) {
// Case 2: 兄弟节点是黑色,且兄弟节点的两个孩子都是黑色
w->color = RED;
x = x->parent;
} else {
if (w->right == nullptr || w->right->color == BLACK) {
// Case 3: 兄弟节点是黑色,且兄弟节点的左孩子是红色,右孩子是黑色
w->left->color = BLACK;
w->color = RED;
rightRotate(root, w);
w = x->parent->right;
}
// Case 4: 兄弟节点是黑色,且兄弟节点的右孩子是红色
w->color = x->parent->color;
x->parent->color = BLACK;
w->right->color = BLACK;
leftRotate(root, x->parent);
x = root;
}
} else {
RBNode* w = x->parent->left;
if (w->color == RED) {
// Case 1: 兄弟节点是红色
w->color = BLACK;
x->parent->color = RED;
rightRotate(root, x->parent);
w = x->parent->left;
}
if ((w->right == nullptr || w->right->color == BLACK) &&
(w->left == nullptr || w->left->color == BLACK)) {
// Case 2: 兄弟节点是黑色,且兄弟节点的两个孩子都是黑色
w->color = RED;
x = x->parent;
} else {
if (w->left == nullptr || w->left->color == BLACK) {
// Case 3: 兄弟节点是黑色,且兄弟节点的右孩子是红色,左孩子是黑色
w->right->color = BLACK;
w->color = RED;
leftRotate(root, w);
w = x->parent->left;
}
// Case 4: 兄弟节点是黑色,且兄弟节点的左孩子是红色
w->color = x->parent->color;
x->parent->color = BLACK;
w->left->color = BLACK;
rightRotate(root, x->parent);
x = root;
}
}
}
if (x != nullptr) {
x->color = BLACK;
}
}
void deleteNode(RBNode*& root, RBNode* z) {
RBNode* y = z;
RBNode* x;
Color yOriginalColor = y->color;
if (z->left == nullptr) {
x = z->right;
transplant(root, z, z->right);
} else if (z->right == nullptr) {
x = z->left;
transplant(root, z, z->left);
} else {
y = minimum(z->right);
yOriginalColor = y->color;
x = y->right;
if (y->parent == z) {
if (x != nullptr) {
x->parent = y;
}
} else {
transplant(root, y, y->right);
y->right = z->right;
y->right->parent = y;
}
transplant(root, z, y);
y->left = z->left;
y->left->parent = y;
y->color = z->color;
}
if (yOriginalColor == BLACK) {
deleteFixup(root, x);
}
delete z;
}