第三周 高级数据结构-树与二叉树

树和二叉树是计算机科学中非常重要的数据结构,它们在算法设计和系统实现中扮演着关键角色。下面是一些基本的理论学习要点:

一、树的定义和术语 

- **树**:由节点组成的层次结构,其中每个节点有零个或多个子节点,但只有一个父节点(除了根节点外)。
- **根节点**:树的顶级节点,没有父节点。
- **子树**:由一个节点和它的所有后代组成的树。
- **叶节点**:没有子节点的节点。
- **内部节点**:至少有一个子节点的节点。
- **树的高度**:从根节点到最远叶节点的最长路径的边数。
- **兄弟节点**:具有相同父节点的节点。
- **树的类型**:包括二叉树、二叉搜索树、平衡树、堆等。

二、二叉树的定义

- **二叉树**:一种特殊的树,其中每个节点最多有两个子节点,通常称为左子节点和右子节点。

三、二叉树的遍历

二叉树的遍历是访问树中所有节点的过程,有以下几种主要的遍历方式:
**前序遍历**:

首先访问根节点,然后递归地进行左子树的前序遍历,最后递归地进行右子树的前序遍历。
**中序遍历**:

首先递归地进行左子树的中序遍历,然后访问根节点,最后递归地进行右子树的中序遍历。
 **后序遍历**:

首先递归地进行左子树的后序遍历,然后递归地进行右子树的后序遍历,最后访问根节点。
 **层序遍历**:

按照层次顺序访问节点,从根节点开始,逐层向右遍历。

### 前序遍历(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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值