[数据结构]二叉搜索树的中序前驱和后继的定义及编程方法

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

本文探讨二叉搜索树的中序前驱和后继的定义及编程。想象以下应用场景,二叉搜索树的中序遍历得到以下序列

… < x n x_n xn < v a l val val < x n + 1 x_{n+1} xn+1 < …

,对于数据为 v a l val val的节点通过’ v a l + + val++ val++'跳到数据为 x n + 1 x_{n+1} xn+1的节点,或通过’ v a l − − val-- val跳到数据为 x n x_n xn的节点。在我的理解中,前驱和后继便是为了解决这一问题而被定义。由于前驱和后继的操作相似,所以对于后继只给出定义,具体编程方法可以参考前驱。

一、前驱和后继的定义及位置

(1)前驱的定义

数据值小于节点n,且与节点n数值最接近的节点(记为节点m)就是节点n的前驱,则称“节点n的前驱是节点m”或“节点m是节点n的前驱”。比如搜索树的中序遍历得到 { 1 , 3 , 5 , 7 , 9 } \{1,3,5,7,9\} {1,3,5,7,9},称3是5的前驱,5的前驱是3。

(2)前驱的位置

根据前驱的定义,我们需要找一个比目前节点小的数,因此可能出现的位置在左子树中;如果没有左子树,那么可能该节点没有前驱(该节点是所有节点中最小的),也有可能该节点有前驱。所以前驱的二叉树中可能的分布位置有以下(2.1)和(2.2)两种:

(2.1) 节点有左子树

下图就是节点n有左子树的情况,首先我们证明所指节点m就是节点n的前驱

证明:
目标:找到一个比节点n小,且与n数值距离最近的节点
事实1:根据搜索二叉树的性质,节点n的左子树中的节点都小于n。
结论1:根据事实1,可知节点n的前驱出现在节点n的左子树。
事实2:根据搜索二叉树的性质,节点m是节点n左子树中数值最大的节点,其位置在节点n左子树最右下角的位置。
结论2:结合结论1和事实2,节点m大于节点n左子树除去节点m的所有节点却小于节点n,形成了 { . . . < 除 外 节 点 m 时 节 点 n 的 左 子 树 < 节 点 m < 节 点 n < . . . } \{...<除外节点m时节点n的左子树<节点m<节点n<...\} {...<mn<m<n<...}的形式。
总结:当节点n有左子树时,其前驱就是节点n左子树中数值最大的节点

在这里插入图片描述

(2.2) 节点无左子树

在节点无左子树时有两种情况:n有前驱和n无前驱。
在这里插入图片描述

情况1:当n是二叉搜索树中的最小值时,n没有前驱。
情况2:当n不是二叉搜索树中最小值时,即存在前驱 v a l val val 使得 v a l < n < . . . val<n<... val<n<... . . . < v a l < n < . . . ...<val<n<... ...<val<n<...,并且 v a l val val 必然处于n的上方,因为n的右子树必然都大于n而不存在n的前驱(如上图)。

下面我们详细讨论情况2,针对 v a l < n < . . . val<n<... val<n<... . . . < v a l < n < . . . ...<val<n<... ...<val<n<... 我们都发现n是val的后继。后继的操作方法是:先走到节点的右孩子,然后一直往左孩子方向走,反过来就是往右上角方向一直走然后再往左上角走一步。
在这里插入图片描述
总结:如果节点n没有左孩子,则前驱就是节点n先往右上角一直走到尽头,走到尽头后往左上角走一步。

(2.3)前驱的位置总结 ***

在这里插入图片描述

如果n有左孩子,就走蓝色路线,走蓝色路线必然存在前驱;如果n没有左孩子,则走红色路线,走红色路线时可能会得到一个空指针,说明n没有前驱。

(2)后继的定义

数据值大于节点n,且数值最接近节点n的第一个节点(记为节点m)就是节点n的后继。

二、前驱的编程方法

(1)寻找前驱的编程方法

//寻找节点n的前驱,返回一个指针
Node* prev(Node* n){
	if(!n){
	//传了个空指针
	return nullptr;
	}
	Node* ptr=n;
	if(n->_left){
	//有左孩子,走蓝色路线
		ptr=ptr->_left;
		while(ptr->_right){
			//有右孩子就一直往右孩子方向走
			ptr=ptr->_right;
		}
		return ptr;
	}
	else{
	//没有左孩子,走红色路线
		while(ptr->_parent&&ptr->_parent->_left==ptr){
		//保证ptr走的路线是右上方的方法是:ptr的父节点的左孩子是ptr
			ptr=ptr->_parent;
		}
		ptr=ptr->_parent;
		return ptr
	}
}

总结

前驱和后继的应用很多,比如重载‘++’和‘–’时节点的跳跃,删除节点时需要找到前驱或后继作为顶替。本笔记主要记录了二叉搜索树的前驱的定义、位置和编程方法,对于后继其实只是一种‘反向操作’,写出了前驱后其实可以通过仿写简单得到后继的程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值