二叉树的进阶一:模拟二叉搜索树(binary search tree)

二叉搜索树的概念

给出一组数据:int arr[] = { 2,4,5,6,8,9,3,7,1,0};

关系模型为下图所示:

通过上图可以发现二叉搜索树:

非空左子树所有值比根节点小

非空右子树的所有值都比根结点大

左右子树都是新的二叉搜索树

二叉树的搜索方式:

所以在寻找一个数时,都是经过根,与根进行比较然后比根小的在左,比根大的在右

二叉搜索树的中序遍历是一个有序的数列:

关系模型

中序结果:0 1 2 3 4 5 6 7 8 9

二叉树的模拟实现:

注意:一定要自己动手多画图要不容易乱(绕)

(非递归方式)

结点的构造:

创建一个结点必须要写出结点的类,对结点进行封装

注意:不要忘记要对类进行默认初始化构造函数的编写

template<class k>
struct BSTreenode
{
	BSTreenode<k>* left;
	BSTreenode<k>* right;
	k _key;

	BSTreenode(k key)
		:left(nullptr)
		, right(nullptr)
		, _key(key)
	{

	}
};

二叉搜索树的编写:

template<class k>
class BSTree
{
typedef BSTreenode<k> node;//重写结点名字,方便

private:
	node* _root=nullptr;//定义头指针,在声明时赋值,就不写初始化构造函数
};
插入insert:

如何插入呢?

是不是要和树里的数据进行对比,找到正确的位置然后插入。

1、

要确定插在哪里要确定位置,用双指针法因为要找到合适位置(nullptr)后就知道该位置的上一个结点然后连接(探测指针为nullptr该位置就是要插入的位置)

例子图举例子不是真的:

所以插入的结点时nullptr

bool insert(k key)
{
	//两种情况;
	//树里没有数据和有数据俩种情况
	if (_root == nullptr)
	{
		_root = new node(key);//开辟一个node对象然后调用初始化构造
		return true;
	}
	else 
	{
		//有数据,要找到正确位置
		node* parent = nullptr;
		node* cur = _root;
		while (cur)
		{
			if (cur->_key>key)//证明待插入元素应该往左边
			{
				parent = cur;//cur是探测作用,parent是保存上一个指针地址
				cur = cur->left;
				
			}
			else if (cur->_key < key)//证明待插入元素应该往右边
			{
				parent = cur;
				cur = cur->right;
			}
		}
		//当cur为nullptr就要找到待插入位置。
		//但是,还要确定插在parent的左还是右
		if (parent->_key>key)
		{
			parent->left = new node(key);
            return true;
		}
		else
		{
			parent->right = new node(key);
            return true;
		}
           return false; 
	}
删除erase(重点):

这个很重要分很多个步骤

下面分析要在找到删除结点的情况下

1、看图(如图上要求)如何删除2呢?

例子图举例子不是真的:

观察2没有左子树也没有右子树或者说2的左指向nullptr,右边也指向nullptr

所以父节点右指针可以连接2的左nullptr和右nullptr

2、如图,当待删的结点有一个结点时

第一、还要将分为待删结点1待删结点8子结点左结点还是右结点

第二、通过上图发现要删除结点还要拥有它的父类结点(双指针)

第三、待删结点剩下的结点要与父类连接(可以理解孙子结点与爷爷连接)

注意:待删结点的子结点有可能在左也可能在右,就是强调第一种情况,左右的情况

其实没有结点的情况也归这里管

以上就是当只有一个待删结点时注意的事项

例子图:

实现代码

//找到了,分情况讨论
			//只有一种的情况下:待删结点的子结点为右边
			//如果cur两边都没有结点符合
			if (cur->left == nullptr)
			{
				//确定parent的左连接到cur还是右连接到cur
				if (parent->left == cur)
				{
					parent->left = cur->right;
				}
				else
				{
					parent->right = cur->right;
				}
				delete cur;
				return true;
			}//待删结点的子结点为左边
			else if (cur->right == nullptr)
			{
				//确定parent的左连接到cur还是右连接到cur
				if (parent->left == cur)
				{
					parent->left = cur->left;
				}
				else
				{
					parent->right = cur->left;
				}
				delete cur;
				return true;
			}

还有一种情况最为复杂待删结点两个结点都有:

用替代法:就是在二叉搜索树中找一个可以替代该结点的数,将结点赋值,然后删除赋值结点的原位置,没有破坏二叉收搜索树的结构

比如5,左子树的最大结点可以满足(在左子树一直往右走)

右子树的最小结点可以满足(在右子树一直往左走)

例子图:

将4或6赋值给5后删除调黑色圈圈的位置(用那个删那个)

实现代码(这里有错的)有一种情况是删不了的

//只剩下两个结点的情况
node* rightsmall = cur->right;
node* rightsmallparent = nullptr;
while (rightsmall->left)//一直往左走就找到右子树的最小的
{//当这个条件结束rightsmall的左边已经没有值了所以就是最小的
   rightsmallparent = cur;
   rightsmall = rightsmall->left;
}
cur->_key = rightsmall->_key;//赋值
//替代完成
//删除替代的原位置
if (rightsmall->left == nullptr)
{
	rightsmallparent->left = rightsmall->right;//因为rightsmall一直往左走
					                           //所以是rightsmallparent的左孩子
}
else
{
	rightsmallparent->left = rightsmall->left;

}
	delete rightsmall;
	return true;

就是删4的时候

rightsmallparent初始化为nullptr导致没有进入循环因为rightsmall->left==nullptr,因为就会导致rightsmallparent为nullptr空去调用

改rightsmallparent初始化为cur

//只剩下两个结点的情况
				node* rightsmall = cur->right;
				node* rightsmallparent = cur;
				while (rightsmall->left)//一直往左走就找到右子树的最小的
				{//当这个条件结束rightsmall的左边已经没有值了所以就是最小的
					rightsmallparent = rightsmall;
					rightsmall = rightsmall->left;
				}
				cur->_key = rightsmall->_key;//赋值
				//替代完成
				//删除替代的原位置
				if (rightsmall->left == nullptr)
				{
					if (rightsmallparent->left ==  rightsmall)
					{
						rightsmallparent->left = rightsmall->right;
					}
					else
					{
						rightsmallparent->right = rightsmall->right;

					}
					//因为rightsmall一直往左走
					 //所以是rightsmallparent的左孩子
				}
				else
				{
					if (rightsmallparent->left == rightsmall)
					{
						rightsmallparent->left = rightsmall->left;
					}
					else
					{
						rightsmallparent->right = rightsmall->left;
					}

				}
				delete rightsmall;
				return true;
			}
			return false;

把4删调了

中序遍历

对象名 . Inorder(this)有隐藏的this指针不方便所以在this内部中调用函数Inorder()让函数调用函数就不会有this参与

void _InOrder(node* root)//递归调用
{
	if (root == nullptr)
	{
		return;
	}
	_InOrder(root->left);
	cout << root->_key << " ";
	_InOrder(root->right);
}
void InOrder()//对象.Inorder(this)有隐藏的this指针不方便所以在this函数中再次调用
{
	_InOrder(_root);
	cout << endl;
}

Find查找:

bool Find(k key)
{
	if (_root == nullptr)
	{
		return false;
	}
	else
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->right;
			}
			else if (cur->_key > key)
			{
				cur = cur->left;
			}
			else
			{
				return true;
			}
		}
	}
}

完整代码

template<class k>
struct BSTreenode
{
	BSTreenode<k>* left;
	BSTreenode<k>* right;
	k _key;

	BSTreenode(k key)
		:left(nullptr)
		, right(nullptr)
		, _key(key)
	{

	}
};
template<class k>
class BSTree
{
typedef BSTreenode<k> node;//重写结点名字,方便
public:
bool erase(const k&key)
{
	//先去找
	//双指针法
	node* cur =_root;
	node* parent = nullptr;
	while (cur)
	{
		if (cur->_key > key)
		{
			parent = cur;
			cur = cur->left;
		}
		else if(cur->_key<key)
		{
			parent = cur;
			cur = cur->right;
		}
		else
		{
			//找到了,分情况讨论
			//只有一种的情况下:待删结点的子结点为右边
			//如果cur两边都没有结点符合
			if (cur->left == nullptr)
			{
				//确定parent的左连接到cur还是右连接到cur
				if (parent->left == cur)
				{
					parent->left = cur->right;
				}
				else
				{
					parent->right = cur->right;
				}
				delete cur;
				return true;
			}//待删结点的子结点为左边
			else if (cur->right == nullptr)
			{
				//确定parent的左连接到cur还是右连接到cur
				if (parent->left == cur)
				{
					parent->left = cur->left;
				}
				else
				{
					parent->right = cur->left;
				}
				delete cur;
				return true;
			}
			else
			{
			   
				//只剩下两个结点的情况
				node* rightsmall = cur->right;
				node* rightsmallparent = cur;
				while (rightsmall->left)//一直往左走就找到右子树的最小的
				{//当这个条件结束rightsmall的左边已经没有值了所以就是最小的
					rightsmallparent = rightsmall;
					rightsmall = rightsmall->left;
				}
				cur->_key = rightsmall->_key;//赋值
				//替代完成
				//删除替代的原位置
				if (rightsmall->left == nullptr)
				{
					if (rightsmallparent->left ==  rightsmall)
					{
						rightsmallparent->left = rightsmall->right;
					}
					else
					{
						rightsmallparent->right = rightsmall->right;

					}
					//因为rightsmall一直往左走
					 //所以是rightsmallparent的左孩子
				}
				else
				{
					if (rightsmallparent->left == rightsmall)
					{
						rightsmallparent->left = rightsmall->left;
					}
					else
					{
						rightsmallparent->right = rightsmall->left;
					}

				}
				delete rightsmall;
				return true;
			}
			return false;
		}
	}
}

bool insert(k key)
{
	//两种情况;
	//树里没有数据和有数据俩种
	if (_root == nullptr)
	{
		_root = new node(key);//开辟一个node对象然后调用初始化构造
		return true;
	}
	else 
	{
		//有数据,要找到正确位置
		node* parent = nullptr;
		node* cur = _root;
		while (cur)
		{
			if (cur->_key>key)//证明待插入元素应该往左边
			{
				parent = cur;//cur是探测作用,parent是保存上一个指针地址
				cur = cur->left;
				
			}
			else if (cur->_key < key)//证明待插入元素应该往右边
			{
				parent = cur;
				cur = cur->right;
			}
		}
		//当cur为nullptr就要找到待插入位置。
		//但是,还要确定插在parent的左还是右
		if (parent->_key>key)
		{
			parent->left = new node(key);
		}
		else
		{
			parent->right = new node(key);
		}
	}
}
void _InOrder(node* root)
{
	if (root == nullptr)
	{
		return;
	}
	_InOrder(root->left);
	cout << root->_key << " ";
	_InOrder(root->right);
}
void InOrder()
{
	_InOrder(_root);
	cout << endl;
}
bool Find(k key)
{
	if (_root == nullptr)
	{
		return false;
	}
	else
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->_key < key)
			{
				cur = cur->right;
			}
			else if (cur->_key > key)
			{
				cur = cur->left;
			}
			else
			{
				return true;
			}
		}
	}
}
private:
	node* _root=nullptr;//定义头指针
};
int main()
{
	BSTree<int> T;
	int arr[] = { 2,4,5,6,8,9,3,7,1,0 };
	for (auto e : arr)
	{
		T.insert(e);
	}
	T.InOrder();
	T.erase(7);
	T.InOrder();
	T.erase(5);
	T.InOrder();
	T.erase(4);
	T.InOrder();

}

  • 34
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 二搜索Binary Search Tree):是一棵空或者具有下列性质的二叉树:若它的左子不空,则左子上所有节点的值均小于它的根节点的值;若它的右子不空,则右子上所有节点的值均大于它的根节点的值;它的左右子也分别为二搜索。 中序遍历序列:对于任意一棵二叉树,中序遍历的结果都是一个序列,这个序列称为中序遍历序列。 因此,判断一棵二叉树是否为二搜索,可以先行中序遍历,再判断遍历结果是否为升序序列。 以下是 Python 代码实现: ```python class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def inorderTraversal(root: TreeNode) -> List[int]: res = [] if not root: return res res.extend(inorderTraversal(root.left)) res.append(root.val) res.extend(inorderTraversal(root.right)) return res def isBST(root: TreeNode) -> bool: res = inorderTraversal(root) for i in range(1, len(res)): if res[i] <= res[i-1]: return False return True ``` 其中,`TreeNode` 是二叉树的节点类,`inorderTraversal` 函数是实现二叉树中序遍历的递归函数,`isBST` 函数是判断二叉树是否为二搜索的函数。 ### 回答2: 要实现这个函数,首先我们可以使用递归的方式对二叉树行中序遍历,即先遍历左子,再访问根节点,最后遍历右子。遍历过程中将遍历到的节点值保存到一个数组中。 接下来,我们需要判断该数组是否是按升序排列的,即判断是否是一棵二搜索。我们可以遍历数组,依次比较相邻的节点值,如果前一个节点的值大于等于后一个节点的值,则认为不是二搜索。反之,如果整个数组都符合这个条件,则认为是一个二搜索。 以下是一个简单的实现代码: ``` class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = right def inorderTraversal(root): if not root: return [] result = [] inorder(root, result) return result def inorder(root, result): if not root: return inorder(root.left, result) result.append(root.val) inorder(root.right, result) def isBST(root): inorder_result = inorderTraversal(root) for i in range(1, len(inorder_result)): if inorder_result[i] <= inorder_result[i-1]: return False return True ``` 这个函数的时间复杂度是O(n),其中n是二叉树中节点的数量,因为我们需要遍历每个节点并将节点的值保存到数组中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值