顺序二叉树的基础操作(C++)

本文详细介绍了二叉查找树的基本概念、结构定义及其主要操作,包括递归与非递归的查找、插入、删除等算法,并探讨了二叉树的遍历方式与线索化过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#ifndef _BSTREE_
#define _BSTREE_

template<typename T>
struct BSNode{
	BSNode(T t) :value(t),lchild(nullptr),rchild(nullptr);

	T value;
	BSNode *lchild;
	BSNode *rchild;
	BSNode *parent;
	int LTag;
	int RTag;//0:指向左右孩子的指针;1:指向前驱/后继的线索
};

//二叉查找树类
/*1.BSTree的根节点root,这是唯一的数据成员
2.操作的外部接口与内部实现接口。例如 preOrder()为提供给用户使用的接口,接口声明为public;而preOrder(AVLTreeNode* pnode)是类内部为了递归操作所使用的接口,接口声明为private。*/
template<typename T>
class BSTree
{
public:
	BSTree();
	~BSTree();

	void preOrder();   //前序遍历二叉树
	void inOrder();        //中序遍历二叉树
	void postOrder();   //后序遍历二叉树
	void layerOrder();    //层次遍历二叉树

	BSNode<T>* search_recursion(T key);   //递归查找
	BSNode<T>* search_Iterator(T key);      //迭代查找

	T search_mininum();       //查找最小元素
	T search_maxinum();      //查找最大元素

	BSNode<T>* successor(BSNode<T> *x);      //查找指定节点的后继节点
	BSNode<T>* predecessor(BSNode<T> *x);         //查找指定节点的前驱节点

	void insert(T key);     //插入指定值节点
	void remove(T key);     //删除指定值节点
	void destory();      //销毁二叉树
	void print();           //打印二叉树
	

	void CreateBSTree();  //创建二叉树
	void InTreading();//中序遍历线索化

private:
	BSNode<T> *root;    //根节点
private:
	BSNode<T>* search(BSNode<T> *&p, T key);
	void remove(BSNode<T> *p, T key);
	void preOrder(BSNode<T> *p);
	void postOrder(BSNode<T>* p);
	T search_minimun(BSNode<T>* p);
	T search_maximum(BSNode<T>* p);
	void destory(BSNode<T>* &p);
	void InTreading(BSNode<T> *p);
};

//递归创建二叉树
template<typename T>
void BSTree<T>::CreateBSTree(BSNode<T> *x)
{
	T ch;
	cin >> ch;
	if (ch == '#')
		*x = nullptr;
	else{
		*x = new BSNode<T>(ch);
		CreateBSTree(&(*x)->lchild);
		CreateBSTree(&(*x)->rchild);
	}
}


/*中序遍历线索化二叉树
线索化的意义:充分利用空指针,记住结点的前驱、后继(不同遍历次序,前驱后继不同)*/
template<typename T>
void BSTree<T>::InTreading()
{
	InTreading(root);
}
template<typename T>
void BSTree<T>::InTreading(BSNode<T> *p)
{
	BSNode<T> *pre = new BSNode<T>;
	*pre = nullptr;

	if (p)//递归左子树线索化
	{
		InTreading(p->lchild);//没有左孩子
		if (!p->lchild)
		{
			p->LTag = 1;//前驱线索
			p->lchild = pre;//左孩子指针指向前驱
		}
		if (!pre->rchild)//前驱没有右孩子
		{
			pre->RTag = 1;//后继线索
			pre->rchild = p;//前驱右孩子指针指向后继(当前结点p)
		}
		pre = p;//保持pre指向p的前驱
		InTreading(p->rchild);//递归右子树线索化
	}
}


/*插入新节点:
1.如果是空树,则创建一个新节点,新节点作为根
2.寻找元素合适的插入位置:新元素与当前结点进行比较,若值大于当前结点,则从右子树进行寻找;否则从左子树进行寻找.
3.找到插入位置之后,以元素的值构建新节点,插入二叉排序树中*/
template<typename T>
void BSTree<T>::insert(T key)
{
	BSNode<T> *pparent = nullptr;
	BSNode<T> *pnode = root;

	while (pnode != nullptr)         // 寻找合适的插入位置
	{
		pparent = pnode;
		if (key > pnode->value)
			pnode = pnode->rchild;
		else if (key < pnode->value)
			pnode = pnode->lchild;
		else
			break;
	}

	pnode = new BSNode<T>(key);//以元素的值构建新节点
	if (pparent == nullptr)//如果是空树
		pnode = root; //则新节点为根
	else{
		if (key > pparent->value)
			pparent->rchild = pnode;//否则新节点为其父节点的左孩
		else
			pparent->lchild=pnode;//或右孩
	}
	pnode->parent = pparent;//指明新节点的父节点 
}

/*遍历平衡二叉树:前序遍历、中序遍历、后序遍历*/
/*前序遍历:
若二叉树为空,则空操作返回,否则先访问根节点,然后前序遍历左子树,再前序遍历右子树。(简记为:VLR)*/
template<typename T>
void BSTree<T>::preOrder()
{
	preOrder(root);
};
template<typename T>
void BSTree<T>::preOrder(BSNode<T> *p)
{
	if (p != nullptr)
	{
		cout << p->value << " ";
		preOrder(p->lchild);
		preOrder(p->rchild);
	}
	cout << endl;
}

/*中序遍历:
若二叉树为空,则空操作返回,否则从根节点开始,中序遍历根节点的左子树,然后访问根节点,最后中序遍历右子树。(简记为:LVR)*/
template<typename T>
void BSTree<T>::inOrder()
{
	inOrder(root);
}
template<typename T>
void BSTree<T>::inOrder(BSNode<T> *p)
{
	if (p != nullptr)
	{
		inOrder(p->lchild);
		cout << p->value << " ";
		inOrder(p->rchild);
	}
	cout << endl;
}

/*后序遍历:
若树为空,则返回空操作,否则从左到右先叶子后节点的方式遍历访问左右子树,左右子树都访问结束,才访问根节点。(简称LRV)*/
template<typename T>
void BSTree<T>::postOrder()
{
	postOrder(root)
}
template<typename T>
void BSTree<T>::postOrder(BSNode<T>* p)
{
	if (p != nullptr)
	{
		postOrder(p->lchild);
		postOrder(p->rchild);
		cout << p->value << " ";
	}
	cout << endl;
}

/*前驱:中序遍历
1.它有左子树,则左子树根节点为其前驱节点
2.它没有左子树,且它本身为右子树,则其父节点为其前驱节点
3.它没有左子树,且它本身为左子树,则它的前驱节点为“第一个拥有右子树的父节点”*/
template<typename T>
BSNode<T> *BSTree<T>::predecessor(BSNode<T> *pnode)
{
	if (pnode->lchild != nullptr)
	{
		pnode = pnode->lchild;
		while (pnode->lchild != nullptr)
			pnode = pnode->rchild;
		return pnode;
	}

	BSNode<T>* pparent = pnode->parent;
	while (pparent != nullptr && pparent->lchild == pnode)//如果进入循环,则是第三种情况;否则为第二种情况
	{
		pnode = pparent;
		pparent = pparent->parent;
	}
	return pparent;
}

/*后继:中序遍历
1.它有右子树;则其后继节点为其右子树的最左节点
2.它没有右子树,但它本身是一个左孩子,则后继节点为它的双亲
3.它没有右子树,但它本身是一个右孩子,则其后继节点为“具有左孩子的最近父节点”*/
template<typename T>
BSNode<T>* BSTree<T>::successor(BSNode<T> *pnode)
{
	if (pnode->rchild != nullptr)
	{
		pnode = pnode->rchild;
		while (pnode->lchild != nullptr)
			pnode = pnode->lchild;
		return pnode;
	}

	BSNode<T>* pparent = pnode->parent;
	while (pparent != nullptr && pparent->rchild == pnode)//如果进入循环,则是第三种情况;否则为第二种情况
	{
		pnode = pparent;
		pparent = pparent->parent;
	}
	return pparent;
}

/*删除节点:
1.被删除节点同时有左子树与右子树:将前驱节点的值保存在当前结点,继而删除前驱节点
2.被删除节点只有左子树或只有右子树:用子树替换被删节点
3.被删除节点没有子树:直接删除节点*/
template<typename T>
void BSTree<T>::remove(T key)
{
	remove(root, kye);
}
template<typename T>
void BSTree<T>::remove(BSNode<T>* pnode, T key)
{
	if (pnode != nullptr)
	{
		if (pnode->value == key)
		{
			BSNode<T>* pdel == nullptr;

			if (pnode->lchild == nullptr || pnode->rchild == nullptr)//情况2、3,被删除的节点只有左右子树,或者没有孩子
				pdel = pnode;
			else
				pdel = predecessor(pnode);//情况1:被删除的节点同时又左右子树,则将该节点的前驱的值保存到该节点

			//此时,被删除的节点只有一个孩子(或没有孩子),保存该孩子指针
			BSNode<T>* pchild = nullptr;
			if (pdel->lchild != nullptr)
				pchild = pdel->lchild;
			else if (pdel->rchild != nullptr)
				pchild = pdel->rchild;

			//让孩子指向被删除节点的父节点
			if (pchild != nullptr)
				pchild->parent = pdel->parent;

			//如果要删除的节点是头结点,注意更改root的值
			if (pdel->parent == nullptr)
				root = pchild;
			else if (pdel->parent->lchild == pdel)//如果要删除的节点不是头节点,要注意更改它的双亲节点指向新的孩子节点
				pdel->parent->lchild = pchild;
			else if (pdel->parent->rchild == pdel)
				pdel->parent->rchild = pchild;

			if (pnode->value != pdel->value)
				pnode->value = pdel->value;//将前驱的值代替该节点的值

			delete pdel;
		}
		else if (pnode->value > key)
			remove(pnode->lchild, key);
		else
			remove(pnode->rchild, key);
	}
}

/*查找元素:
元素的查找过程与元素的插入过程一致,也是在不断地与当前结点进行比较,若值比当前节点的值大,则在右子树进行查找,
若值比当前节点的值小,则在左子树进行查找,可以看到这是一个很适合递归操作的过程。
而由于二叉排序树这种左小右大的节点特征,也很容易进行非递归查找*/
//非递归
template<typename T>
BSNode<T>* BSTree<T>::search_Iterator(T key)
{
	BSNode<T>* pnode = root;
	while (pnode!=nullptr)
	{
		if (key == pnode->value)//找到
			return pnode;
		else if (key > pnode->value)//大了,在节点右子树查找
			pnode = pnode->rchild;
		else//小了,在节点左子树查找
			pnode = pnode->lchild;
	}
	return nullptr;
}

//递归
template<typename T>
BSNode<T>* BSTree<T>::search_recursion(T key)
{
	return search(root, key);
}
template<typename T>
BSNode<T>* BSTree<T>::search(BSNode<T>* pnode, T key)
{
	if (pnode != nullptr)
	{
		if (key == pnode->value)
			return pnode;
		else if (key > pnode->value)
			return search(pnode->lchild, key);
		else
			return search(pnode->rchild, key);
	}
	else
		return nullptr;
}

//查找最值元素
//最小元素
template<typename T>
T BSTree<T>::search_minimun()
{
	return search_minimun(root);
}
template<typename T>
T BSTree<T>::search_minimun(BSNode<T>* pnode)
{
	if (pnode->lchild != nullptr)
		return search_minimun(pnode->lchild);
	return pnode->value;
}
//最大元素
template<typename T>
T BSTree<T>::search_maximun()
{
	return search_maximun(root);
}
template<typename T>
T BSTree<T>::search_maximun(BSNode<T>* pnode)
{
	if (pnode->rchild != nullptr)
		return search_maximun(pnode->rchild);
	return pnode->value;
}

//销毁二叉树:使用遍历递归销毁二叉树
template<typename T>
void BSTree<T>::destory()
{
	return destory(root);
}
template<typename T>
void BSTree<T>::destory(BSNode<T>* pnode)
{
	if (p != nullptr)//后序
	{
		if (pnode->lchild != nullptr)
			destory(pnode->lchild);
		if (pnode->rchild != nullptr)
			destory(pnode->rchild);
		delete pnode;
		pnode = nullptr;
	}
}
#endif

### 二叉树的基本操作及其C++实现 #### 节点创建 在C++中,可以通过结构体`struct`来定义二叉树的节点。每个节点包含三个部分:存储的数据值、指向左子节点的指针和指向右子节点的指针。通过构造函数初始化这些成员变量[^1]。 ```cpp #include <iostream> using namespace std; struct TreeNode { int val; TreeNode* left; TreeNode* right; TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} }; ``` #### 插入节点 为了向二叉树中插入新节点,通常需要递归地找到合适的位置并分配内存给新的节点对象。下面是一个简单的递归插入方法: ```cpp TreeNode* insert(TreeNode* node, int value) { if (node == nullptr) { return new TreeNode(value); } if (value < node->val) { node->left = insert(node->left, value); } else if (value > node->val){ node->right = insert(node->right, value); } return node; } ``` 此代码片段展示了如何基于比较当前节点值与待插入值的关系决定继续往哪个方向深入查找位置[^2]。 #### 遍历方式 遍历是指按照某种顺序访问二叉树中的每一个结点一次的过程。常见的三种主要遍历方式分别为前序遍历、中序遍历以及后序遍历。此外还有层序遍历(广度优先搜索BFS),它按层次从上到下逐层访问所有节点。 ##### 前序遍历(Pre-order Traversal) 前序遍历遵循根-左-右的原则进行访问。 ```cpp void preOrderTraversal(TreeNode *root) { if(root != nullptr){ cout << root->val << " "; // 访问根节点 preOrderTraversal(root->left); // 左子树 preOrderTraversal(root->right); // 右子树 } } ``` ##### 中序遍历(In-order Traversal) 中序遍历则采用左-根-右的方式依次处理各个节点数据项。 ```cpp void inOrderTraversal(TreeNode *root) { if(root != nullptr){ inOrderTraversal(root->left); // 左子树 cout << root->val << " "; // 访问根节点 inOrderTraversal(root->right); // 右子树 } } ``` ##### 后序遍历(Post-order Traversal) 而后序遍历最后才去读取父级的信息,在实际应用当中可能用于释放资源等情况较多见。 ```cpp void postOrderTraversal(TreeNode *root) { if(root != nullptr){ postOrderTraversal(root->left); // 左子树 postOrderTraversal(root->right); // 右子树 cout << root->val << " "; // 访问根节点 } } ``` 以上每种遍历都利用了递归来简化逻辑控制流程的设计思路。 #### 层次遍历(Level Order Traversal 或 BFS 广搜) 不同于上述几种深度优先策略,层次遍历借助队列辅助完成横向扫描动作。 ```cpp #include <queue> void levelOrderTraversal(TreeNode* root) { queue<TreeNode*> q; if(root!=nullptr)q.push(root); while(!q.empty()){ TreeNode* current=q.front();q.pop(); cout<<current->val<<" "; if(current->left!=nullptr)q.push(current->left); if(current->right!=nullptr)q.push(current->right); } } ``` 这段程序首先把初始顶点压栈,之后不断循环直至队列为空为止;每次取出头部元素打印其数值后再将其左右孩子分别加入等待序列之中。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值