当前位置: 首页 > article >正文

数据结构 【搜索二叉树】

        搜索二叉树是STL中map和set的重要铺垫,学好搜索二叉树有助于理解map和set的特性。搜索二叉树也是一种二叉树结构,只是多了一些特定的性质。

        一棵搜索二叉树可以为空树,如果不为空时,一定满足下面的性质。

  • 若它的左子树不为空,则所有左子树上的节点都小于根节点的键值。
  • 若它的右子树不为空,则所有的右子树上的节点都大于根节点的键值。
  • 它的左右子树也分别为搜索二叉树。

         基于先描述在组织的思想,我们可以尝试搭建一个搜索二叉树结构。

1、框架搭建

        先描述具体的节点,再进行节点间的组织链接。

template<class K>
class BSTreeNode
{
public:
	BSTreeNode<K>* _left;
	BSTreeNode<K>* _right;
	K _key;

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

template<class K>
class BSTree
{
	typedef BSTreeNode<K> Node;
public:
	BSTree()
		:_root(nullptr)
	{}
private:
	Node* _root;
};

        框架构造还是六个字:先描述,再组织。

2、节点插入

        数据进行插入时要分情况讨论:

  1. 搜索二叉树为空时,可以直接插入;
  2. 不为空时,如果插入的键值大于根节点就去根节点的右边继续寻找位置进行插入,如果插入的键值小于根节点就去根节点的左边寻找位置进行插入;如果搜索二叉树种存在要插入的键值就返回false;
	bool Insert(const K& key)
	{
		if (_root == nullptr)
		{
			_root = new Node(key);
			return true;
		}
		else
		{
			//搜索二叉树不为空
			Node* parent = _root;
			Node* cur = _root;
			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);
			cur->_key > parent->_key ? parent->_right = cur : parent->_left = cur;
			return true;
		}
	}

      在寻找空位置的过程中,也要同时记录当前节点的父节点位置。主要是方便进行后面的链接。

3、中序遍历

        由于中序遍历需要传入当前节点的地址进行递归,这里为了方便外部的对象调用。对中序遍历进行了一次封装。在封装之后的函数内部传入_root;

//省略类域	
    void InOrder()
	{
		_InOrder(_root);
	    cout << endl;
	}
private:
    void _InOrder(Node* root)
{
	if (root == nullptr)
	{
		return;
	}

	_InOrder(root->_left);
	cout << root->_key << " ";
	_InOrder(root->_right);
}

        对上述代码进行简单的测试:

#include<iostream>
using namespace std;
#include"BinarySearchTree.h"

void TestBSTree()
{
	int arr[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };
	BSTree<int> bs;
	for (auto e : arr)
	{
		bs.Insert(e);
	}

	bs.InOrder();

}

int main()
{
	TestBSTree();
	return 0;
}

        运行结果:

 4、节点删除

        节点的删除比较复杂。需要分下面的情况进行分析:

  • 要删除的节点没有子节点,可以直接进行删除,再删除之后要对它的父节点的位置进行置空操作。

  • 如果要删除的节点只有一个子节点,同样也可以直接删除,在删除之后需要在进行判断,如果删除节点的子节点小于删除节点的父节点,那么就链接在父节点的左边;反之,就连接在右边。

  • 如果要删除的节点左右子树都非空树,那么这时就需要进行替换删除。为了保证替换后的树形结构仍然满足搜索二叉树的性质,这时的替换要求为:只能和删除节点的左子树的最右节点或者右子树的最左节点进行替换。通常情况下使用左子树的最右节点进行替换。

上面的1和2可以归纳为一点,当删除的节点的左子树为空树时,就将删除节点的父节点链接上删除节点的右节点;反之,就将删除节点的父节点链接上删除节点的左节点。

	bool Erase(const K& key)
	{
		Node* parent = _root;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				//找到了,进行删除操作
				//1.左子树为空
				if (cur->_left == nullptr)
				{
					if (cur == _root)
					{
						_root = _root->_right;
					}
					else
					{
						//判断删除节点的位置,进行原位置链接
						if (parent->_left == cur)
						{
							parent->_left = cur->_right;
						}
						else
						{
							parent->_right = cur->_right;
						}
					}
				}
				//2.右子树为空
				else if (cur->_right == nullptr)
				{
					if (cur == _root)
					{
						_root = _root->_left;
					}
					else
					{
						if (parent->_left == cur)
						{
							parent->_left = cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
				}
				//3.左右子树都不为空
				else
				{
					Node* leftMaxParent = cur;
					Node* leftMax = cur->_left;
					//寻找左子树的最右节点 且找到它的父节点
					while (leftMax->_right)
					{
						leftMaxParent = leftMax;
						leftMax = leftMax->_right;
					}
					std::swap(cur->_key, leftMax->_key);

					if (leftMaxParent->_left == leftMax)
					{
						leftMaxParent->_left = leftMax->_left;
					}
					else
					{
						leftMaxParent->_right = leftMax->_left;
					}
					//下面统一删除cur;
					cur = leftMax;
				}
				delete cur;
				cur = nullptr;
				return true;
			}
		}
		return false;
	}


http://www.kler.cn/a/563094.html

相关文章:

  • 暨南大学智科院电子信息复试Tips
  • w803|联盛德|WM IoT SDK2.X测试|pinout|(2):w803开发板简介
  • 剑指 Offer II 033. 变位词组
  • [算法--前缀和] 矩阵区域和
  • 计算机基础:二进制基础01,比特与字节
  • 说说 Spring MVC 的执行流程
  • 全国各省山峰分布SHP数据详解及其在科学研究与旅游规划中的应用
  • 排序算法适合的场景
  • ffmpeg av_find_input_format的作用
  • Linux通过设备名称如何定位故障硬盘
  • 洛谷 B2006:地球人口承载力估计 ← float 类型
  • WebView中操作视频播放,暂停
  • Redis 中有序集合(Sorted Set)的使用方法
  • 用PySpark和PyTorch实现跨境支付Hive数据仓库的反洗钱数据分析
  • 物联网+大数据,智慧公租房管理系统构建未来社区
  • 嵌入式硬件篇---阶跃函数冲激函数
  • 分布式主键生成服务
  • 【清华大学】DeepSeek从入门到精通系列教程 第五版:DeepSeek与AI幻觉 pdf文档下载
  • 聊聊制造企业数字化质量管理的业务架构与流程
  • Qt | Excel创建、打开、读写、另存和关闭