32.C++二叉树进阶1(二叉搜索树)

⭐上篇文章:31.C++多态4(静态多态,动态多态,虚函数表的存储位置)-CSDN博客

⭐本篇代码:c++学习/18.二叉树进阶-二叉搜索树 · 橘子真甜/c++-learning-of-yzc - 码云 - 开源中国 (gitee.com)

⭐标⭐是比较重要的部分

目录

[一. 二叉搜索树的概念](#一. 二叉搜索树的概念)

[二. 二叉搜索树的插入](#二. 二叉搜索树的插入)

[三. 二叉搜索树的查找](#三. 二叉搜索树的查找)

[四. 二叉树的删除](#四. 二叉树的删除)

[4.1 删除节点右子树为空](#4.1 删除节点右子树为空)

[4.2 删除节点左子树为空](#4.2 删除节点左子树为空)

[4.3 左右孩子均不为空](#4.3 左右孩子均不为空)

[4.4 删除节点代码](#4.4 删除节点代码)

[五. 二叉搜索树的遍历](#五. 二叉搜索树的遍历)

六.代码与测试


一. 二叉搜索树的概念

二叉搜索树也称二叉排序树,它有可能是一颗空树。二叉搜索树性质如下:

1 若它有左子树,那么它的左子树所有节点的值小于根节点

2 如它有右子树,那么它的右子树所有节点的值小于根节点

3 它的左右子树也是二叉搜索树

二叉树的代码框架如下:

cpp 复制代码
#pragma once
#include <iostream>
#include <vector>

//二叉树节点
template<class K>
struct BSTreeNode
{
	K _key;
	BSTreeNode* _left;
	BSTreeNode* _right;

	BSTreeNode(const K& key)
		:_key(key), _left(nullptr), _right(nullptr)
	{};
};


//二叉树
template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	bool Insert()
	{}

	bool find()
	{}

	bool Delete)
	{}

	//二叉树不允许修改数据
private:
	Node* _root;	//根节点
};

二. 二叉搜索树的插入

假设我们插入的节点为key

若一棵树为空,则直接插入即可

若不为空:

按照二叉搜索树的性质,插入该节点即可。

如果某一个节点和key值相同,则直接不插入

若某一个节点比key值大,则应该将key插入到该节点的左子树中

若某一个节点比key值小,则应该将key插入到该节点的右子树中

然后在其左右子树中进行判断,直到子树节点为空,然后插入即可

流程图如下:

插入函数的代码如下:

cpp 复制代码
	bool Insert(const K& key)
	{
		//根节点为空
		if (!root)
		{
			_root = new Node(key);
			return true;
		}

		//不为空,根据性质插入数据
		Node* cur = _root;
		Node* parent = cur;			//最后cur为空,需要一个节点保存其父节点用于连接
		while (cur)
		{
			if (key > cur->_key)	//比当前值大,找其右子树
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)	//比当前树小,找其左子树
			{
				parent = cur;
				cur = cur->_left;
			}
			else
				return false;	//有相同节点,插入失败
		}

		//此时cur为空,cur即为插入的数据
		cur = new Node(key);
		if (cur->_key > parent->_key)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}
		return true;
	}

三. 二叉搜索树的查找

二叉搜索树的查找过程和插入过程非常相似。比如在插入的过程中,如果发现值相同,返回true即可,如果为空,则说明没有这个节点,返回false

cpp 复制代码
	bool find(const K& key)
	{
		if (!_root)
			return false;

		Node* cur = _root;
		while (cur)
		{
			if (key > cur->_key)
				cur = cur->_right;
			else if (key < cur->_key)
				cur = cur->_left;
			else 
				return true;
		}
		//cur为空,没有key这个节点
		return false;
	}

四. 二叉树的删除

二叉树的删除比较麻烦。可以分为下面四个情况。

1 该节点无左右子节点:

2 该节点只有左子树:

3 该节点只有右子树

4 该节点有左右子树

实际情况1与情况2或者3是重合的,所以只需考虑三者情况

4.1 删除节点右子树为空

下面这流程图我们要删除节点9

若删除节点是父亲左孩子,父亲的左指向删除节点的左孩子

若删除节点是父亲的右孩子,父亲的右指向删除节点的左孩子

如果一个节点左右都为空,也满足这种情况

4.2 删除节点左子树为空

与上面情况类似

若删除节点是父亲左孩子,父亲的左指向删除节点的右孩子

若删除节点是父亲的右孩子,父亲的右指向删除节点的右孩子

4.3 左右孩子均不为空

此时需要找到一个节点来替代这个节点,在二叉搜索树中,根据其左子树节点比根节点小,右子树节点比根节点大的性质。 如果一给节点左右不为空,只需找出其左子树最大,或者右子树最小来替代这个节点即可。

流程图如下:

4.4 删除节点代码

cpp 复制代码
//删除
	bool Erase(const K& key)
	{
		//1.该节点只有一个孩子
		//2.该节点没有孩子
		//3.该节点有三个孩子
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到了,开始删除
			{
				if (cur->_left == nullptr)
				{
					//当cur为根的时候要特判
					if (cur == _root)
					{
						_root = cur->_right;
					}

					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;
					cur = nullptr;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}

					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
					cur = nullptr;
				}
				else//两边都不为空
				{
					//找到左边最大,右边最小与当前值交互即可
					Node* rightmin = cur->_right;
					Node* rightminparent = cur;
					
					while (rightmin->_left)
					{
						rightminparent = rightmin;
						rightmin = rightmin->_left;
					}
					cur->_key = rightmin->_key;

					//如果cur的右孩子是最小的,直接让cur指向右孩子的右孩子即可
					if (rightmin == cur->_right)
					{
						cur->_right = rightmin->_right;
					}
					else
					{
						rightminparent->_left = rightmin->_right;
					}

					delete rightmin;
					rightmin = nullptr;
				}
				return true;
			}
		}
		return false;
	}

五. 二叉搜索树的遍历

根据二叉搜索树的性质,如果我们以中序遍历(左根右),那我们的遍历的时候先是最小的,然后依次遍历更大的。那么最终的排序顺序是有序的!

中序遍历代码如下:

cpp 复制代码
	
	void _InOrder(const Node* root)
	{
		if (!root)
			return;
		//中序遍历
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

六.代码与测试

bstree.h

cpp 复制代码
#pragma once
#include<iostream>
#include<vector>
using namespace std;

template<class K>
struct BSTreeNode
{
	BSTreeNode* _left;
	BSTreeNode* _right;
	K _key;
	//构造函数
	BSTreeNode(const K& key)
		:_left(nullptr)
		, _right(nullptr)
		, _key(key)
	{}
};

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	//插入
	bool Insert(const K& key)
	{
		//第一次插入
		if (!_root)
		{
			_root = new Node(key);
			return  true;
		}

		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		cur = new Node(key);
		if (parent->_key > key)
		{
			parent->_left = cur;
		}
		else
		{
			parent->_right = cur;
		}
		return true;
	}

	//查找
	bool find(const K& key)
	{
		if (!_root)
			return false;

		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				cur = cur->_left;
			}
			else
			{
				return true;
			}
		}
		return false;
	}

	//删除
	bool Erase(const K& key)
	{
		//1.该节点只有一个孩子
		//2.该节点没有孩子
		//3.该节点有三个孩子
		Node* cur = _root;
		Node* parent = cur;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//找到了,开始删除
			{
				if (cur->_left == nullptr)
				{
					//当cur为根的时候要特判
					if (cur == _root)
					{
						_root = cur->_right;
					}

					if (cur == parent->_left)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
					delete cur;
					cur = nullptr;
				}
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = cur->_left;
					}

					if (cur == parent->_left)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
					delete cur;
					cur = nullptr;
				}
				else//两边都不为空
				{
					//找到左边最大,右边最小与当前值交互即可
					Node* rightmin = cur->_right;
					Node* rightminparent = cur;
					
					while (rightmin->_left)
					{
						rightminparent = rightmin;
						rightmin = rightmin->_left;
					}
					cur->_key = rightmin->_key;

					//如果cur的右孩子是最小的,直接让cur指向右孩子的右孩子即可
					if (rightmin == cur->_right)
					{
						cur->_right = rightmin->_right;
					}
					else
					{
						rightminparent->_left = rightmin->_right;
					}

					delete rightmin;
					rightmin = nullptr;
				}
				return true;
			}
		}
		return false;
	}

	//二叉搜索树不允许修改,修改后会导致二叉树失效
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

private:
	Node* _root = nullptr;
	
	void _InOrder(const Node* root)
	{
		if (!root)
			return;
		//中序遍历
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}
};

test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1
#include"bstree.h"

void test1()
{
	BSTree<int> tree;

	vector<int> arr;
	for (int i = 0; i < 10; i++)
	{
		int t = rand() % 10000;
		arr.push_back(t);
		tree.Insert(t);
	}

	tree.InOrder();
	for (int i = 0; i <= 9; i++)
	{
		cout << "删除" << arr[i] << "后:";
		tree.Erase(arr[i]);
		tree.InOrder();
	}
}

int main()
{
	srand((unsigned int)time(0));
	test1();
}

可以看到,排序的顺序是有序的

相关推荐
夏天的味道٥3 小时前
使用 Java 执行 SQL 语句和存储过程
java·开发语言·sql
IT、木易4 小时前
大白话JavaScript实现一个函数,将字符串中的每个单词首字母大写。
开发语言·前端·javascript·ecmascript
Mr.NickJJ5 小时前
JavaScript系列06-深入理解 JavaScript 事件系统:从原生事件到 React 合成事件
开发语言·javascript·react.js
Dream it possible!5 小时前
LeetCode 热题 100_字符串解码(71_394_中等_C++)(栈)
c++·算法·leetcode
Archer1946 小时前
C语言——链表
c语言·开发语言·链表
My Li.6 小时前
c++的介绍
开发语言·c++
功德+n6 小时前
Maven 使用指南:基础 + 进阶 + 高级用法
java·开发语言·maven
达斯维达的大眼睛6 小时前
qt小项目,简单的音乐播放器
开发语言·qt
uhakadotcom6 小时前
Apache CXF 中的拒绝服务漏洞 CVE-2025-23184 详解
后端·面试·github
uhakadotcom6 小时前
CVE-2025-25012:Kibana 原型污染漏洞解析与防护
后端·面试·github