课题摘要:
树是一种非常重要的非线性数据结构,它在计算机科学中有广泛的应用,例如在文件系统、数据库索引、算法设计等领域。
树是一种非常重要的非线性数据结构,它在计算机科学中有广泛的应用,例如在文件系统、数据库索引、算法设计等领域。以下是对树的详细解释,包括其定义、基本术语、常见类型以及主要操作。
树是由一个或多个节点组成的有限集合,其中有一个特定的节点称为根节点(root),其余节点分为若干个互不相交的子集,每个子集本身也是一棵树,称为根的子树(subtree)。树具有层次结构,节点之间存在父子关系。
二叉树(Binary Tree)
二叉搜索树(Binary Search Tree, BST)
平衡二叉树(Balanced Binary Tree)
AVL树(Adelson - Velsky and Landis Tree)
红黑树(Red - Black Tree)
std::map
和std::set
、Linux内核的内存管理等。B树(B - Tree)
B + 树(B + - Tree)
遍历(Traversal)
插入(Insertion)
x
的节点,从根节点开始,如果x
小于当前节点的值,则向左子树移动;如果x
大于当前节点的值,则向右子树移动。重复此过程,直到找到一个空位置,将新节点插入到该位置。删除(Deletion)
查找(Search)
文件系统
C:\
是根目录,C:\Users
、C:\Program Files
等是根目录的子目录。每个子目录下还可以有更深层次的目录和文件。数据库索引
算法设计
总之,树是一种非常重要的数据结构,具有丰富的应用。了解树的基本概念、常见类型以及主要操作,有助于我们在实际工作中合理选择和使用树结构,解决各种复杂的问题。
二叉树是一种非常重要的数据结构,它在计算机科学中有广泛的应用,例如在算法设计、数据存储、搜索和排序等领域。以下是对二叉树的详细解释,包括其定义、基本术语、常见类型、主要操作以及实现方法。
二叉树是一种特殊的树结构,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。二叉树可以为空,也可以包含一个或多个节点。
满二叉树(Full Binary Tree)
完全二叉树(Complete Binary Tree)
二叉搜索树(Binary Search Tree, BST)
平衡二叉树(Balanced Binary Tree)
AVL树(Adelson - Velsky and Landis Tree)
红黑树(Red - Black Tree)
std::map
和std::set
、Linux内核的内存管理等。遍历(Traversal)
插入(Insertion)
x
的节点,从根节点开始,如果x
小于当前节点的值,则向左子树移动;如果x
大于当前节点的值,则向右子树移动。重复此过程,直到找到一个空位置,将新节点插入到该位置。删除(Deletion)
查找(Search)
以下是使用C++实现的二叉搜索树的代码示例:
#include
#include
using namespace std;
// 定义二叉树节点类
class TreeNode {
public:
int key;
TreeNode* left;
TreeNode* right;
TreeNode(int key) : key(key), left(nullptr), right(nullptr) {}
};
// 定义二叉搜索树类
class BinarySearchTree {
private:
TreeNode* root;
// 插入辅助函数
void _insert(TreeNode* node, int key) {
if (key < node->key) {
if (node->left == nullptr) {
node->left = new TreeNode(key);
} else {
_insert(node->left, key);
}
} else {
if (node->right == nullptr) {
node->right = new TreeNode(key);
} else {
_insert(node->right, key);
}
}
}
// 搜索辅助函数
TreeNode* _search(TreeNode* node, int key) {
if (node == nullptr || node->key == key) {
return node;
}
if (key < node->key) {
return _search(node->left, key);
}
return _search(node->right, key);
}
// 删除辅助函数
TreeNode* _delete(TreeNode* node, int key) {
if (node == nullptr) {
return node;
}
if (key < node->key) {
node->left = _delete(node->left, key);
} else if (key > node->key) {
node->right = _delete(node->right, key);
} else {
if (node->left == nullptr) {
return node->right;
} else if (node->right == nullptr) {
return node->left;
}
TreeNode* temp = _min_value_node(node->right);
node->key = temp->key;
node->right = _delete(node->right, temp->key);
}
return node;
}
// 查找最小值节点辅助函数
TreeNode* _min_value_node(TreeNode* node) {
TreeNode* current = node;
while (current->left != nullptr) {
current = current->left;
}
return current;
}
// 中序遍历辅助函数
void _inorder_traversal(TreeNode* node, vector<int>& result) {
if (node == nullptr) {
return;
}
_inorder_traversal(node->left, result);
result.push_back(node->key);
_inorder_traversal(node->right, result);
}
public:
BinarySearchTree() : root(nullptr) {}
// 插入函数
void insert(int key) {
if (root == nullptr) {
root = new TreeNode(key);
} else {
_insert(root, key);
}
}
// 搜索函数
TreeNode* search(int key) {
return _search(root, key);
}
// 删除函数
void deleteNode(int key) {
root = _delete(root, key);
}
// 中序遍历函数
vector<int> inorder_traversal() {
vector<int> result;
_inorder_traversal(root, result);
return result;
}
};
int main() {
// 示例用法
BinarySearchTree binary_search_tree;
binary_search_tree.insert(5);
binary_search_tree.insert(3);
binary_search_tree.insert(7);
binary_search_tree.insert(2);
binary_search_tree.insert(4);
binary_search_tree.insert(6);
binary_search_tree.insert(8);
cout << "中序遍历结果:";
vector<int> traversal_result = binary_search_tree.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
TreeNode* search_result = binary_search_tree.search(4);
if (search_result) {
cout << "找到节点,值为:" << search_result->key << endl;
} else {
cout << "未找到节点" << endl;
}
binary_search_tree.deleteNode(3);
cout << "删除节点3后的中序遍历结果:";
traversal_result = binary_search_tree.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
return 0;
}
TreeNode类:
key
和左右子节点指针left
、right
。BinarySearchTree类:
root
。主函数:
BinarySearchTree
对象。运行上述代码后,输出如下:
中序遍历结果: 2 3 4 5 6 7 8
找到节点,值为:4
删除节点3后的中序遍历结果: 2 4 5 6 7 8
数据存储
搜索
排序
算法设计
总之,二叉树是一种非常重要的数据结构,具有丰富的应用。了解二叉树的基本概念、常见类型以及主要操作,有助于我们在实际工作中合理选择和使用二叉树,解决各种复杂的问题。
以下是将文章中的代码部分改写为C++代码后的版本,同时保留了原文的大纲结构:
二叉树的遍历是指按照某种顺序访问二叉树中的每个节点。常见的二叉树遍历方法有三种:前序遍历(Pre-order Traversal)、中序遍历(In-order Traversal)和后序遍历(Post-order Traversal)。此外,还有层次遍历(Level-order Traversal)。以下是对这些遍历方法的详细解释和实现。
定义:访问根节点,然后递归地对左子树进行前序遍历,最后递归地对右子树进行前序遍历。前序遍历的顺序为:根 - 左 - 右。
特点:
递归实现:
#include
#include
using namespace std;
struct TreeNode {
int key;
TreeNode* left;
TreeNode* right;
TreeNode(int key) : key(key), left(nullptr), right(nullptr) {}
};
vector<int> preorder_traversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
result.push_back(root->key);
vector<int> left_traversal = preorder_traversal(root->left);
vector<int> right_traversal = preorder_traversal(root->right);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
}
非递归实现(使用栈):
vector<int> preorder_traversal_iterative(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode* node = s.top();
s.pop();
result.push_back(node->key);
if (node->right) {
s.push(node->right);
}
if (node->left) {
s.push(node->left);
}
}
return result;
}
定义:递归地对左子树进行中序遍历,访问根节点,然后递归地对右子树进行中序遍历。中序遍历的顺序为:左 - 根 - 右。
特点:
递归实现:
vector<int> inorder_traversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
vector<int> left_traversal = inorder_traversal(root->left);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.push_back(root->key);
vector<int> right_traversal = inorder_traversal(root->right);
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
}
非递归实现(使用栈):
vector<int> inorder_traversal_iterative(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
stack<TreeNode*> s;
TreeNode* current = root;
while (current != nullptr || !s.empty()) {
while (current != nullptr) {
s.push(current);
current = current->left;
}
current = s.top();
s.pop();
result.push_back(current->key);
current = current->right;
}
return result;
}
定义:递归地对左子树进行后序遍历,递归地对右子树进行后序遍历,最后访问根节点。后序遍历的顺序为:左 - 右 - 根。
特点:
递归实现:
vector<int> postorder_traversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
vector<int> left_traversal = postorder_traversal(root->left);
vector<int> right_traversal = postorder_traversal(root->right);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
result.push_back(root->key);
return result;
}
非递归实现(使用栈):
vector<int> postorder_traversal_iterative(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
stack<TreeNode*> s;
s.push(root);
while (!s.empty()) {
TreeNode* node = s.top();
s.pop();
result.push_back(node->key);
if (node->left) {
s.push(node->left);
}
if (node->right) {
s.push(node->right);
}
}
reverse(result.begin(), result.end());
return result;
}
定义:按照层次顺序从上到下、从左到右访问二叉树中的每个节点。层次遍历通常使用队列实现。
特点:
实现:
#include
vector<int> level_order_traversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front();
q.pop();
result.push_back(node->key);
if (node->left) {
q.push(node->left);
}
if (node->right) {
q.push(node->right);
}
}
return result;
}
以下是一个完整的示例,展示如何使用上述遍历方法:
int main() {
// 创建一个二叉树
// 1
// / \
// 2 3
// / \ \
// 4 5 6
TreeNode* root = new TreeNode(1);
root->left = new TreeNode(2);
root->right = new TreeNode(3);
root->left->left = new TreeNode(4);
root->left->right = new TreeNode(5);
root->right->right = new TreeNode(6);
// 前序遍历
vector<int> preorder = preorder_traversal(root);
cout << "前序遍历结果:";
for (int key : preorder) {
cout << " " << key;
}
cout << endl;
vector<int> preorder_iterative = preorder_traversal_iterative(root);
cout << "前序遍历结果(非递归):";
for (int key : preorder_iterative) {
cout << " " << key;
}
cout << endl;
// 中序遍历
vector<int> inorder = inorder_traversal(root);
cout << "中序遍历结果:";
for (int key : inorder) {
cout << " " << key;
}
cout << endl;
vector<int> inorder_iterative = inorder_traversal_iterative(root);
cout << "中序遍历结果(非递归):";
for (int key : inorder_iterative) {
cout << " " << key;
}
cout << endl;
// 后序遍历
vector<int> postorder = postorder_traversal(root);
cout << "后序遍历结果:";
for (int key : postorder) {
cout << " " << key;
}
cout << endl;
vector<int> postorder_iterative = postorder_traversal_iterative(root);
cout << "后序遍历结果(非递归):";
for (int key : postorder_iterative) {
cout << " " << key;
}
cout << endl;
// 层次遍历
vector<int> level_order = level_order_traversal(root);
cout << "层次遍历结果:";
for (int key : level_order) {
cout << " " << key;
}
cout << endl;
return 0;
}
二叉树的遍历方法有前序遍历、中序遍历、后序遍历和层次遍历。每种遍历方法都有其特点和应用场景。了解这些遍历方法及其实现,有助于我们在实际工作中合理选择和使用二叉树,解决各种复杂的问题。
在计算机科学中,完全二叉树(Complete Binary Tree)可以用数组来表示,这种表示方法利用了完全二叉树的性质,使得数组索引与树节点之间存在一种自然的映射关系。这种表示方法特别适用于完全二叉树,但对于一般的二叉树也可以通过填充空节点来实现。
完全二叉树是指除了最后一层外,每一层的节点数都达到最大值,并且最后一层的节点从左到右依次填充。这种结构使得可以用数组高效地存储和操作二叉树。
假设用数组arr
来表示完全二叉树,索引i
处的节点有以下关系:
i
的节点,其父节点的索引为(i - 1) / 2
(整数除法)。i
的节点,其左子节点的索引为2 * i + 1
。i
的节点,其右子节点的索引为2 * i + 2
。可以直接将完全二叉树的节点按层次顺序存储到数组中,从索引0开始。
示例:
假设有一个完全二叉树如下:
1
/ \
2 3
/ \ / \
4 5 6 7
这个二叉树可以用数组[1, 2, 3, 4, 5, 6, 7]
来表示。
需要在数组中填充nullptr
或特殊值(如#
)来表示空节点,以保持数组索引与树节点之间的映射关系。
示例:
假设有一个非完全二叉树如下:
1
/ \
2 3
/ \ \
4 5 6
这个二叉树可以用数组[1, 2, 3, 4, 5, nullptr, 6]
来表示。
以下是一个C++代码示例,展示如何用数组表示二叉树,并实现基本的遍历操作。
#include
#include
#include
using namespace std;
class BinaryTree {
private:
vector<int> array;
public:
BinaryTree(const vector<int>& arr) : array(arr) {}
int get_root() {
return array.empty() ? -1 : array[0];
}
int get_left_child(int index) {
int left_index = 2 * index + 1;
return left_index >= array.size() ? -1 : array[left_index];
}
int get_right_child(int index) {
int right_index = 2 * index + 2;
return right_index >= array.size() ? -1 : array[right_index];
}
int get_parent(int index) {
if (index == 0) {
return -1;
}
return array[(index - 1) / 2];
}
vector<int> preorder_traversal() {
function<vector<int>(int)> _preorder = [&](int index) {
if (index >= array.size() || array[index] == -1) {
return vector<int>();
}
vector<int> result = {array[index]};
vector<int> left_traversal = _preorder(2 * index + 1);
vector<int> right_traversal = _preorder(2 * index + 2);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
};
return _preorder(0);
}
vector<int> inorder_traversal() {
function<vector<int>(int)> _inorder = [&](int index) {
if (index >= array.size() || array[index] == -1) {
return vector<int>();
}
vector<int> result;
vector<int> left_traversal = _inorder(2 * index + 1);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.push_back(array[index]);
vector<int> right_traversal = _inorder(2 * index + 2);
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
};
return _inorder(0);
}
vector<int> postorder_traversal() {
function<vector<int>(int)> _postorder = [&](int index) {
if (index >= array.size() || array[index] == -1) {
return vector<int>();
}
vector<int> result;
vector<int> left_traversal = _postorder(2 * index + 1);
vector<int> right_traversal = _postorder(2 * index + 2);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
result.push_back(array[index]);
return result;
};
return _postorder(0);
}
vector<int> level_order_traversal() {
if (array.empty()) {
return {};
}
vector<int> result;
queue<int> q;
q.push(0);
while (!q.empty()) {
int index = q.front();
q.pop();
if (index < array.size() && array[index] != -1) {
result.push_back(array[index]);
q.push(2 * index + 1);
q.push(2 * index + 2);
}
}
return result;
}
};
int main() {
vector<int> array = {1, 2, 3, 4, 5, -1, 6};
BinaryTree binary_tree(array);
cout << "根节点: " << binary_tree.get_root() << endl;
cout << "左子节点(索引0): " << binary_tree.get_left_child(0) << endl;
cout << "右子节点(索引0): " << binary_tree.get_right_child(0) << endl;
cout << "父节点(索引3): " << binary_tree.get_parent(3) << endl;
vector<int> preorder = binary_tree.preorder_traversal();
cout << "前序遍历结果:";
for (int key : preorder) {
cout << " " << key;
}
cout << endl;
vector<int> inorder = binary_tree.inorder_traversal();
cout << "中序遍历结果:";
for (int key : inorder) {
cout << " " << key;
}
cout << endl;
vector<int> postorder = binary_tree.postorder_traversal();
cout << "后序遍历结果:";
for (int key : postorder) {
cout << " " << key;
}
cout << endl;
vector<int> level_order = binary_tree.level_order_traversal();
cout << "层次遍历结果:";
for (int key : level_order) {
cout << " " << key;
}
cout << endl;
return 0;
}
对于上述代码和示例数组[1, 2, 3, 4, 5, -1, 6]
,输出结果如下:
根节点: 1
左子节点(索引0): 2
右子节点(索引0): 3
父节点(索引3): 2
前序遍历结果: 1 2 4 5 3 6
中序遍历结果: 4 2 5 1 3 6
后序遍历结果: 4 5 2 6 3 1
层次遍历结果: 1 2 3 4 5 6
用数组表示二叉树是一种高效且简洁的方法,特别适用于完全二叉树。通过数组索引与树节点之间的映射关系,可以方便地实现二叉树的各种操作,如遍历、查找等。对于非完全二叉树,虽然需要填充空节点,但这种方法仍然具有一定的通用性。希望这次的回答能够帮助您更好地理解和使用数组来表示二叉树。
二叉搜索树(Binary Search Tree,BST)是一种特殊的二叉树,具有非常重要的性质和广泛的应用。以下是对二叉搜索树的详细解释,包括其定义、性质、操作以及实现方法。
二叉搜索树是一种特殊的二叉树,其中每个节点的值满足以下条件:
查找操作的目标是在二叉搜索树中找到一个特定值的节点。查找过程从根节点开始,根据目标值与当前节点值的比较结果,决定是向左子树还是向右子树移动,直到找到目标节点或到达空节点。
递归实现:
TreeNode* search(TreeNode* root, int key) {
if (root == nullptr || root->key == key) {
return root;
}
if (key < root->key) {
return search(root->left, key);
}
return search(root->right, key);
}
非递归实现:
TreeNode* search_iterative(TreeNode* root, int key) {
TreeNode* current = root;
while (current != nullptr) {
if (current->key == key) {
return current;
} else if (key < current->key) {
current = current->left;
} else {
current = current->right;
}
}
return nullptr;
}
插入操作的目标是在二叉搜索树中插入一个新节点,同时保持二叉搜索树的性质。插入过程从根节点开始,根据新节点的值与当前节点值的比较结果,决定是向左子树还是向右子树移动,直到找到一个空位置插入新节点。
递归实现:
TreeNode* insert(TreeNode* root, int key) {
if (root == nullptr) {
return new TreeNode(key);
}
if (key < root->key) {
root->left = insert(root->left, key);
} else {
root->right = insert(root->right, key);
}
return root;
}
非递归实现:
TreeNode* insert_iterative(TreeNode* root, int key) {
TreeNode* new_node = new TreeNode(key);
TreeNode* parent = nullptr;
TreeNode* current = root;
while (current != nullptr) {
parent = current;
if (key < current->key) {
current = current->left;
} else {
current = current->right;
}
}
if (parent == nullptr) {
return new_node;
}
if (key < parent->key) {
parent->left = new_node;
} else {
parent->right = new_node;
}
return root;
}
删除操作的目标是从二叉搜索树中删除一个特定值的节点,同时保持二叉搜索树的性质。删除操作需要考虑以下三种情况:
递归实现:
TreeNode* delete_node(TreeNode* root, int key) {
if (root == nullptr) {
return root;
}
if (key < root->key) {
root->left = delete_node(root->left, key);
} else if (key > root->key) {
root->right = delete_node(root->right, key);
} else {
if (root->left == nullptr) {
return root->right;
} else if (root->right == nullptr) {
return root->left;
}
TreeNode* temp = min_value_node(root->right);
root->key = temp->key;
root->right = delete_node(root->right, temp->key);
}
return root;
}
TreeNode* min_value_node(TreeNode* node) {
TreeNode* current = node;
while (current->left != nullptr) {
current = current->left;
}
return current;
}
二叉搜索树的遍历方法与普通二叉树相同,常见的有前序遍历、中序遍历和后序遍历。中序遍历的结果是一个递增的有序序列,这是二叉搜索树的一个重要性质。
中序遍历:
vector<int> inorder_traversal(TreeNode* root) {
if (root == nullptr) {
return {};
}
vector<int> result;
vector<int> left_traversal = inorder_traversal(root->left);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.push_back(root->key);
vector<int> right_traversal = inorder_traversal(root->right);
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
}
以下是一个完整的C++实现,包括查找、插入和删除操作:
#include
#include
using namespace std;
struct TreeNode {
int key;
TreeNode* left;
TreeNode* right;
TreeNode(int key) : key(key), left(nullptr), right(nullptr) {}
};
class BinarySearchTree {
private:
TreeNode* root;
TreeNode* _insert(TreeNode* node, int key) {
if (node == nullptr) {
return new TreeNode(key);
}
if (key < node->key) {
node->left = _insert(node->left, key);
} else {
node->right = _insert(node->right, key);
}
return node;
}
TreeNode* _search(TreeNode* node, int key) {
if (node == nullptr || node->key == key) {
return node;
}
if (key < node->key) {
return _search(node->left, key);
}
return _search(node->right, key);
}
TreeNode* _delete(TreeNode* node, int key) {
if (node == nullptr) {
return node;
}
if (key < node->key) {
node->left = _delete(node->left, key);
} else if (key > node->key) {
node->right = _delete(node->right, key);
} else {
if (node->left == nullptr) {
return node->right;
} else if (node->right == nullptr) {
return node->left;
}
TreeNode* temp = _min_value_node(node->right);
node->key = temp->key;
node->right = _delete(node->right, temp->key);
}
return node;
}
TreeNode* _min_value_node(TreeNode* node) {
TreeNode* current = node;
while (current->left != nullptr) {
current = current->left;
}
return current;
}
vector<int> _inorder_traversal(TreeNode* node) {
if (node == nullptr) {
return {};
}
vector<int> result;
vector<int> left_traversal = _inorder_traversal(node->left);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.push_back(node->key);
vector<int> right_traversal = _inorder_traversal(node->right);
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
}
public:
BinarySearchTree() : root(nullptr) {}
void insert(int key) {
root = _insert(root, key);
}
TreeNode* search(int key) {
return _search(root, key);
}
void delete_node(int key) {
root = _delete(root, key);
}
vector<int> inorder_traversal() {
return _inorder_traversal(root);
}
};
int main() {
BinarySearchTree bst;
bst.insert(5);
bst.insert(3);
bst.insert(7);
bst.insert(2);
bst.insert(4);
bst.insert(6);
bst.insert(8);
cout << "中序遍历结果:";
vector<int> traversal_result = bst.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
TreeNode* search_result = bst.search(4);
if (search_result) {
cout << "找到节点,值为:" << search_result->key << endl;
} else {
cout << "未找到节点" << endl;
}
bst.delete_node(3);
cout << "删除节点3后的中序遍历结果:";
traversal_result = bst.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
return 0;
}
虽然二叉搜索树在理想情况下(即树是平衡的)具有高效的查找、插入和删除操作(时间复杂度为O(log n)),但在最坏情况下(例如,树退化为链表),时间复杂度会退化为O(n)。为了克服这个问题,研究者们提出了平衡二叉搜索树的概念,如AVL树和红黑树。
总之,二叉搜索树是一种非常重要的数据结构,具有高效的查找、插入和删除操作。了解二叉搜索树的定义、性质、操作以及实现方法,有助于我们在实际工作中合理选择和使用二叉搜索树,解决各种复杂的问题。
AVL树(Adelson-Velsky and Landis Tree)是一种自平衡的二叉搜索树,由G.M. Adelson-Velsky和E.M. Landis在1962年提出。AVL树的主要特点是能够自动保持平衡,从而确保查找、插入和删除操作的时间复杂度始终为O(log n)。以下是对AVL树的详细解释,包括其定义、性质、平衡操作以及实现方法。
AVL树是一种特殊的二叉搜索树,满足以下条件:
为了保持平衡,AVL树在插入或删除节点后可能需要进行旋转操作。旋转操作分为四种基本类型:
单旋转(Single Rotation)
双旋转(Double Rotation)
插入操作的目标是在AVL树中插入一个新节点,同时保持树的平衡。插入操作分为以下步骤:
1. 插入节点 :按照二叉搜索树的插入规则,将新节点插入到合适的位置。
2. 更新高度 :从插入节点的父节点开始,逐级向上更新节点的高度。
3. 检查平衡性 :从插入节点的父节点开始,逐级向上检查每个节点的平衡因子。如果发现某个节点的平衡因子的绝对值大于1,则需要进行旋转操作以恢复平衡。
插入操作的旋转示例:假设插入一个新节点后,某个节点A的左子树高度比右子树高度高2,且A的左子树的左子树高度大于等于A的左子树的右子树高度,此时需要进行右旋转。
删除操作的目标是从AVL树中删除一个指定值的节点,同时保持树的平衡。删除操作分为以下步骤:
1. 删除节点 :按照二叉搜索树的删除规则,删除目标节点。删除操作可能需要找到目标节点的中序后继或中序前驱来替换目标节点的值。
2. 更新高度 :从删除节点的父节点开始,逐级向上更新节点的高度。
3. 检查平衡性 :从删除节点的父节点开始,逐级向上检查每个节点的平衡因子。如果发现某个节点的平衡因子的绝对值大于1,则需要进行旋转操作以恢复平衡。
删除操作的旋转示例:假设删除一个节点后,某个节点A的右子树高度比左子树高度高2,且A的右子树的右子树高度大于等于A的右子树的左子树高度,此时需要进行左旋转。
以下是一个完整的C++实现,包括插入和删除操作:
#include
#include
using namespace std;
struct TreeNode {
int key;
TreeNode* left;
TreeNode* right;
int height;
TreeNode(int key) : key(key), left(nullptr), right(nullptr), height(1) {}
};
class AVLTree {
private:
TreeNode* root;
int height(TreeNode* node) {
if (node == nullptr) {
return 0;
}
return node->height;
}
int get_balance(TreeNode* node) {
if (node == nullptr) {
return 0;
}
return height(node->left) - height(node->right);
}
TreeNode* right_rotate(TreeNode* y) {
TreeNode* x = y->left;
TreeNode* T2 = x->right;
x->right = y;
y->left = T2;
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x;
}
TreeNode* left_rotate(TreeNode* x) {
TreeNode* y = x->right;
TreeNode* T2 = y->left;
y->left = x;
x->right = T2;
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y;
}
TreeNode* insert(TreeNode* node, int key) {
if (node == nullptr) {
return new TreeNode(key);
}
if (key < node->key) {
node->left = insert(node->left, key);
} else if (key > node->key) {
node->right = insert(node->right, key);
} else {
return node;
}
node->height = 1 + max(height(node->left), height(node->right));
int balance = get_balance(node);
if (balance > 1 && key < node->left->key) {
return right_rotate(node);
}
if (balance < -1 && key > node->right->key) {
return left_rotate(node);
}
if (balance > 1 && key > node->left->key) {
node->left = left_rotate(node->left);
return right_rotate(node);
}
if (balance < -1 && key < node->right->key) {
node->right = right_rotate(node->right);
return left_rotate(node);
}
return node;
}
TreeNode* delete_node(TreeNode* node, int key) {
if (node == nullptr) {
return node;
}
if (key < node->key) {
node->left = delete_node(node->left, key);
} else if (key > node->key) {
node->right = delete_node(node->right, key);
} else {
if (node->left == nullptr || node->right == nullptr) {
TreeNode* temp = node->left ? node->left : node->right;
if (temp == nullptr) {
temp = node;
node = nullptr;
} else {
*node = *temp;
}
delete temp;
} else {
TreeNode* temp = min_value_node(node->right);
node->key = temp->key;
node->right = delete_node(node->right, temp->key);
}
}
if (node == nullptr) {
return node;
}
node->height = 1 + max(height(node->left), height(node->right));
int balance = get_balance(node);
if (balance > 1 && get_balance(node->left) >= 0) {
return right_rotate(node);
}
if (balance > 1 && get_balance(node->left) < 0) {
node->left = left_rotate(node->left);
return right_rotate(node);
}
if (balance < -1 && get_balance(node->right) <= 0) {
return left_rotate(node);
}
if (balance < -1 && get_balance(node->right) > 0) {
node->right = right_rotate(node->right);
return left_rotate(node);
}
return node;
}
TreeNode* min_value_node(TreeNode* node) {
TreeNode* current = node;
while (current->left != nullptr) {
current = current->left;
}
return current;
}
vector<int> inorder_traversal(TreeNode* node) {
if (node == nullptr) {
return {};
}
vector<int> result;
vector<int> left_traversal = inorder_traversal(node->left);
result.insert(result.end(), left_traversal.begin(), left_traversal.end());
result.push_back(node->key);
vector<int> right_traversal = inorder_traversal(node->right);
result.insert(result.end(), right_traversal.begin(), right_traversal.end());
return result;
}
public:
AVLTree() : root(nullptr) {}
void insert(int key) {
root = insert(root, key);
}
void delete_node(int key) {
root = delete_node(root, key);
}
vector<int> inorder_traversal() {
return inorder_traversal(root);
}
};
int main() {
AVLTree avl_tree;
vector<int> keys = {10, 20, 30, 40, 50, 25};
for (int key : keys) {
avl_tree.insert(key);
}
cout << "中序遍历结果:";
vector<int> traversal_result = avl_tree.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
avl_tree.delete_node(20);
cout << "删除节点20后的中序遍历结果:";
traversal_result = avl_tree.inorder_traversal();
for (int key : traversal_result) {
cout << " " << key;
}
cout << endl;
return 0;
}
优点
缺点
总之,AVL树是一种非常重要的自平衡二叉搜索树,具有高效的查找、插入和删除操作。了解AVL树的定义、性质、平衡操作以及实现方法,有助于我们在实际工作中合理选择和使用AVL树,解决各种复杂的问题。
以下是整理后的文章,其中的代码部分已使用C++语言重写,并将所有“Python”字样改为“C++”:
红黑树(Red-Black Tree)是一种自平衡的二叉搜索树,它在每个节点上增加了一个存储位来表示节点的颜色,可以是红色或黑色。通过这些颜色标记和特定的规则,红黑树能够确保从根到叶子的最长路径不会超过最短路径的两倍,从而保持大致平衡。红黑树是实际应用中非常重要的数据结构,广泛用于实现各种关联数组和符号表。
红黑树是一种特殊的二叉搜索树,满足以下五个基本性质:
NULL
节点)是黑色。插入操作的目标是在红黑树中插入一个新节点,同时保持红黑树的性质。插入操作分为以下步骤:
1. 插入节点 :按照二叉搜索树的插入规则,将新节点插入到合适的位置,并将新节点标记为红色。
2. 修复红黑树性质 :从插入节点开始,逐级向上检查并修复红黑树的性质。修复过程可能涉及颜色翻转和旋转操作。
插入操作的修复示例:
假设插入一个新节点后,某个节点A的左子节点和左子节点的左子节点都是红色,此时需要进行右旋转,并调整颜色以恢复红黑树的性质。
删除操作的目标是从红黑树中删除一个指定值的节点,同时保持红黑树的性质。删除操作分为以下步骤:
1. 删除节点 :按照二叉搜索树的删除规则,删除目标节点。删除操作可能需要找到目标节点的中序后继或中序前驱来替换目标节点的值。
2. 修复红黑树性质 :从删除节点的父节点开始,逐级向上检查并修复红黑树的性质。修复过程可能涉及颜色翻转和旋转操作。
删除操作的修复示例:
假设删除一个节点后,某个节点A的左子节点是红色,而A的右子节点是黑色且右子节点的两个子节点都是黑色,此时需要进行左旋转,并调整颜色以恢复红黑树的性质。
以下是一个完整的C++实现,包括插入和删除操作:
#include
using namespace std;
enum Color { RED, BLACK };
struct Node {
int key;
Color color;
Node* left;
Node* right;
Node* parent;
Node(int k, Color c = RED) : key(k), color(c), left(nullptr), right(nullptr), parent(nullptr) {}
};
class RedBlackTree {
private:
Node* root;
Node* NIL;
void leftRotate(Node* x) {
Node* y = x->right;
x->right = y->left;
if (y->left != NIL) y->left->parent = x;
y->parent = x->parent;
if (x->parent == NIL) root = y;
else if (x == x->parent->left) x->parent->left = y;
else x->parent->right = y;
y->left = x;
x->parent = y;
}
void rightRotate(Node* x) {
Node* y = x->left;
x->left = y->right;
if (y->right != NIL) y->right->parent = x;
y->parent = x->parent;
if (x->parent == NIL) root = y;
else if (x == x->parent->right) x->parent->right = y;
else x->parent->left = y;
y->right = x;
x->parent = y;
}
void fixInsert(Node* node) {
while (node != root && node->parent->color == RED) {
if (node->parent == node->parent->parent->left) {
Node* uncle = node->parent->parent->right;
if (uncle->color == RED) {
node->parent->color = BLACK;
uncle->color = BLACK;
node->parent->parent->color = RED;
node = node->parent->parent;
} else {
if (node == node->parent->right) {
node = node->parent;
leftRotate(node);
}
node->parent->color = BLACK;
node->parent->parent->color = RED;
rightRotate(node->parent->parent);
}
} else {
Node* uncle = node->parent->parent->left;
if (uncle->color == RED) {
node->parent->color = BLACK;
uncle->color = BLACK;
node->parent->parent->color = RED;
node = node->parent->parent;
} else {
if (node == node->parent->left) {
node = node->parent;
rightRotate(node);
}
node->parent->color = BLACK;
node->parent->parent->color = RED;
leftRotate(node->parent->parent);
}
}
}
root->color = BLACK;
}
public:
RedBlackTree() {
NIL = new Node(0, BLACK);
root = NIL;
}
void insert(int key) {
Node* newNode = new Node(key);
newNode->left = NIL;
newNode->right = NIL;
newNode->parent = nullptr;
Node* parent = nullptr;
Node* current = root;
while (current != NIL) {
parent = current;
if (newNode->key < current->key) current = current->left;
else current = current->right;
}
newNode->parent = parent;
if (parent == nullptr) root = newNode;
else if (newNode->key < parent->key) parent->left = newNode;
else parent->right = newNode;
newNode->color = RED;
fixInsert(newNode);
}
void inorderTraversal(Node* node) {
if (node != NIL) {
inorderTraversal(node->left);
cout << node->key << " ";
inorderTraversal(node->right);
}
}
void inorderTraversal() {
inorderTraversal(root);
cout << endl;
}
};
int main() {
RedBlackTree rbt;
int keys[] = {26, 17, 41, 14, 7, 21, 34, 30, 10, 8, 3, 18, 23, 38, 27};
for (int key : keys) {
rbt.insert(key);
}
cout << "中序遍历结果:" << endl;
rbt.inorderTraversal();
return 0;
}
std::map
和std::set
通常使用红黑树来实现。优点
缺点
总之,红黑树是一种非常重要的自平衡二叉搜索树,具有高效的查找、插入和删除操作。了解红黑树的定义、性质、操作以及实现方法,有助于我们在实际工作中合理选择和使用红黑树,解决各种复杂的问题。
B树(B-Tree)是一种多路平衡搜索树,由Rudolf Bayer和Edward McCreight在1972年提出。B树在数据库和文件系统中被广泛使用,因为它能够有效地处理大量数据,并且在磁盘I/O操作中表现出色。以下是对B树的详细解释,包括其定义、性质、操作以及实现方法。
B树是一种多路平衡搜索树,满足以下条件:
查找操作的目标是在B树中找到一个特定值的节点。查找过程从根节点开始,根据目标值与当前节点键值的比较结果,决定是向哪个子树移动,直到找到目标节点或到达叶子节点。
查找过程:
插入操作的目标是在B树中插入一个新键值,同时保持B树的性质。插入过程分为以下步骤:
删除操作的目标是从B树中删除一个特定值的节点,同时保持B树的性质。删除过程分为以下步骤:
以下是一个完整的C++实现,包括查找、插入和删除操作:
#include
#include
using namespace std;
class BTreeNode {
private:
int* keys;
BTreeNode children;
int t;
int n;
bool leaf;
public:
BTreeNode(int _t, bool _leaf) : t(_t), leaf(_leaf), n(0) {
keys = new int[2 * t - 1];
children = new BTreeNode*[2 * t];
for (int i = 0; i < 2 * t; ++i) children[i] = nullptr;
}
~BTreeNode() {
delete[] keys;
delete[] children;
}
void insertNonFull(int k) {
int i = n - 1;
if (leaf) {
while (i >= 0 && keys[i] > k) {
keys[i + 1] = keys[i];
--i;
}
keys[i + 1] = k;
++n;
} else {
while (i >= 0 && keys[i] > k) --i;
if (children[i + 1]->n == 2 * t - 1) {
splitChild(i + 1, children[i + 1]);
if (keys[i + 1] < k) ++i;
}
children[i + 1]->insertNonFull(k);
}
}
void splitChild(int i, BTreeNode* y) {
BTreeNode* z = new BTreeNode(y->t, y->leaf);
z->n = t - 1;
for (int j = 0; j < t - 1; ++j) z->keys[j] = y->keys[j + t];
if (!y->leaf) {
for (int j = 0; j < t; ++j) z->children[j] = y->children[j + t];
}
y->n = t - 1;
for (int j = n; j > i; --j) children[j + 1] = children[j];
children[i + 1] = z;
for (int j = n - 1; j >= i; --j) keys[j + 1] = keys[j];
keys[i] = y->keys[t - 1];
++n;
}
void traverse() {
for (int i = 0; i < n; ++i) {
if (!leaf) children[i]->traverse();
cout << keys[i] << " ";
}
if (!leaf) children[n]->traverse();
}
};
class BTree {
private:
BTreeNode* root;
int t;
public:
BTree(int _t) : t(_t) {
root = new BTreeNode(t, true);
}
void insert(int k) {
if (root->n == 2 * t - 1) {
BTreeNode* s = new BTreeNode(t, false);
s->children[0] = root;
s->splitChild(0, root);
int i = 0;
if (s->keys[0] < k) ++i;
s->children[i]->insertNonFull(k);
root = s;
} else {
root->insertNonFull(k);
}
}
void traverse() {
root->traverse();
cout << endl;
}
};
int main() {
BTree b(3);
int keys[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
for (int key : keys) b.insert(key);
cout << "中序遍历结果:" << endl;
b.traverse();
return 0;
}
优点
缺点
总之,B树是一种非常重要的多路平衡搜索树,具有高效的查找、插入和删除操作。了解B树的定义、性质、操作以及实现方法,有助于我们在实际工作中合理选择和使用B树,解决各种复杂的问题。
B+树(B+ Tree)是B树的一种变体,广泛应用于数据库索引和文件系统中。B+树在B树的基础上进行了优化,使得其更适合于范围查询和顺序访问。以下是对B+树的详细解释,包括其定义、性质、操作以及实现方法。
B+树是一种多路平衡搜索树,满足以下条件:
查找操作的目标是在B+树中找到一个特定值的节点。查找过程从根节点开始,根据目标值与当前节点键值的比较结果,决定是向哪个子树移动,直到找到目标节点或到达叶子节点。
查找过程:
插入操作的目标是在B+树中插入一个新键值,同时保持B+树的性质。插入过程分为以下步骤:
删除操作的目标是从B+树中删除一个特定值的节点,同时保持B+树的性质。删除过程分为以下步骤:
以下是一个完整的C++实现,包括查找、插入和删除操作:
#include
#include
using namespace std;
class BPlusTreeNode {
private:
int* keys;
BPlusTreeNode children;
int t;
int n;
bool leaf;
public:
BPlusTreeNode(int _t, bool _leaf) : t(_t), leaf(_leaf), n(0) {
keys = new int[2 * t - 1];
children = new BPlusTreeNode*[2 * t];
for (int i = 0; i < 2 * t; ++i) children[i] = nullptr;
}
~BPlusTreeNode() {
delete[] keys;
delete[] children;
}
void insertNonFull(int k) {
int i = n - 1;
if (leaf) {
while (i >= 0 && keys[i] > k) {
keys[i + 1] = keys[i];
--i;
}
keys[i + 1] = k;
++n;
} else {
while (i >= 0 && keys[i] > k) --i;
if (children[i + 1]->n == 2 * t - 1) {
splitChild(i + 1, children[i + 1]);
if (keys[i + 1] < k) ++i;
}
children[i + 1]->insertNonFull(k);
}
}
void splitChild(int i, BPlusTreeNode* y) {
BPlusTreeNode* z = new BPlusTreeNode(y->t, y->leaf);
z->n = t - 1;
for (int j = 0; j < t - 1; ++j) z->keys[j] = y->keys[j + t];
if (!y->leaf) {
for (int j = 0; j < t; ++j) z->children[j] = y->children[j + t];
}
y->n = t - 1;
for (int j = n; j > i; --j) children[j + 1] = children[j];
children[i + 1] = z;
for (int j = n - 1; j >= i; --j) keys[j + 1] = keys[j];
keys[i] = y->keys[t - 1];
++n;
}
void traverse() {
for (int i = 0; i < n; ++i) {
if (!leaf) children[i]->traverse();
cout << keys[i] << " ";
}
if (!leaf) children[n]->traverse();
}
};
class BPlusTree {
private:
BPlusTreeNode* root;
int t;
public:
BPlusTree(int _t) : t(_t) {
root = new BPlusTreeNode(t, true);
}
void insert(int k) {
if (root->n == 2 * t - 1) {
BPlusTreeNode* s = new BPlusTreeNode(t, false);
s->children[0] = root;
s->splitChild(0, root);
int i = 0;
if (s->keys[0] < k) ++i;
s->children[i]->insertNonFull(k);
root = s;
} else {
root->insertNonFull(k);
}
}
void traverse() {
root->traverse();
cout << endl;
}
};
int main() {
BPlusTree b(3);
int keys[] = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
for (int key : keys) b.insert(key);
cout << "中序遍历结果:" << endl;
b.traverse();
return 0;
}
优点
缺点
总之,B+树是一种非常重要的多路平衡搜索树,具有高效的查找、插入和删除操作。了解B+树的定义、性质、操作以及实现方法,有助于我们在实际工作中合理选择和使用B+树,解决各种复杂的问题。