二叉搜索树

1.二叉搜索树的特点

  • 二叉搜索树也可以是空树
  • 如果它的左子树不为空,则左子树上的所有节点都小于根节点的值
  • 如果它的右子树不为空,则右子树上的所有节点都大于根节点的值
  • 它的左右子树都是二叉树搜索树

2.二叉树的操作 

 1.查找

bool Find(const K& key)

2.插入

bool Insert(const K& key)

插入操作和查找操作的逻辑是一样的

  • 从根节点开始寻找,如果比根节点小,就向左寻找,如果比根节点大,就向右寻找。
  • 查找:如果找到空还没找到目标值,就说明值不存在
  • 插入:如果找到空就可以插入节点了
		bool Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (key < cur->_key)
				{
					cur = cur->_left;
				}
				else if (key > cur->_key)
				{
					cur = cur->_right;
				}
				else
					return true;
			}
			return false;

		}
bool Insert(const K& key)
{
	Node* pnode = new Node(key);
	//如果是第一个节点直接插入
	if (_root == nullptr)
	{
		_root = pnode;
		return true;
	}

	else
	{
		//设置一个父亲节点用于连接
		Node* parent = _root;
		Node* cur = _root;
		while (cur)
		{
			if (key < cur->_key)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (key > cur->_key)
			{
				parent = cur;
				cur = cur->_right;
			}
			else
				return false;
		}
		//这里需要判断一下这个节点在父节点的左边还是右边
		if (key < parent->_key)
			parent->_left = pnode;
		else if (key > parent->_key)
			parent->_right = pnode;
		return true;
	}
}

3 .删除

a.删除节点,首先要找到要删除的节点,此时需要使用查找的逻辑

b.如果找到需要删除的节点,就需要判断这个节点有几个孩子

  1. 如果该节点没有左右孩子,直接删除该节点即可
  2. 如果该节点只有左孩子,把这个节点的左孩子托付给该节点的父亲,然后删除该节点
  3. 如果该节点只有右孩子,把这个节点的右孩子托付给该节点的父亲,然后删除该节点
  4. 如果该节点有左右孩子,需要使用替换法,将(最大左子树节点/最小右子树)节点的值,和该节点的值交换,然后删除最大左子树节点/最小右子树)节点

 

bool Erase(const K& key)
{
	Node* parent = _root;
	Node* cur = _root;
	while (cur)
	{
		if (key < cur->_key)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (key > cur->_key)
		{
			parent = cur;
			cur = cur->_right;
		}
		else//找到了要删除的数
		{
			//分三种情况
			//1.没有孩子 2.有一个孩子 3.有两个孩子
			//1和2情况可以合为一个情况
			if (cur->_left == nullptr)//把cur的右孩子托付给parent
			{
				//cur是parent的左孩子
				if (parent->_key > cur->_key)
				{
					parent->_left = cur->_right;
				}
				else//cur是parent的右孩子
				{
					parent->_right = cur->_right;
				}
				delete cur;
				return true;
			}
			else if (cur->_right == nullptr)//把cur的左孩子托付给parent
			{
				//cur是parent的左孩子
				if (parent->_key > cur->_key)
				{
					parent->_left = cur->_left;
				}
				else//cur是parent的右孩子
				{
					parent->_right = cur->_left;
				}
				delete cur;
				return true;
			}
			else//删除节点有两个孩子
			{
				//使用替换法,使用最大左孩子的替代
				Node* MinRight = cur->_left;
				Node* MinParent = cur;
				while (MinRight->_right)
				{
					MinParent = MinRight;
					MinRight = MinRight->_right;
				}
				cur->_key = MinRight->_key;

				//最大左孩子没有右孩子
				if (MinParent->_left == MinRight)
				{
					MinParent->_left = MinRight->_left;
				}
				else if (MinParent->_right== MinRight)
				{
					MinParent->_right = MinRight->_left;
				}
				delete MinRight;
				return true;
			
			}

		}
	}

	//找不到删除的数
	return false;
}

4.递归写法

  1. 查找递归写法没有什么要注意,直接看代码即可
  2. 插入递归写法,函数参数要使用引用指针,这样可以直接改变指针的指向,不需要记录父节点指针了
    	bool FindR(const K& key)
    		{
    			return _FindR(_root, key);
    		}
    
    		bool _FindR(Node* root, const K& key)
    		{
    			if (root == nullptr)
    				return false;
    			else if (root->_key > key)
    				_FindR(root->_left, key);
    			else if(root->_key < key)
    				_FindR(root->_right, key);
    			else
    				return true;
    		}
    
    		bool InsertR(const K& key)
    		{
    			return _InsertR(_root, key);
    		}
    
    
    		//这里使用引用,可以直接修改root
    		bool _InsertR(Node*& root, const K& key)
    		{
    			//等于NULL在此处插入,root是引用
    			if(root==nullptr)
    			{
    				Node* NewNode = new Node(key);
    				root = NewNode;
    				return true;
    			}
    			if (root->_key > key)
    			{
    				return _InsertR(root->_left,key);
    			}
    			else if (root->_key < key)
    			{
    				return _InsertR(root->_right,key);
    			}
    			else
    				return false;
    						
    		}
    
  3. 删除递归写法,也需要使用引用指针。需要注意:在交换节点的值之后,想要删除最大右孩子,需要调用_EraseR(root->_left, key),而不是 _EraseR(MinRight, key),因为需要通过引用指针去改变父亲节点的指向,如果调用_EraseR(MinRight, key)就无法改变。

    		bool EraseR(const K& key)
    		{
    			return _EraseR(_root, key);
    		}
    
    		bool _EraseR(Node*& root, const K& key)
    		{
    			if (root->_key > key)
    			{
    				return _EraseR(root->_left, key);
    			}
    			else if (root->_key < key)
    			{
    				return _EraseR(root->_right, key);
    			}
    			else
    			{
    				Node* del = root;
    				if (root->_left == nullptr)
    				{
    					root = root->_right;
    				}
    				else if (root->_right == nullptr)
    				{
    					root = root->_left;
    				}
    				else//两个孩子
    				{
    					//使用替换法,使用最大左孩子的替代
    					Node* MinRight = root->_left;
    					while (MinRight->_right)
    					{
    						MinRight = MinRight->_right;
    					}
    
    					std::swap(root->_key, MinRight->_key);
    					//把最大右孩子交换一下,继续递归一下(右边是空直接)
    
    
    					return _EraseR(root->_left, key);//使用root->_left可以修改指向
    					//return _EraseR(MinRight, key);//不能写成这样,这样会导致内存泄露
    				}
    				delete del;
    				return true;
    			}
    		}
    		

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值