C ++ Gong practitioners manual XXVII (binary search trees and simple implementation of the principle of interpretation)

Binary search tree description:

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:
若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
它的左右子树也分别为二叉搜索树

Binary search tree operations

1.二叉搜索树的查找: 
  若根结点不为空:
  如果根节点的val == 查找的val直接返回true
  如果根节点的val >  查找的val那就去根节点的右子树去寻找
  如果根节点的val <  查找的val那就去根节点的左子树去寻找
  否则返回false
2.二叉搜索树的插入
二叉树的插入位置只有三种可能
  1》左子树为空的结点
  2》右子树为空的结点
  3》叶子结点
插入的结点一定不会是左右孩子都在的结点,这个是由二叉树的性质来决定的,要是还是没有办法理解的话可以画个图看一看

a.删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点
b.删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点
c.在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题

Delete binary search tree, illustration

Here Insert Picture Description
Here Insert Picture Description

template<class T>
struct BSTNode
{
	BSTNode(T _value = T())
	:left(nullptr), right(nullptr)
	, value(_value)
	{}

	BSTNode<T>* left;
	BSTNode<T>* right;
	T value;
};
template<class T>
class BSTree
{
	typedef BSTNode<T> Node;
public:
	BSTree()
		:root(nullptr)
	{}
	~BSTree()
	{
		_destory(root);
	}
	BSTree(const BSTree& tree)
	{
		this->root = _copy(tree.root);
	}
	Node* _copy(Node* _root)
	{
		if (_root == nullptr)
			return nullptr;
		Node* temp = new Node(_root->value);
		temp->left  = _copy(_root->left);
		temp->right = _copy(_root->right);
		return temp;
	}
	BSTree& operator=(const BSTree& tree)
	{
		if (this != &tree)
		{
			_destory(this->root);
			this->root =  _copy(tree.root);
		}
		return *this;
	}
	bool Insert(const T& val)
	{
		if (root == nullptr)
		{
			root = new Node(val);
			return true;
		}
		else
		{
			Node* pCur = root;  //用来查找结点
			Node* pParent = nullptr;  //用来记录当前正在查找结点的双亲结点
			while (pCur)
			{
				pParent = pCur;
				if (pCur->value > val)
					pCur = pCur->left;
				else if (pCur->value < val)
					pCur = pCur->right;
				else
					return false;  //默认是不能插入重复的元素
			}
			pCur = new Node(val);
			if (pParent->value > val)
				pParent->left = pCur;
			else
				pParent->right = pCur;
		}
		return true;
	}
	Node* Find(const T& val)
	{
		if (root == nullptr)
			return nullptr;
		Node* pCur = root;
		while (pCur)
		{
			if (pCur->value > val)
				pCur = pCur->left;
			else if (pCur->value < val)
				pCur = pCur->right;
			else
				return pCur;
		}
		return nullptr;
	}
	bool Erase(const T& val)
	{
		if (root == nullptr)
			return false;
		//遍历整个二叉搜索树,找val
		Node* pCur = root;
		Node* pParent = nullptr;
		while (pCur)
		{
			
			if (pCur->value == val)
				break;
			else if (pCur->value > val)
			{
				pParent = pCur;     //这一快只能这么写,之前我把pParent = pCur放在外面,但是但是错了,这里的逻辑就是这样,不理解的话可以画图
				pCur = pCur->left;
			}
			else
			{
				pParent = pCur;
				pCur = pCur->right;
			}
		}
		//不存在val
		if (pCur == nullptr)
			return false;
		//1.待删除结点的左孩子为空
		//2.待删除结点的右孩子为空
		//3.待删除结点的左右孩子都不为空
		//4.待删除结点的左右孩子都为空
		if (pCur->left == nullptr && pCur->right == nullptr)  //左右孩子都为空
		{
			if (pParent->left == pCur)
				pParent->left = nullptr;
			else
				pParent->right = nullptr;
			delete pCur;

		}
		else if (pCur->left == nullptr)  //待删除的结点是左孩子为空的情况
		{
			if (pCur->value == val)  //刚好根节点就是要删除的结点
			{                        
				root = pCur->right;   //因为没有左孩子所以树的头指针,直接指向根节点的右孩子就可以
			}
			else  //删除的结点不是头结点
			{
				//判断删除结点是双亲结点的左孩子还是右孩子
				if (pParent->left == pCur)
					pParent->left = pCur->right;  //如果是删除结点是双亲结点的左孩子,那就让删除节点的双亲结点的左孩子指向删除结点的右孩子
				else
					pParent->right = pCur->right; //反之相反
			}
			delete pCur;
		}
		else if (pCur->right == nullptr)    //待删除结点的右孩子为空
		{
			if (pCur->value == val)  
			{
				root = pCur->left;   
			}
			else  //删除的结点不是头结点
			{
				//判断删除结点是双亲结点的左孩子还是右孩子
				if (pParent->left == pCur)
					pParent->left = pCur->left;  //如果是删除结点是双亲结点的左孩子,那就让删除节点的双亲结点的左孩子指向删除结点的右孩子
				else
					pParent->right = pCur->left; //反之相反
			}
			delete pCur;
		}
		else  //待删除结点的左右节点都不为空的时候
		{
			if (pCur->left != nullptr && pCur->right != nullptr)
			{
				//去找待删除结点的右孩子中的最小值,或者左孩子中的最大值,其实都一样,这里我们就使用右孩子中的最小值
				Node* p = pCur->right;
				pParent = pCur;  //标记双亲结点,这里的标记主要是为了防止没有进入下面while循环的情况
				while (p->left)
				{
					pParent = p; //标记双亲结点
					p = p->left;
				}
				pCur->value = p->value;//替换
				//下面这两种情况比较特殊需要拿出来讨论
				if (p == pParent->left)
				{
					pParent->left = p->right;
				}
				else
				{
					pParent->right = p->right;
				}
				delete p;
			}
			return true;
		}
		return false;
	}
	void Inorder()
	{
		_Inorder(this->root);
	}
private:
	void _Inorder(Node* tree)
	{
		if (tree == nullptr)
			return;
		_Inorder(tree->left);
		cout << tree->value << " ";
		_Inorder(tree->right);
	}
	void _destory(Node*& tree) //为了在内部改变外部的指针,这里使用的就是指针的引用
	{
		if (tree)
		{
			_destory(tree->left);
			_destory(tree->right);
			delete tree;
			tree = nullptr;
		}
	}
private:
	Node* root;
};
int main()
{

	BSTree<int> BST;
	BST.Insert(1);
	BST.Insert(3);
	BST.Insert(7);
	BST.Insert(5);
	BST.Insert(4);
	BST.Insert(6);
	BST.Insert(2);
	BST.Inorder();
	BST.Erase(1);
	cout << endl;
	BST.Inorder();
	cout << endl;
	BST.Erase(2);
	BST.Inorder();
	cout << endl;
	BST.Erase(5);
	BST.Inorder();
	cout << endl;
	BST.Erase(4);
	BST.Inorder();
	cout << endl;
	cout << BST.Find(7)<< endl;
	BSTree<int> BST2;
	BST2 = BST;
	BST2.Inorder();
	BSTree<int> BST3(BST2);
	BST3.Inorder();

	return 0;
}

First in the face of this code I appeared analysis of the issues:
First, that is a node in case of deletion, that is, left and right subtrees are not empty cases
Here Insert Picture Description
need to draw two pictures
Here Insert Picture Description
Here Insert Picture Description
in both cases has been given.

This is followed by the following two codes
Here Insert Picture Description
above these two codes, one code insertion node, delete node is a code, the insertion and deletion, the need relates to find the node, the insertion node when the need to find a suitable node, the appropriate node to the left or right
to find the right node of the process needs time to record the current traversal nodes parent nodes, but different preservation parent node way .
In the insertion node when the last pCure find is an empty node, and really want it is to insert the left or right of the parent node of pParent of pCure.
In the delete node when the same need to traverse the entire tree, traversing the entire tree when the need to save the parent node currently traversed node, is not the same as when deleting nodes, pCure is no longer empty node, but the real point needs to be deleted, and pParent that is to be deleted node parent node, so these two codes are not the same.

Here Insert Picture Description
Here Insert Picture Description
What problem is the deduction code.

Performance analysis of two numbers fork

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。
但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为:log n;
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:n / 2;

Question: If degenerate into a single tree branch, performance binary search tree lost. It can be improved, no matter in what order to insert a key code, you can make the performance of the best binary search tree?
AVL (balanced binary search tree)

Published 230 original articles · won praise 28 · views 9309

Guess you like

Origin blog.csdn.net/weixin_43767691/article/details/103734579