树和二叉树是计算机科学中非常重要的数据结构,它们在算法设计和系统实现中扮演着关键角色。下面是一些基本的理论学习要点:
一、树的定义和术语
- **树**:由节点组成的层次结构,其中每个节点有零个或多个子节点,但只有一个父节点(除了根节点外)。
- **根节点**:树的顶级节点,没有父节点。
- **子树**:由一个节点和它的所有后代组成的树。
- **叶节点**:没有子节点的节点。
- **内部节点**:至少有一个子节点的节点。
- **树的高度**:从根节点到最远叶节点的最长路径的边数。
- **兄弟节点**:具有相同父节点的节点。
- **树的类型**:包括二叉树、二叉搜索树、平衡树、堆等。
二、二叉树的定义
- **二叉树**:一种特殊的树,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。
三、二叉树的遍历
二叉树的遍历是访问树中所有节点的过程,有以下几种主要的遍历方式:
**前序遍历**:
首先访问根节点,然后递归地进行左子树的前序遍历,最后递归地进行右子树的前序遍历。
**中序遍历**:
首先递归地进行左子树的中序遍历,然后访问根节点,最后递归地进行右子树的中序遍历。
**后序遍历**:
首先递归地进行左子树的后序遍历,然后递归地进行右子树的后序遍历,最后访问根节点。
**层序遍历**:
按照层次顺序访问节点,从根节点开始,逐层向右遍历。
### 前序遍历(Preorder Traversal)
前序遍历的顺序是:根节点 -> 左子树 -> 右子树。具体步骤如下:
1. 访问根节点。
2. 递归地进行左子树的前序遍历。
3. 递归地进行右子树的前序遍历。
**示例**:
假设有如下二叉树:
```
A
/ \
B C
/ \
D E
```
前序遍历的结果是:A, B, D, E, C。
### 中序遍历(Inorder Traversal)
中序遍历的顺序是:左子树 -> 根节点 -> 右子树。具体步骤如下:
1. 递归地进行左子树的中序遍历。
2. 访问根节点。
3. 递归地进行右子树的中序遍历。
**示例**:
使用与前序遍历相同的二叉树:
```
A
/ \
B C
/ \
D E
```
中序遍历的结果是:D, B, E, A, C。
### 后序遍历(Postorder Traversal)
后序遍历的顺序是:左子树 -> 右子树 -> 根节点。具体步骤如下:
1. 递归地进行左子树的后序遍历。
2. 递归地进行右子树的后序遍历。
3. 访问根节点。
**示例**:
使用与前序遍历相同的二叉树:
```
A
/ \
B C
/ \
D E
```
后序遍历的结果是:D, E, B, C, A。
### 层序遍历(Level Order Traversal)
虽然不是问题中提到的三种遍历方式之一,但层序遍历也是非常重要的,特别是在处理二叉树的广度优先搜索(BFS)时。层序遍历的顺序是按照从上到下,从左到右的顺序访问每个节点。
**示例**:
使用与前序遍历相同的二叉树:
```
A
/ \
B C
/ \
D E
```
层序遍历的结果是:A, B, C, D, E。
这些遍历方式是理解和操作二叉树的关键,每种遍历方式都有其特定的应用场景和用途。希望这些解释能帮助你更好地理解这些概念。
三、二叉搜索树(BST)
- **二叉搜索树**:一种特殊的二叉树,其中每个节点的值大于或等于其左子树中所有节点的值,并且小于或等于其右子树中所有节点的值。
- **操作**:插入、删除、查找等。
- **性质**:
- 有序性:左子树上所有值 <= 根节点值 <= 右子树上所有值。
- 唯一性:每个节点的值是唯一的。
- 动态性:可以动态地插入和删除节点。
二叉搜索树提供了高效的查找、插入和删除操作,其时间复杂度通常为 O(log n),在最坏的情况下(树退化成链表)为 O(n)。
//节点
class Node {
public:
Node* left = NULL;
Node* right = NULL;
int value;
Node() {
this->value = 0;
}
Node(int value) {
this->value = value;
}
};
//搜索二叉树
class Tree {
public:
Node* root = NULL;
Tree() {
}
Node* insert(Node* node, int value) {
if (node == NULL) {
node = new Node(value);
return node;
}
if (value >= node->value) {
node->right = insert(node->right, value);
}
if (value < node->value) {
node->left = insert(node->left, value);
}
return node;
}
void insert(int value) {
root = insert(root, value);
}
void qian(Node * node) {
if (node != NULL) {
cout << node->value << ' ';
qian(node->left);
qian(node->right);
}
}
void zhong(Node* node) {
if (node != NULL) {
zhong(node->left);
cout << node->value << ' ';
zhong(node->right);
}
}
void hou(Node* node) {
if (node != NULL) {
hou(node->left);
hou(node->right);
cout << node->value << ' ';
}
}
void ceng() {
cout << "层序遍历" << endl;
queue<Node*> q;
if(root!=NULL)q.push(root);
while (!q.empty()) {
Node* node = q.front();
q.pop();
cout << node->value << ' ';
if (node->left!=NULL) q.push(node->left);
if (node->right != NULL) q.push(node->right);
}
cout << endl;
}
Node* search(int value, Node* node) {
if (node == NULL) {
return NULL; // 没有找到节点
}
if (value == node->value) {
return node; // 找到节点
}
else if (value < node->value) {
return search(value, node->left); // 在左子树中搜索
}
else {
return search(value, node->right); // 在右子树中搜索
}
}
void delete_value(int value) {
//寻找目标节点
Node* parent = NULL;
Node* node = root;
while (node->value != value) {
parent = node;
if (node->value > value) node = node->left;
else node = node->right;
}
if (node->left == NULL && node->right == NULL) {
if (parent == NULL) root = NULL;
else if (parent->left == node) parent->left = NULL;
else if (parent->right == node) parent->right = NULL;
delete node;
}
else if ((node->left == NULL && node->right != NULL) || (node->left != NULL && node->right == NULL)) {
if (parent == NULL) {
if (node->left != NULL) root = node->left;
else root = node->right;
}
else {
if (node->left != NULL) {
if (node == parent->left) parent->left = node->left;
else parent->right = node->left;
}
else {
if (node == parent->left) parent->left = node->right;
else parent->right = node->right;
}
}
delete node;
}
else {
//寻找后继
Node* temp = node->right;
Node* temp_parent = node;
while (temp->left != NULL) {
temp_parent = temp;
temp = temp->left;
}
node->value = temp->value;
if(temp_parent!=node) temp_parent->left = temp->right;
else temp_parent->right = temp->right;
delete temp;
}
}
};
四、平衡二叉树
对于二叉平衡树,需要满足平衡规则:
- 可以是空树。
- 假如不是空树,任何⼀个结点的左子树与右子树都是平衡⼆叉树,并且高度之差的绝对值不超过 1。
平衡因子:
某结点的左子树与右子树的高度(深度)差即为该节点的平衡因子(BF,Balance Factor)。平衡⼆叉树中不存在平衡因子大于于 1 的节点。
在⼀棵平衡⼆叉树中,节点的平衡因子只能取0、1、-1
- 0 :左右子树等高
- 1:左子树比较高
- -1 :右子树比较高
在插入的过程中产生了最小失衡子树,所以需要在插入的同时递归检查是否产生失衡(在插入代码部分需要注意检查失衡)
最小失衡子树:在新插入的结点向上找,以第一个平衡因子绝对值大于1的结点为根的子树称为最小失衡子树
平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的,旋转分为左旋和右旋,其目的,就是减少树的高度
旋转时注意:
1.谁是主体,
2.谁出问题(下图中的黑色圈为平衡因子异常的节点 灰色为可能存在的节点,写代码时需要进行处理)
3.是谁在旋转
4.旋转后各个节点如何处理
左旋
右旋
先左旋,后右旋
先右旋,后左旋
代码实现如下:
#include<queue>
#include<iostream>
using namespace std;
int max(int a, int b) {
if (a >= b) return a;
else return b;
}
class Node {
public:
Node* left = NULL;
Node* right = NULL;
int height = 0;
int value;
Node() {
this->value = 0;
}
Node(int value) {
this->value = value;
height = 1;
}
};
class Tree {
public:
Node* root = NULL;
Tree() {
}
void qian(Node * node) {
if (node != NULL) {
cout << node->value << ' ';
qian(node->left);
qian(node->right);
}
}
void zhong(Node* node) {
if (node != NULL) {
zhong(node->left);
cout << node->value << ' ';
zhong(node->right);
}
}
void hou(Node* node) {
if (node != NULL) {
hou(node->left);
hou(node->right);
cout << node->value << ' ';
}
}
void ceng() {
cout << "层序遍历" << endl;
queue<Node*> q;
if(root!=NULL)q.push(root);
while (!q.empty()) {
Node* node = q.front();
q.pop();
cout << node->value << " height:" << node->height << endl;
if (node->left!=NULL) q.push(node->left);
if (node->right != NULL) q.push(node->right);
}
cout << endl;
}
int creat_height(Node* root) {
if (root == NULL) return 0;
root->height = 1 + max(creat_height(root->left),creat_height(root->right));
return root->height;
}
void updata_height() {
creat_height(this->root);
}
int height(Node* node) {
return (node == NULL) ? 0 : node->height;
}
Node* insert(Node* node, int value);
Node* check_balance(int value);
void balance(Node* node, int value);
void set_no_balance_mode(Node* node, int value);
void insert(int value);
Node* left_turn(Node* node);
Node* right_turn(Node* node);
};
Node* Tree::left_turn(Node* node) {
Node* newRoot = node->right;
Node* temp = newRoot->left;
node->right = temp;
newRoot->left = node;
newRoot->height = max(height(newRoot->left), height(newRoot->right)) + 1;
node->height = max(height(node->left), height(node->right)) + 1;
return newRoot;
}
Node* Tree::right_turn(Node* node) {
Node* newRoot = node->left;
Node* temp = newRoot->right;
node->left = temp;
newRoot->right = node;
newRoot->height = max(height(newRoot->left), height(newRoot->right)) + 1;
node->height = max(height(node->left), height(node->right)) + 1;
return newRoot;
}
Node* Tree::insert(Node* node, int value) {
if (node == NULL) {
node = new Node(value);
return node;
}
if (value >= node->value) {
node->right = insert(node->right, value);
}
if (value < node->value) {
node->left = insert(node->left, value);
}
node->height += 1;
int balance = height(node->left) - height(node->right);
//如果不平衡 return值应该发生变化
if (balance > 1 && value < node->left->value) {
return left_turn(node);
}
if (balance < -1 && value > node->right->value) {
right_turn(node->right);
return left_turn(node);
}
if (balance > 1 && value > node->left->value) {
left_turn(node->left);
return right_turn(node);
}
if (balance < -1 && value < node->right->value) {
return right_turn(node);
}
return node;
}
void Tree::insert(int value) {
root = insert(root, value);
}
int main() {
for (int j = 0; j < 10; j++) {
int n, value;
cin >> n;
Tree tree;
for (int i = 0; i < n; i++) {
cin >> value;
tree.insert(value);
}
tree.creat_height(tree.root);
tree.ceng();
cout << endl;
}
return 0;
}