【数据结构】搜索二叉树的(递归与非递归)实现,包括:增Insert,删Remove,查Find

搜索二叉树,是二叉树一种特殊的结构。

特点:

(1)每个节点都有一个关键码,并且关键码不重复。

(2)左子树上的每个节点的关键码都小于根节点的关键码。

(3)右子树上的每个节点的关键码都大于根节点的关键码。

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

下面,为方便大家理解,我举例画一个搜索二叉树,如下:

代码见下:

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using namespace std;

//搜索二叉树的节点结构
template<class K,class V>
struct SearchBinaryNode
{
	SearchBinaryNode* _left;//左子树
	SearchBinaryNode* _right;//右子树
	K _key;//每个节点是(_key,_value)形式
	V _value;

	//搜索二叉树的节点的构造函数
	SearchBinaryNode(const K& key, const V& value)
		:_left(NULL)
		, _right(NULL)
		, _key(key)
		, _value(value)
	{}
};

template<class K,class V>
class SearchBinaryTree
{
	typedef SearchBinaryNode<K, V> Node;
public:
	//搜索二叉树的构造函数
	SearchBinaryTree()
		:_root(NULL)
	{}

	//给搜索二叉树插入一个节点
	bool Insert(const K& key, const V& value)
	{
		//搜索二叉树无节点,直接挂上该节点
		if (_root == NULL)
		{
			_root = new Node(key, value);
			return true;
		}

		//遍历搜索二叉树,要插入的节点比每个根节点小,往左子树走,否则大往右子树走
		Node* cur = _root;
		Node* prev = NULL;//保存节点,便于插入节点链在上面
		while (cur)
		{
			if (key < cur->_key)
			{
				prev = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				prev = cur;
				cur = cur->_right;
			}
			else
			{
				return false;//搜索二叉树要求无重复关键码
			}
		}

		//此时不知道链在左子树还是右子树,分情况
		if (prev->_key > key)
		{
			prev->_left = new Node(key, value);
		}
		else if (prev->_key < key)
		{
			prev->_right = new Node(key, value);
		}
		return true;
	}

	//寻找搜索二叉树的一个节点
	bool Find(const K& key,const V& value)
	{
		if (_root == NULL)
		{
			return false;
		}
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
			{
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				cur = cur->_right;
			}
			else
			{
				return true;
			}
		}
		return true;
	}

	//删除搜索二叉树的一个节点
	bool Remove(const K& key,const V& value)
	{
		//无结点:返回
		if (_root == NULL)
		{
			return false;
		}

		//一个节点:直接删
		else if (_root->_left == NULL && _root->_right == NULL)
		{
			if (_root->_key == key)
			{
				delete _root;
				_root = NULL;
				return true;
			}
			else
				return false;
		}

		//多个节点
		else
		{
			Node* cur = _root;
			Node* parent = NULL;
			while (cur)
			{
				//往左子树上走去找
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}

				//往右子树上走去找
				else if (cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}

				//找到该节点,确定如何去删除该节点
				else
				{
					//要删除的节点无左子树
					if (cur->_left == NULL)
					{
						//问题:这时候要处理一个特殊情况,如果删到只剩下根节点parent:5,以及它的右子树parent->_right:9时,parent为空,
						//访问parent的成员会导致程序崩溃
						//解决:parent为空时,则将parent->right置成根_root,删掉parent节点
						if (parent == NULL)
						{
							_root = cur->_right;
							delete cur;
							cur = NULL;
							return true;
						}

						//两种情况:
						//(1)删除的cur在parent的右子树上时,cur->_right链在parent->_right上
						if (parent->_key<cur->_key)
						{
							parent->_right = cur->_right;
						}

						//(2)否则,cur->_right链在parent->_left
						else if (parent->_key>cur->_key)
						{
							parent->_left = cur->_right;
						}
						delete cur;
						cur = NULL;
						return true;
					}

					//要删除的节点无右子树
					else if (cur->_right == NULL)
					{
						//同cur->_left为空
						if (parent == NULL)
						{
							_root = cur->_left;
							delete cur;
							cur = NULL;
							return true;
						}

						//两种情况:
						//(1)删除的cur在parent的右子树上时,cur->_left链在parent->_right上
						if (parent->_key < cur->_key)
						{
							parent->_right = cur->_left;
						}

						//(2)否则,cur->_left链在parent->_left
						else if (parent->_key > cur->_key)
						{
							parent->_left = cur->_left;
						}

						//删除释放
						delete cur;
						cur = NULL;
						return true;
					}

					//左右子树都不为空,分两种情况
					else
					{
						//firstLeft是要删除的节点cur的右子树(代码走到这一步,说明左右必然不为空,则肯定不为空)
						Node* firstLeft = cur->_right;

						//往firstLeft的左子树上走,找最左节点
						//(1)左为空,交换,把链在firstLeft的一串节点(仅有右子树)链在cur->_right上
						if (firstLeft->_left == NULL)
						{
							swap(cur->_key, firstLeft->_key);
							swap(cur->_value, firstLeft->_value);
							cur->_right = firstLeft->_right;
							delete firstLeft;
							firstLeft = NULL;
							return true;
						}

						//左为空,一直往其左子树上走,找到,交换
						else
						{
							Node* tmp = firstLeft;

							//tmp为最左节点
							while (tmp->_left)
							{
								firstLeft = tmp;
								tmp = tmp->_left;
							}
							swap(cur->_key, tmp->_key);
							swap(cur->_value, tmp->_value);

							//把链在tmp的一串节点(仅有右子树)链在firstLeft->_right上
							firstLeft->_left = tmp->_right;
							delete tmp;
							tmp = NULL;
							return true;
						}
					}
				}
			}
		}
	}

	//调用_Insert_R
	void Insert_R(const K& key, const V& value)
	{
		_Insert_R(_root, key, value);
	}

	//调用_Find_R
	Node* Find_R(const K& key, const V& value)
	{
		Node* ret = _Find_R(_root, key, value);
		return ret;
	}

	//调用_Remove_R
	void Remove_R(const K& key, const V& value)
	{
		_Remove_R(_root,key, value);
	}
	//调用_InOrder
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

protected:

	//中序遍历打印:便于查看是否构造搜索二叉树是否正确
	void _InOrder(Node* root)
	{
		if (root == NULL)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_key << "  ";
		_InOrder(root->_right);
	}

	//给搜索二叉树插入一个节点的递归写法
	/*注意:递归写法传引用,创建的节点要放在_root上,而不传引用的话,相当于在_root上拷贝
	的临时形参变量root上建了个节点,对_root本身没有作用*/
	void _Insert_R(Node*& root, const K& key, const V& value)
	{
		if (root == NULL)
		{
			root = new Node(key, value);
			return;
		}
		if (root->_key < key)//往右子树上递归
		{
			_Insert_R(root->_right, key, value);
		}
		else if (root->_key > key)//往左子树上递归
		{
			_Insert_R(root->_left, key, value);
		}
		else//搜索二叉树要求无重复关键码
			return;
	}

	//寻找搜索二叉树的一个节点的递归写法
	Node* _Find_R(Node* root, const K& key, const V& value)
	{
		if (root == NULL)
		{
			return NULL;
		}
		if (root->_key < key)//往右子树上递归
		{
			return _Find_R(root->_right, key, value);
		}
		else if (root->_key > key)//往左子树上递归
		{
			return _Find_R(root->_left, key, value);
		}
		else
			return root;//找到,返回节点指针
	}

	//删除搜索二叉树的一个节点的递归写法
	void _Remove_R(Node*& root, const K& key, const V& value)
	{
		//空节点,返回
		if (root == NULL)
			return;	

		//一个节点:若为该节点,删除。否则不处理
		if (root->_left == NULL && root->_right == NULL)
		{
			if (root->_key == key)
			{
				delete root;
				root = NULL;
				return;
			}
			else
				return;
		}

		//往左子树上递归寻找
		if (root->_key > key)
		{
			_Remove_R(root->_left, key, value);
		}

		//往右子树上递归寻找
		else if (root->_key < key)
		{
			_Remove_R(root->_right, key, value);
		}

		//找到该节点,且该节点的左右子树不空,确定如何去删除该节点
		else
		{
			Node* del = NULL;

			//右子树为空,此时不需和非递归写法一样分两种情况(parent->_right是否)分别去处理,这就是递归的优势所在。
			//parent->_right为空,上一层递归来的是root->_left,即此时的root,链在root->_left上
			//parent->_right不为空时,上一层递归来的root->_right,即此时的root,链在root->_left上
			//所以,当parent->_right为空或者不为空时,均是上一层递归而来,root->_left或者root->_right,都可以用此时的root表示
			if (root->_right == NULL)
			{
				del = root;
				root = root->_left;
				delete del;
				del = NULL;
			}

			//同上,parent->_left为空时,上层递归来的root->_right,即此时的root,链在root->_right上
			//parent->_right不为空时,上层递归来的root->_left,是此时的root,链在root->_right上
			//而这两种情况,都可以用递归解决,无需分别处理
			else if (root->_left == NULL)
			{
				del = root;
				root = root->_right;
				delete del;
				del = NULL;
			}

			//左右子树都不为空,若不递归的话,要分两种情况处理。要删除的root节点要先找到右子树节点firstLeft,
			//再往firstLeft的左子树走,去找最左节点,此时可能左子树为空或者不为空。借用递归的优势,不管是上层的左还是右子树传过来的,
			//都是上一层递归来的,即都是此时的root.一起处理掉。
			//所以,这两种情况合在一起,可以
			else
			{
				Node* firstLeft = root->_right;
				Node* tmp = firstLeft;

				//找最左节点
				while (tmp->_left)
				{
					firstLeft = tmp;
					tmp = tmp->_left;
				}

				//找到最左边节点,保存,交换,删除
				swap(tmp->_key, root->_key);
				swap(tmp->_value, root->_value);
				del = root;
				root = tmp->_right;
				delete del;
				del = NULL;
			}
		}
	}

protected :
	Node* _root;
};

void TestSearchBinaryTree()
{
	typedef SearchBinaryNode<int, int> Node;
	SearchBinaryTree<int, int> sbt;
	sbt.Insert(5, 1);
	sbt.Insert(3, 1);
	sbt.Insert(2, 1);
	sbt.Insert(6, 1);
	sbt.Insert(0, 1);
	sbt.Insert(9, 1);
	sbt.Insert(7, 1);
	sbt.Insert(8, 1);
	sbt.Insert(4, 1);
	sbt.Insert(1, 1);
	sbt.InOrder();

	sbt.Find(3,1);

	//删除的完整测试用例:将上序插入的序列删除顺序打乱,并全部删除,所有删除情况基本都可涵盖。
	sbt.Remove(1, 1);
	sbt.Remove(3, 1);
	sbt.Remove(4, 1);
	sbt.Remove(7, 1);
	sbt.Remove(6, 1);
	sbt.Remove(0, 1);
	sbt.Remove(8, 1);
	sbt.Remove(2, 1);
	sbt.Remove(5, 1);
	sbt.Remove(9, 1);
	sbt.InOrder();

	sbt.Insert_R(5, 1);
	sbt.Insert_R(3, 1);
	sbt.Insert_R(2, 1);
	sbt.Insert_R(6, 1);
	sbt.Insert_R(0, 1);
	sbt.Insert_R(9, 1);
	sbt.Insert_R(7, 1);
	sbt.Insert_R(8, 1);
	sbt.Insert_R(4, 1);
	sbt.Insert_R(1, 1);
	sbt.InOrder();

	Node* ret = sbt.Find_R(3, 1);
	if (ret == NULL)
	{
		cout << "not exist!" << endl;
	}
	else
		cout << ret->_key << endl;

	sbt.Remove_R(1, 1);
	sbt.Remove_R(3, 1);
	sbt.Remove_R(4, 1);
	sbt.Remove_R(7, 1);
	sbt.Remove_R(6, 1);
	sbt.Remove_R(0, 1);
	sbt.Remove_R(8, 1);
	sbt.Remove_R(2, 1);
	sbt.Remove_R(5, 1);
	sbt.Remove_R(9, 1);
	sbt.InOrder();
}

int main()
{
	TestSearchBinaryTree();
	system("pause");
	return 0;
}
时间: 2024-10-14 11:24:51

【数据结构】搜索二叉树的(递归与非递归)实现,包括:增Insert,删Remove,查Find的相关文章

数据结构——二叉树遍历之“递归与非递归遍历”

简述 二叉树的遍历分为先序遍历.中序遍历和后序遍历.如下图所示: 递归遍历 private void bianli1(List<Integer> list, TreeNode root) { // 先序遍历 if (root == null) { return; } list.add(root.val); bianli1(list, root.left); bianli1(list, root.right); } private void bianli2(List<Integer>

数据结构二叉树的递归与非递归遍历之 实现可编译(1)java

前一段时间,学习数据结构的各种算法,概念不难理解,只是被C++的指针给弄的犯糊涂,于是用java,web,javascript,分别去实现数据结构的各种算法. 二叉树的遍历,本分享只是以二叉树中的先序遍历为例进行说明,中序遍历和后序遍历,以此类推! 二叉树递归与非递归遍历的区别,虽然递归遍历,跟容易读懂,代码量少,运算快,但是却容易出现溢出的问题,所以所以非递归遍历,在处理千万级的运算量时会先的很有用处. 二叉树的先序遍历:先访问根节点,再访问先后访问左右节点.如图: 二叉树的递归遍历之java

Java数据结构系列之——树(4):二叉树的中序遍历的递归与非递归实现

package tree.binarytree; import java.util.Stack; /** * 二叉树的中序遍历:递归与非递归实现 * * @author wl * */ public class BiTreeInOrder { // 中序遍历的递归实现 public static void biTreeInOrderByRecursion(BiTreeNode root) { if (root == null) { return; } biTreeInOrderByRecursi

Java数据结构系列之——树(5):二叉树的后序遍历的递归与非递归实现

package tree.binarytree; import java.util.Stack; /** * 二叉树后序遍历的递归与非递归实现 * * @author wl * */ public class BitreePostOrder { // 后序遍历的递归实现 public static void biTreePostOrderByRecursion(BiTreeNode root) { if (root == null) { return; } biTreePostOrderByRe

【数据结构与算法】二叉树深度遍历(非递归)

据说这个笔试面试的时候非常easy考到,所以写到这里. 图示 代码实现 /** * 源代码名称:TreeIteratorNoRecursion.java * 日期:2014-08-23 * 程序功能:二叉树深度遍历(非递归) * 版权:[email protected] * 作者:A2BGeek */ import java.util.Stack; public class TreeIteratorNoRecursion { class TreeNode<T> { private T mNod

二叉树递归与非递归遍历,最近公共父节点算法

#include <iostream> #include <stack> using namespace std; #define MAX 100 //字符串最大长度 typedef struct Node //二叉树结点 { char data; Node *lchild,*rchild; } *Btree; void createBT(Btree &t); //先序构造二叉树 void preorder(Btree &t); //二叉树递归先序遍历 void i

二叉树的前序、中序、后序遍历(递归、非递归)实现

本文部分来源于CSDN兰亭风雨大牛的原创.链接为http://blog.csdn.net/ns_code/article/details/12977901 二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的.二叉树有前.中.后三种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但其开销也比较大,而若采用非递归方法实现三种遍历,则要用栈来模拟实现(递归也是用栈实现的).下面先简要介绍三种遍历方式的递归实现,再详细介绍三种遍

C实现二叉树(模块化集成,遍历的递归与非递归实现)

C实现二叉树模块化集成 实验源码介绍(源代码的总体介绍):header.h : 头文件链栈,循环队列,二叉树的结构声明和相关函数的声明.LinkStack.c : 链栈的相关操作函数定义.Queue.c : 循环队列的相关操作函数定义.BinTree.c : 二叉树的相关操作的函数定义,层序序列生成二叉树,二叉树的前序序列.中序序列.后序序列的递归和非递归实现,求叶子结点的递归与非递归实现,求树高.我目前还是新手且第一次写类似的博客,文章中难免会有错误!如发现错误,望各路大神能够指出!详见源代码

图的深度优先搜索 递归和非递归实现 c++版本

本文参考了李春葆版本的数据结构上机指导,但是原版是c代码, 本文用了c++实现,并且修复了深度优先搜索非递归的一个bug. graph.cpp文件: #include "graph.h" #include <queue> #include <stack> int visited[MAXV ]; MGraph ::MGraph(int A[100][10], int nn , int ee) { e= ee ; n= nn ; for (int i=0;i<