#算法实现#—二叉搜索树的C++实现

二叉搜索树上的基本操作花费的时间与这棵树的高度成正比,对于有n个节点的搜索二叉树平均高度O(lg n),但是这样的情况并不能保证。

1)、二叉搜索树(BST)性质:
1. 若任意根节点存在左子树,则左子树的所有节点均小于等于根节点。
2. 若任意根节点存在右子树,则右子树的所有节点均大于等于根节点。
3. 子树同样是二叉搜索树。
搜索树的实现包括:插入操作、查找操作、删除操作、遍历操作。

2)、二叉搜索树的分块实现:

①、结构体搭建

//创建二叉树结构体 ,左孩子, 右孩子, 父节点,实例化操作;

struct STreeNode {
    	//成员
    	TreeKeyType key;
    	pSTreeNode  pLeftChild;
    	pSTreeNode  pRightChild;
    	//实例化操在这里插入代码片作
    	STreeNode (TreeKeyType Value){
    		key = Value;
    		pLeftChild = NULL;
    		pRightChild = NULL;
    	}
    }; 

②、创建一个二叉搜索数的类,用来管理独有的方法
//创建类来管理操作方法

    class CBinTree {
     public :
    	 CBinTree();;
    	 ~CBinTree();
    	 void Insert(TreeKeyType Value);
    	 void Insert(pSTreeNode pNode, TreeKeyType Value);
    	 pSTreeNode Search(TreeKeyType Value);
    	 pSTreeNode Search(pSTreeNode pNode, TreeKeyType Value);
    
    	 void Delete(TreeKeyType Value);
    	 pSTreeNode getSuccessor(pSTreeNode Value); 	//删除操作
    	 void Preorder();		//	前序遍历,非递归方法(借用堆栈)
    	 void Inorder();		//	中序遍历,非递归方法(借用堆栈)
    	 void Postorder();		//	后序遍历,非递归方法(借用堆栈)
    	 void PreorderRecursively(pSTreeNode pNode);	//	前序遍历,递归调用
    	 void InorderRecursively(pSTreeNode pNode);  	//	中序遍历,递归调用
    	 void PostorderRecursively(pSTreeNode pNode);	//	后序遍历,递归调用
    	 pSTreeNode GetMaxKey();     //  获得二叉查找树中元素值最大的节点
    	 pSTreeNode GetMinKey();     //  获得二叉查找树中元素值最小的节点
    public:
    	pSTreeNode pRoot;	//定义一个根节点
    };

③、插入
{15, 3, 20, 8, 10} 是如何顺序插入的,在纸上实现一遍以自己加深记忆。
在这里插入图片描述
当我想插入10这个元素时,需要根节点和val = 10 进行比较;
if(node - > 左孩子存在)
if (val < node.val) {
将下一个左孩子作为根节点继续比较 ;
}
if(node - > 右孩子存在)
if (val > node.val) {
将下一个右孩子作为根节点继续比较 ;
}
程序如下

   void CBinTree::Insert(pSTreeNode pNode, TreeKeyType Value) {
   	//目前的节点数据大于输入值
   	if (pNode->key > Value)
   	{
   		//如果左孩子空了,插入左孩子处,否则进入递归调用
   		if (pNode->pLeftChild == NULL)
   			pNode->pLeftChild = new STreeNode(Value);
   		else
   			Insert(pNode->pLeftChild, Value);
   	}else  {
   		if (pNode->pRightChild == NULL)
   			//找到叶子节点就进行插入
   			pNode->pRightChild = new STreeNode(Value);
   		else {
   			Insert(pNode->pRightChild, Value);
   		}
   	}
   }

④、查找操作
查找操作最为简单,从根节点开始遍历,val大,向右子树开始继续寻找,否则就向左子树开始继续寻找。这里也用了递归调用的方法,看起来一目了然。
程序如下:

    //入口函数
        pSTreeNode CBinTree::Search(TreeKeyType Value) {
        	return Search(pRoot, Value);
        }
       
        pSTreeNode CBinTree::Search(pSTreeNode pNode,TreeKeyType Value) {
        	if (pNode == NULL) {
        		return NULL;
        	}
        	if (pNode->key == Value) {
        		return pNode;
        	}else {
        		if (pNode->key < Value) {
        			return Search(pRoot->pRightChild, Value);
        		}else {
        			return Search(pRoot->pLeftChild, Value);
        		}
        	}
        }

⑤、删除操作
二叉树的删除操作最为麻烦,再删除之前,对着搜索二叉树的图进行试探,并且从其他大佬们那学到的,删除操作最好考虑全部的可能性,之后再看是否能优化:
1.删除叶子节点;
2.删除节点只有单独的右子树;
3.删除节点只有单独的左子树;
4.删除节点左子树右子树均存在;
首先找到需要删除的节点pFindNode,并定义一个pParentNode变量来标记删除节点的父节点。
情况1,如果删除的时叶子节点,需要将标记的pParentNode直接指向NULL即可删除,结束后释放pFindNode内存;
情况2,3,只存在单独的子树,当删除节点pFindNode不存在左子树的情况下,其右子树均大于pFindNode,那么,如果pFindNode是父节点pParentNode的左孩子,说明下面的一切都比父节点小,请都归顺于pParentNode,成为他的左子树吧,因为你们都比他小,同理删除节点是父节点的有孩子,那么全部归顺为右子树。
当删除节点pFindNode不存在右子树的情况下,其左子树均小于pFindNode,那么,如果pFindNode是父节点pParentNode的左孩子,说明下面的一切都比父节点小,请都归顺于pParentNode,成为他的左子树吧,因为你们都比他小。
情况4,删除的节点同时拥有左右子树,则重点是找到删除元素的后继节点,将后继节点(刚好比pFindNode大的数)为首,重新构造搜索二叉树子树,再将其衔接到pParentNode节点下。
如果你理解了删除的内涵,你会发现,情况4的复杂在于后继节点不能直接继承,需要找到间接继承人。

在这里插入图片描述
如图,后继点 9 一定是没有左子树的(因为他就是最左的),他的右子树,此时需要继承在sucParent的左子树下,因为他是恒小于sucParent的。

    //删除操作,二叉树删除操作最为麻烦,起码要考虑四种方式
    //1.叶子节点2.单独的左子树3.单独的右子树3.左右子树都存在
    void CBinTree::Delete(TreeKeyType Value) {
    	pSTreeNode pParentNode = pRoot;
    	pSTreeNode pFindNode = pRoot;
    	//	找到Value元素对应的节点
    	while (pFindNode != NULL)
    	{
    		if (pFindNode->key == Value)
    			break;
    		pParentNode = pFindNode;
    		if (pFindNode->key > Value)
    			pFindNode = pFindNode->pLeftChild;
    		else
    			pFindNode = pFindNode->pRightChild;
    	}
    	if (pFindNode == NULL)
    		return;
    	//	‘独生子家庭’ 和 ‘丁克’同时讨论,这两种轻情况优化再一起,通过一个pTemp来存储后继节点的头
    	if (pFindNode->pLeftChild == NULL || pFindNode->pRightChild == NULL)
    	{
    		//	一个子结点为空或者两个子结点都为空
    		pSTreeNode pTemp = NULL;
    		if (pFindNode->pLeftChild != NULL)
    			pTemp = pFindNode->pLeftChild;
    		else if (pFindNode->pRightChild != NULL)
    			pTemp = pFindNode->pRightChild;
    		if (pParentNode->pLeftChild == pFindNode)
    			pParentNode->pLeftChild = pTemp;
    		else
    			pParentNode->pRightChild = pTemp;
    		delete pFindNode;
    		pFindNode = NULL;
    	}
    	else
    	{
    		//	删除这边主要是找到后继节点作为交换节点,其他的重新构成一个树
    		//  再整体进行继承
    		pSTreeNode successor = getSuccessor(pFindNode);
    		
    		if (pFindNode == pRoot) {
    			pRoot = successor;
    		}else if (pFindNode == pParentNode->pLeftChild) {
    			pParentNode->pLeftChild = successor;
    		}else {
    			pParentNode->pRightChild = successor;
    		}
    		successor->pLeftChild = pFindNode->pLeftChild;
    	}
    }
    
    pSTreeNode CBinTree::getSuccessor(pSTreeNode delNode) {
    	pSTreeNode pTemp = delNode->pRightChild;   	//此处定义右孩子是为了找到后继节点
    	//所谓的后继节点其实就是右孩子的最左孩子---**比删除节点大的最小节点**;
    	pSTreeNode sucParent = NULL;
    	pSTreeNode successor = pTemp;
    	while (pTemp != NULL) {
    		//找到后继节点并建立新的索引
    		sucParent = successor;
    		successor = pTemp;
    		pTemp = pTemp->pLeftChild;
    	}
    	if (successor != delNode->pRightChild) {
    		sucParent->pLeftChild = successor->pRightChild;//都是比父节点小的,左边分支的右子树永远比这个小根节点小
    		successor->pRightChild = delNode->pRightChild;
    	}
    	//如果右孩子本来就和删除元素相等就不用变化了
    	return successor;
    
    }

⑥、获取最值操作
搜做二叉树的获取最值只与其深度有关,这也是为甚么平衡搜索二叉树的出现原因。
1.最右即为最大;
2.最左即为最小;
程序如下:

    	pSTreeNode CBinTree::GetMaxKey() {
    	pSTreeNode pMax = pRoot;
    	while (pMax->pLeftChild != NULL) {
    		if (pMax->pLeftChild != NULL)
    			pMax = pMax->pLeftChild;
    	}
    	return pMax;
    }
    pSTreeNode CBinTree::GetMinKey() {
    	pSTreeNode pMin = pRoot;
    	while (pMin->pRightChild != NULL) {   
    		if (pMin->pRightChild != NULL)
    			pMin = pMin->pRightChild;
    	}
    	return pMin;
    }

⑦、二叉树的遍历实现
递归实现先序遍历不谈,只说一点
在这里插入图片描述
在这里插入图片描述
对于递归上图所示的二叉树实际内部访问顺序为:
F C A A A C D B B B D D C F E H H H E G M M M G G E F
先序遍历 Preorder 实际上是递归顺序中每个节点的第一次出现的顺序;
中序遍历 Inorder 实际上是递归顺序中每个节点的第二次出现的顺序;
后续遍历Postorder实际上是递归顺序中每个节点的第三次出现的顺序;
递归实现程序如下:

    //递归试前序遍历实现输出
    void CBinTree::PreorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	cout << "" << pNode->key << " ";
    	PreorderRecursively(pNode->pLeftChild);
    	PreorderRecursively(pNode->pRightChild);
    }
    
    //递归调用实现中序遍历
    void CBinTree::InorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	InorderRecursively(pNode->pLeftChild);
    	cout << "" << pNode->key << " ";
    	InorderRecursively(pNode->pRightChild);
    }
    //递归调用实现后续遍历
    void CBinTree::PostorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	PostorderRecursively(pNode->pLeftChild);
    	PostorderRecursively(pNode->pRightChild);
    	cout << "" << pNode->key << " ";
    }

3)主程序清单

    #include <iostream>
    #include <stack>
    using namespace std;
    
    typedef int TreeKeyType;
    typedef struct STreeNode* pSTreeNode;
    
    //创建二叉树结构体 ,左右父
    struct STreeNode {
    	//成员
    	TreeKeyType key;
    	pSTreeNode  pLeftChild;
    	pSTreeNode  pRightChild;
    	//实例化操作
    	STreeNode (TreeKeyType Value){
    		key = Value;
    		pLeftChild = NULL;
    		pRightChild = NULL;
    	}
    }; 
    
    //创建类来管理操作方法
    class CBinTree {
     public :
    	 CBinTree();;
    	 ~CBinTree();
    	 void Insert(TreeKeyType Value);
    	 void Insert(pSTreeNode pNode, TreeKeyType Value);
    	 pSTreeNode Search(TreeKeyType Value);
    	 pSTreeNode Search(pSTreeNode pNode, TreeKeyType Value);
    
    	 void Delete(TreeKeyType Value);
    	 void Preorder();		//	前序遍历,非递归方法(借用堆栈)
    	 void Inorder();		//	中序遍历,非递归方法(借用堆栈)
    	 void Postorder();		//	后序遍历,非递归方法(借用堆栈)
    	 void PreorderRecursively(pSTreeNode pNode);	//	前序遍历,递归调用
    	 void InorderRecursively(pSTreeNode pNode);	//	中序遍历,递归调用
    	 void PostorderRecursively(pSTreeNode pNode);	//	后序遍历,递归调用
    	 pSTreeNode GetMaxKey();     //  获得二叉查找树中元素值最大的节点
    	 pSTreeNode GetMinKey();     //  获得二叉查找树中元素值最小的节点
    	 pSTreeNode getSuccessor(pSTreeNode Value);
    	 void FreeMemory(pSTreeNode pNode);	//	释放内存
    public:
    	pSTreeNode pRoot;
    };
    
    CBinTree::CBinTree()
    {
    	pRoot = NULL;
    }
    CBinTree::~CBinTree()
    {
    	if (pRoot == NULL)
    		return;
    	//FreeMemory(pRoot);
    }
    
    void CBinTree::Insert(TreeKeyType Value) {
    	if (pRoot == NULL) {
    		pRoot = new STreeNode(Value);
    	}
    	else {
    		Insert(pRoot, Value);
    	}
    }
    
    void CBinTree::Insert(pSTreeNode pNode, TreeKeyType Value) {
    	//目前的节点数据大于输入值
    	if (pNode->key > Value)
    	{
    		//如果左孩子空了,插入左孩子处,否则进入递归调用
    		if (pNode->pLeftChild == NULL)
    			pNode->pLeftChild = new STreeNode(Value);
    		else
    			Insert(pNode->pLeftChild, Value);
    	}else  {
    		if (pNode->pRightChild == NULL)
    			//找到叶子节点就进行插入
    			pNode->pRightChild = new STreeNode(Value);
    		else {
    			Insert(pNode->pRightChild, Value);
    		}
    	}
    }
    
    //search
    pSTreeNode CBinTree::Search(TreeKeyType Value) {
    	return Search(pRoot, Value);
    }
    
    pSTreeNode CBinTree::Search(pSTreeNode pNode,TreeKeyType Value) {
    	if (pNode == NULL) {
    		return NULL;
    	}
    	if (pNode->key == Value) {
    		return pNode;
    	}else {
    		if (pNode->key < Value) {
    			return Search(pRoot->pRightChild, Value);
    		}else {
    			return Search(pRoot->pLeftChild, Value);
    		}
    	}
    }
    
    //删除操作,二叉树删除操作最为麻烦,起码要考虑四种方式
    //1.叶子节点2.单独的左子树3.单独的右子树3.左右子树都存在
    void CBinTree::Delete(TreeKeyType Value) {
    	pSTreeNode pParentNode = pRoot;
    	pSTreeNode pFindNode = pRoot;
    	//	找到Value元素对应的节点
    	while (pFindNode != NULL)
    	{
    		if (pFindNode->key == Value)
    			break;
    		pParentNode = pFindNode;
    		if (pFindNode->key > Value)
    			pFindNode = pFindNode->pLeftChild;
    		else
    			pFindNode = pFindNode->pRightChild;
    	}
    	if (pFindNode == NULL)
    		return;
    	//	处理Value元素的父节点和Value元素的节点
    	if (pFindNode->pLeftChild == NULL || pFindNode->pRightChild == NULL)
    	{
    		//	一个子结点为空或者两个子结点都为空
    		pSTreeNode pTemp = NULL;
    		if (pFindNode->pLeftChild != NULL)
    			pTemp = pFindNode->pLeftChild;
    		else if (pFindNode->pRightChild != NULL)
    			pTemp = pFindNode->pRightChild;
    		if (pParentNode->pLeftChild == pFindNode)
    			pParentNode->pLeftChild = pTemp;
    		else
    			pParentNode->pRightChild = pTemp;
    		delete pFindNode;
    		pFindNode = NULL;
    	}
    	else
    	{
    		//	删除这边主要是找到后继节点作为交换节点,其他的重新构成一个树
    		//整体进行替换
    		pSTreeNode successor = getSuccessor(pFindNode);
    		
    		if (pFindNode == pRoot) {
    			pRoot = successor;
    		}else if (pFindNode == pParentNode->pLeftChild) {
    			pParentNode->pLeftChild = successor;
    		}else {
    			pParentNode->pRightChild = successor;
    		}
    		successor->pLeftChild = pFindNode->pLeftChild;
    	}
    }
    
    pSTreeNode CBinTree::getSuccessor(pSTreeNode delNode) {
    	pSTreeNode pTemp = delNode->pRightChild;
    	pSTreeNode sucParent = NULL;
    	pSTreeNode successor = pTemp;
    	while (pTemp != NULL) {
    		//找到后继节点并建立新的索引
    		sucParent = successor;
    		successor = pTemp;
    		pTemp = pTemp->pLeftChild;
    	}
    	if (successor != delNode->pRightChild) {
    		sucParent->pLeftChild = successor->pRightChild;//都是比父节点小的,左边分支的右子树永远比这个小根节点小
    		successor->pRightChild = delNode->pRightChild;
    	}
    	return successor;
    
    }
    
    pSTreeNode CBinTree::GetMaxKey() {
    	pSTreeNode pMax = pRoot;
    	while (pMax->pLeftChild != NULL) {
    		if (pMax->pLeftChild != NULL)
    			pMax = pMax->pLeftChild;
    	}
    	return pMax;
    }
    pSTreeNode CBinTree::GetMinKey() {
    	pSTreeNode pMin = pRoot;
    	while (pMin->pRightChild != NULL) {   
    		if (pMin->pRightChild != NULL)
    			pMin = pMin->pRightChild;
    	}
    	return pMin;
    }
    
    //非递归算法实现先序遍历
    void CBinTree::Preorder() {
    	if (pRoot == NULL) {
    		cout << "二叉树为空" << endl;
    		return;
    	}
    	stack<pSTreeNode> StackTree;
    	pSTreeNode pNode = pRoot;
    	while (!StackTree.empty() || pNode != NULL) {
    		while (pNode != NULL) {
    			cout << " " << pNode->key << " ";
    			StackTree.push(pNode);
    			pNode = pNode->pLeftChild;
    		}
    		pNode = StackTree.top();	//提出栈顶元素,但是不删除
    		StackTree.pop();
    		pNode = pNode->pRightChild;
    	}
    
    
    }
    
    //非递归算法实现中序遍历
    void CBinTree::Inorder() {
    	if (pRoot == NULL) {
    		cout << "二叉树为空" << endl;
    		return;
    	}
    	stack<pSTreeNode> StackTree;
    	pSTreeNode pNode = pRoot;
    	while (pNode != NULL || !StackTree.empty()) {
    		while (pNode != NULL) {
    			StackTree.push(pNode);
    			pNode = pNode->pLeftChild;
    		}
    		pNode = StackTree.top();
    		StackTree.pop();
    		cout << " " << pNode->key << " ";
         	}
    	//cout << endl;
    			
    }
    
    //非递归算法的后序遍历实现
    //将先序遍历的结果存入
    void CBinTree::Postorder() {
    	if (pRoot == NULL) {
    		cout << "二叉树为空" << endl;
    		return;
    	}
    	stack<pSTreeNode> StackTree;
    	stack<pSTreeNode> StackHelp;
    	pSTreeNode pNode = pRoot;
    	StackTree.push(pNode);
    	while (!StackTree.empty()) {
    		pNode = StackTree.top();
    		StackTree.pop();
    		StackHelp.push(pNode);
    		if (pNode->pLeftChild != NULL) {
    			StackTree.push(pNode->pLeftChild);
    		}
    		if (pNode->pRightChild != NULL) {
    			StackTree.push(pNode->pRightChild);
    		}
    		        
    	}
    	while (!StackHelp.empty()) {
    		pNode = StackHelp.top();
    		StackHelp.pop();
    		cout << " " << pNode->key << " ";
    	}
    	//cout << endl;
    
    
    }
    
    
    //递归试前序遍历实现输出
    void CBinTree::PreorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	cout << "" << pNode->key << " ";
    	PreorderRecursively(pNode->pLeftChild);
    	PreorderRecursively(pNode->pRightChild);
    }
    
    //递归调用实现中序遍历
    void CBinTree::InorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	InorderRecursively(pNode->pLeftChild);
    	cout << "" << pNode->key << " ";
    	InorderRecursively(pNode->pRightChild);
    }
    //递归调用实现后续遍历
    void CBinTree::PostorderRecursively(pSTreeNode pNode) {
    	if (pNode == NULL)
    		return;
    	PostorderRecursively(pNode->pLeftChild);
    	PostorderRecursively(pNode->pRightChild);
    	cout << "" << pNode->key << " ";
    }
    
    int  main() {
    	CBinTree* pBinTree = new CBinTree();
    	if (pBinTree == NULL)
    		return 0;
    	// 1.实现插入数据,存储成搜索二叉树的形式
    	pBinTree->Insert(15);
    	pBinTree->Insert(3);
    	pBinTree->Insert(20);
    	pBinTree->Insert(8);
    	pBinTree->Insert(10);
    	pBinTree->Insert(18);
    	pBinTree->Insert(6);
    	pBinTree->Insert(1);
    	pBinTree->Insert(26);
    	//数据已经插入进去了,现在前序遍历一遍
    	pSTreeNode  pRoot = pBinTree->pRoot;
    	cout << "递归先序遍历为:" ;
    	pBinTree->PreorderRecursively(pRoot);
    	cout << endl;
    
    	cout << "非递归先序遍历为:";
    	pBinTree->Preorder();
    	cout << endl;
    
    
    	cout << "递归中序遍历为:";
    	pBinTree->InorderRecursively(pRoot);
    	cout << endl;
    
    	cout << "非递归中序遍历为:";
    	pBinTree->Inorder();
    	cout << endl;
    
    	cout << "递归后序遍历为:";
    	pBinTree->PostorderRecursively(pRoot);
    	cout << endl;
    
    	cout << "非递归后序遍历为:";
    	pBinTree->Postorder();
    	cout << endl;
    	
    	pSTreeNode pMaxNode = pBinTree->GetMaxKey();
    	pSTreeNode pMinNode = pBinTree->GetMinKey();
    	cout <<"输出最大值为:" <<pMaxNode->key;
    	cout << "输出最小值为:" << pMinNode->key;
    	cout << endl;
    
    	int DeleteKey = 8;
    	pBinTree->Delete(DeleteKey);
    	cout << "删除元素" << DeleteKey << "之后的递归前序遍历:";
    	pBinTree->PreorderRecursively(pRoot);
    	cout << endl;
    
    
    }s

这篇程序是从大佬那学习来的,为了加深自己理解,在此总结记录,新手,有错请见谅。
参考文献
/https://www.cnblogs.com/Renyi-Fan/p/8253136.html/
/https://blog.csdn.net/lining0420/article/details/76167901/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章