二叉搜索树的非递归实现

本文介绍了二叉搜索树的概念及其性质,详细阐述了非递归插入节点的思路,包括空树和非空树两种情况,并通过实例解析了插入过程。同时讨论了删除节点的策略,尤其是替换法在删除非叶子节点时的应用,指出在某些情况下不需要使用替换法。文章附有相关代码实现。

      “折半查找”这个词相信大家在学习C语言的时候已经很熟悉,由于数组中数字的排列是有序的,那么我们就可以将要查找的数字与最中间的数进行比较,每次比较都可以把范围缩小一半,依次下去,很好的提高了查找效率。而在数据结构中,我们也有处理这种问题的对应模型------二叉搜索树(也称为二叉排序树)。

二叉搜索树的性质:

(1)每个节点都有一个作为搜索依据的关键码(key),所有节点的关键码互不相同

(2)左子树上所有节点的关键码(key)都小于根节点的关键码(key)

(3)右子树上所有节点的关键码(key)都大于根节点的关键码(key)

(4)左右子树都是二叉搜索树

以下面这颗树为例:


二叉搜索树节点的定义:

struct SearchBinaryTreeNode
{
	SearchBinaryTreeNode<K>* _left;
	SearchBinaryTreeNode<K>* _right;
	K _key;

	//节点的初始化
	SearchBinaryTreeNode(const K&k)
		:_left(NULL)
		, _right(NULL)
		, _key(k)
	{}
};

插入:

这里说一下插入的思路:分两种情况讨论---

(1)如果本来就是一颗空树,则直接插入

(2)树不为空,则将待插元素与根节点的值进行比较,若大于根节点则移向右子树,再与右子树的根节点进行比较,依次下去,直到找到合适的插入位置。。。小于根节点同理

还是直接上图帮助理解:(就是上面那棵树,插入12)


空树比较简单,直接new一个节点插入就好;这里着重讲一下树不为空的情况-->

(1)如图,首先定义一个cur指针,最初指向根节点,每一次比较,cur指针都进行一次移动(上面蓝色箭头和圈的序号表示移动的位置和次序);

(2)12比5大,则说明在5的右子树,于是cur向右移,移到7的位置;12比7大,,说明在7的右子树,则cur再向右移,移到8的位置;以此类推,当cur移到9所在位置时,12还比9大,于是cur再向右移,这时cur为空,因为9已经是叶子节点了。

到这里,最关键的一步就来了:cur这时已经为空,那么如何把12链到9的右边?可见,定义一个指针远远不够,必须再定义一个parent指针,用来保存cur的路径(即cur每走一步,都要先赋给parent),这样最终parent就指向9,把12直接链上去就好。

附上代码:

bool Insert(const K&key)
{
	if (_root == NULL)
	{
		_root = new Node(key);
		return true;
	}
	Node* cur = _root;
	Node*parent = cur;
	while (cur)
	{
		if (key>cur->_key)//插入节点的数值比当前节点大,则cur向右子树移动
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key<cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else
		{
			return false;//要插入的节点元素值与这棵树中的节点元素值相等,不能插入
		}
	}
	//到这里cur已经指向了叶子节点的下一步,需要将插入值与叶子节点进行比较,来确定插在叶子节点的左子树还是右子树
	if (key>parent->_key)
	{
		parent->_right = new Node(key);
	}
	else
	{
		parent->_left = new Node(key);
	}
	return true;
}

删除:

删除比较复杂,我们从最简单的情况入手:

(1)首先,如果树只有一个根节点--->直接删除,并置空

(2)删除的是叶子节点--->删除后,还需考虑父节点指向其的指针要置空

接下来就是较复杂的情况:非叶子节点(又可以分为3种情况)

(1)左为空,右不为空

(2)左不为空,右为空

(3)左、右都不为空

再仔细想,我们可以把叶子节点和非叶子节点的(1)和(2)合并,那么问题就简单化了

综上,归纳总结如下:


重点:替换法删除(左、右都不为空的情况)


这里再来说一个值得关注的点,左为空或者右为空的情况需不需要替换法?(我之前就把问题复杂化了)


可以看出,替换和不替换的中序遍历结果都一样,且都符合二叉搜索树的条件,所以这里不需要使用替换法删除。

附上代码:

bool Remove(const K&key)
{
	Node*parent = NULL;
	Node*cur = _root;
	while (cur)
	{
		if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else//已找到要删除的节点
		{
			Node*del = cur;
			if (cur->_left == NULL)//cur的左为空(右为空或右不为空)
			{
				if (parent == NULL)//若删除的是根节点,则再赋予新的根节点
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_left == cur)//cur在parent的左边
					{
						parent->_left = cur->_right;//如果是叶子节点则这句的作用为:将parent的左置空;如果右边还有子树则这句的作用为:将parent的左指针指向子树
					}
						else//cur在parent的右边
					{
						parent->_right = cur->_right;//如果是叶子节点则这句的作用为:将parent的右置空;如果右边还有子树则这句的作用为:将parent的右指针指向子树
					}
				}
			}
			else if (cur->_right == NULL)//cur的右为空(左为空或左不为空)
			{
				if (parent == NULL)
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_left == cur)//cur在parent的左边
					{
						parent->_left == cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
			}
			else//cur的左,右都不为空
			{
				parent = cur;
				Node*subleft = cur->_right;
				//寻找右子树的最左节点,替换法删除
				while (subleft->_left)
				{
					parent = subleft;
					subleft = subleft->_left;
				}
				del = subleft;
				cur->_key = subleft->_key;//替换
				if (parent->_left == subleft)//右子树的最左节点存在
				{
					parent->_left = subleft->_right;
				}
				else//右子树的最左节点就是右子树的根
				{
					parent->_right = subleft->_right;
				}
			}
			delete del;
			return true;
		}
	}
	return false;
}

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

最后附上所有源代码:

//SearchBinaryTree.h
#include<iostream>
using namespace std;
template<class K>
//定义二叉搜索树节点
struct SearchBinaryTreeNode
{
	SearchBinaryTreeNode<K>* _left;
	SearchBinaryTreeNode<K>* _right;
	K _key;

	//节点的初始化
	SearchBinaryTreeNode(const K&k)
		:_left(NULL)
		, _right(NULL)
		, _key(k)
	{}
};
//定义二叉搜索树
template<class K>
class SearchBinaryTree
{
	typedef SearchBinaryTreeNode<K> Node;
public:
	SearchBinaryTree()
		:_root(NULL)
	{}
	~SearchBinaryTree()
	{
		Destory(_root);
	}

	//插入
	bool Insert(const K&key)
	{
		if (_root == NULL)
		{
			_root = new Node(key);
			return true;
		}
		Node* cur = _root;
		Node*parent = cur;//这里之所以要定义parent指针,是因为要记录cur走的路径。每比较一次,cur指针移动一步,cur每次移动之前都要把它给parent,以便于最终找到节点的插入位置
		while (cur)
		{
			if (key>cur->_key)//插入节点的数值比当前节点大,则cur向右子树移动
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key<cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;//要插入的节点元素值与这棵树中的节点元素值相等,不能插入
			}
		}
		//到这里cur已经指向了叶子节点的下一步,需要将插入值与叶子节点进行比较,来确定插在叶子节点的左子树还是右子树
		if (key>parent->_key)
		{
			parent->_right = new Node(key);
		}
		else
		{
			parent->_left = new Node(key);
		}
		return true;
	}
	//删除
	bool Remove(const K&key)
	{
		Node*parent = NULL;
		Node*cur = _root;
		while (cur)
		{
			if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else//已找到要删除的节点
			{
				Node*del = cur;
				if (cur->_left == NULL)//cur的左为空(右为空或右不为空)
				{
					if (parent == NULL)//若删除的是根节点,则再赋予新的根节点
					{
						_root = cur->_right;
					}
					else
					{
						if (parent->_left == cur)//cur在parent的左边
						{
							parent->_left = cur->_right;
						}
						else//cur在parent的右边
						{
							parent->_right = cur->_right;
						}
					}
				}
				else if (cur->_right == NULL)//cur的右为空(左为空或左不为空)
				{
					if (parent == NULL)
					{
						_root = cur->_left;
					}
					else
					{
						if (parent->_left == cur)//cur在parent的左边
						{
							parent->_left == cur->_left;
						}
						else
						{
							parent->_right = cur->_left;
						}
					}
				}
				else//cur的左,右都不为空
				{
					parent = cur;
					Node*subleft = cur->_right;
					//寻找右子树的最左节点,替换法删除
					while (subleft->_left)
					{
						parent = subleft;
						subleft = subleft->_left;
					}
					del = subleft;
					cur->_key = subleft->_key;//替换
					if (parent->_left == subleft)//右子树的最左节点存在
					{
						parent->_left = subleft->_right;
					}
					else//右子树的最左节点就是右子树的根
					{
						parent->_right = subleft->_right;
					}
				}
				delete del;
				return true;
			}
		}
		return false;
	}

	//查找
	Node*Find(const K&key)
	{
		if (_root == NULL)
		{
			return NULL;
		}
		Node*cur = _root;
		while (cur)
		{
			if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return NULL;
	}
	//中序遍历
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

protected:
	void Destory(Node*&root)
	{
		if (root == NULL)
		{
			return;
		}
		Destory(root->_left);
		Destory(root->_right);
		delete root;
		root = NULL;
	}
	void _InOrder(Node*root)
	{
		if (root == NULL)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << " ";
		_InOrder(root->_right);
	}

protected:
	Node*_root;
};


//test.c
#include"SearchBinaryTree.h"
#include<stdlib.h>
void TestBSTree()
{
	int a[] = { 5, 3, 4, 1, 7, 8, 2, 6, 0, 9 };
	SearchBinaryTree<int> t1;
	for (size_t i = 0; i < sizeof(a) / sizeof(a[10]); i++)
	{
		t1.Insert(a[i]);
	}

	t1.InOrder();
	SearchBinaryTreeNode<int>*ret = t1.Find(9);
	cout << ret->_key << endl;
	t1.Remove(0);
	t1.Remove(1);
	t1.Remove(2);
	t1.Remove(3);
	t1.Remove(4);
	t1.Remove(5);
	t1.Remove(6);
	t1.Remove(7);
	t1.Remove(8);
	t1.Remove(9);
	//t1.Remove(10);
	t1.InOrder();
}

int main()
{
	TestBSTree();
	system("pause");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值