目录
二叉树
1. 二叉树的定义
2. 二叉树的遍历
3. 二叉树的应用
4. 实现细节
5. C++中的实现
面试准备
红黑树
红黑树的原理
红黑树的用途
示例代码
面试准备
1. 红黑树的工作原理及其规则
2. 红黑树的优势及与其他二叉搜索树(如AVL树)的比较
3. 红黑树操作的时间复杂度
4. 红黑树的基本操作编写代码
代码
红黑树节点定义和基本结构
辅助函数实现
插入操作和违规修正
遍历函数
测试用例
在C++中,二叉树可以通过结构体或类来实现。例如:
struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(NULL), right(NULL) {}
};
红黑树的关键特性是保持树的平衡,这通过以下规则实现:
通过这些规则,红黑树保持大致的平衡,从而在插入、删除和查找操作中提供接近O(log n)的最坏情况时间复杂度。
红黑树在许多高级数据结构中都有应用,如:
map
和set
中被用作底层结构。 在C++的标准模板库(STL)中,map
和set
通常是基于红黑树实现的。这些数据结构提供了高效的查找、插入和删除操作。
map:
#include
#include
set:
#include
#include
using namespace std;
int main() {
set exampleSet;
// 插入元素
exampleSet.insert(10);
exampleSet.insert(30);
exampleSet.insert(20);
exampleSet.insert(5);
// 遍历set
for (int element : exampleSet) {
cout << element << endl;
}
return 0;
}
虽然C++ STL中的priority_queue
通常是基于二叉堆实现的,红黑树也可以用来实现一个功能类似的结构,支持快速元素插入和删除。
注意:这里提供的是一个简化的示例,仅用于演示概念。
#include
#include
using namespace std;
class PriorityQueue {
set pq;
public:
void insert(int value) {
pq.insert(value);
}
int top() {
if (!pq.empty())
return *pq.begin();
return -1; // 或抛出异常
}
void pop() {
if (!pq.empty())
pq.erase(pq.begin());
}
bool isEmpty() {
return pq.empty();
}
};
int main() {
PriorityQueue pq;
pq.insert(10);
pq.insert(5);
pq.insert(20);
while (!pq.isEmpty()) {
cout << pq.top() << endl;
pq.pop();
}
return 0;
}
在CPU调度系统中,红黑树可以用来维护一个任务的优先级队列。以下示例展示了一个简化的任务调度系统,其中任务按优先级排序。
#include
#include
using namespace std;
struct Task {
int priority;
string taskName;
bool operator<(const Task& t) const {
return priority < t.priority;
}
};
int main() {
set schedule;
// 添加任务
schedule.insert({1, "Task A"});
schedule.insert({3, "Task C"});
schedule.insert({2, "Task B"});
// 按优先级执行任务
for (const auto& task : schedule) {
cout << "执行任务: " << task.taskName << " (优先级: " << task.priority << ")" << endl;
}
return 0;
}
以下是C++中红黑树节点的基本结构示例:
enum Color { RED, BLACK };
struct Node {
int data;
bool color;
Node *left, *right, *parent;
// Constructor
Node(int data) : data(data) {
parent = left = right = nullptr;
color = RED;
}
};
class RedBlackTree {
private:
Node *root;
protected:
void rotateLeft(Node *&, Node *&);
void rotateRight(Node *&, Node *&);
void fixViolation(Node *&, Node *&);
public:
RedBlackTree() { root = nullptr; }
void insert(const int &n);
// ... 其他函数,例如遍历和删除
};
在这个结构中,每个节点都有一个数据值、一个颜色标记(红色或黑色)、指向左子节点、右子节点和父节点的指针。红黑树类包含用于维护树平衡的旋转和修复函数。
这只是一个非常简化的示例,红黑树的完整实现需要包括详细的插入、删除和平衡调整逻辑。
在面试中,你可能需要:
红黑树是一种自平衡二叉搜索树,它通过以下规则确保高效的操作时间复杂度(主要是插入和删除):
红黑树的主要优势在于它提供了一种平衡插入、删除和查找操作的高效方式。与AVL树相比,红黑树在插入和删除操作中更加高效,因为它们需要更少的重新平衡操作。
红黑树的所有基本操作(插入、删除、查找)的时间复杂度都是O(log n)。由于树是大致平衡的,所以这些操作的最坏情况性能也是很好的。
编写红黑树的代码需要注意其性质的维持。插入和删除操作特别复杂,因为它们可能会破坏红黑树的性质,所以需要通过旋转和重新着色来修复。由于代码实现较长,我之前的回答已经提供了插入操作的一个示例。删除操作的代码更加复杂,但基本思路是在删除节点后通过一系列的旋转和颜色更改来保持树的平衡和性质。
首先,定义红黑树的节点结构和基本的红黑树类:
#include
using namespace std;
enum Color { RED, BLACK };
struct Node {
int data;
bool color;
Node *left, *right, *parent;
Node(int data) : data(data) {
parent = left = right = nullptr;
color = RED;
}
};
class RedBlackTree {
private:
Node *root;
protected:
void rotateLeft(Node *&root, Node *&pt);
void rotateRight(Node *&root, Node *&pt);
void fixViolation(Node *&root, Node *&pt);
public:
RedBlackTree() { root = nullptr; }
void insert(const int &n);
void inorder();
void levelOrder();
};
接下来,实现旋转和修正违规的辅助函数:
void RedBlackTree::rotateLeft(Node *&root, Node *&pt) {
Node *pt_right = pt->right;
pt->right = pt_right->left;
if (pt->right != nullptr)
pt->right->parent = pt;
pt_right->parent = pt->parent;
if (pt->parent == nullptr)
root = pt_right;
else if (pt == pt->parent->left)
pt->parent->left = pt_right;
else
pt->parent->right = pt_right;
pt_right->left = pt;
pt->parent = pt_right;
}
void RedBlackTree::rotateRight(Node *&root, Node *&pt) {
Node *pt_left = pt->left;
pt->left = pt_left->right;
if (pt->left != nullptr)
pt->left->parent = pt;
pt_left->parent = pt->parent;
if (pt->parent == nullptr)
root = pt_left;
else if (pt == pt->parent->right)
pt->parent->right = pt_left;
else
pt->parent->left = pt_left;
pt_left->right = pt;
pt->parent = pt_left;
}
插入操作和违规修正是红黑树最核心的部分:
void RedBlackTree::fixViolation(Node *&root, Node *&pt) {
Node *parent_pt = nullptr;
Node *grand_parent_pt = nullptr;
while ((pt != root) && (pt->color != BLACK) && (pt->parent->color == RED)) {
parent_pt = pt->parent;
grand_parent_pt = pt->parent->parent;
/* Case : A
Parent of pt is left child of Grand-parent of pt */
if (parent_pt == grand_parent_pt->left) {
Node *uncle_pt = grand_parent_pt->right;
/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if (uncle_pt != nullptr && uncle_pt->color == RED) {
grand_parent_pt->color = RED;
parent_pt->color = BLACK;
uncle_pt->color = BLACK;
pt = grand_parent_pt;
} else {
/* Case : 2
pt is right child of its parent
Left-rotation required */
if (pt == parent_pt->right) {
rotateLeft(root, parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
/* Case : 3
pt is left child of its parent
Right-rotation required */
rotateRight(root, grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
}
/* Case : B
Parent of pt is right child of Grand-parent of pt */
else {
Node *uncle_pt = grand_parent_pt->left;
/* Case : 1
The uncle of pt is also red
Only Recoloring required */
if ((uncle_pt != nullptr) && (uncle_pt->color == RED)) {
grand_parent_pt->color = RED;
parent_pt->color = BLACK;
uncle_pt->color = BLACK;
pt = grand_parent_pt;
} else {
/* Case : 2
pt is left child of its parent
Right-rotation required */
if (pt == parent_pt->left) {
rotateRight(root, parent_pt);
pt = parent_pt;
parent_pt = pt->parent;
}
/* Case : 3
pt is right child of its parent
Left-rotation required */
rotateLeft(root, grand_parent_pt);
swap(parent_pt->color, grand_parent_pt->color);
pt = parent_pt;
}
}
}
root->color = BLACK;
}
void RedBlackTree::insert(const int &data) {
Node *pt = new Node(data);
// Do a normal BST insert
root = BSTInsert(root, pt);
// fix Red Black Tree violations
fixViolation(root, pt);
}
Node* BSTInsert(Node* root, Node* pt) {
/* If the tree is empty, return a new node */
if (root == nullptr)
return pt;
/* Otherwise, recur down the tree */
if (pt->data < root->data) {
root->left = BSTInsert(root->left, pt);
root->left->parent = root;
} else if (pt->data > root->data) {
root->right = BSTInsert(root->right, pt);
root->right->parent = root;
}
/* return the (unchanged) node pointer */
return root;
}
遍历函数用于验证树的结构:
void inorderHelper(Node *root) {
if (root == nullptr)
return;
inorderHelper(root->left);
cout << root->data << " ";
inorderHelper(root->right);
}
void RedBlackTree::inorder() { inorderHelper(root); }
void levelOrderHelper(Node *root) {
if (root == nullptr)
return;
std::queue q;
q.push(root);
while (!q.empty()) {
Node *temp = q.front();
cout << temp->data << " ";
q.pop();
if (temp->left != nullptr)
q.push(temp->left);
if (temp->right != nullptr)
q.push(temp->right);
}
}
void RedBlackTree::levelOrder() { levelOrderHelper(root); }
最后,定义一些测试用例以验证红黑树的功能:
int main() {
RedBlackTree tree;
tree.insert(7);
tree.insert(6);
tree.insert(5);
tree.insert(4);
tree.insert(3);
tree.insert(2);
tree.insert(1);
cout << "Inorder Traversal of Created Tree\n";
tree.inorder();
cout << "\n\nLevel Order Traversal of Created Tree\n";
tree.levelOrder();
return 0;
}