二元查找树转换成双向链表

本文详细介绍了如何将二元查找树转换成排序的双向链表,包括问题描述、二元查找树的定义、算法思想和实现。算法分为两步:首先通过递归将二元查找树转换为单链表,然后在一次遍历中构建双向链表。最后,文章提出了优化方案,通过一次遍历完成双向链表的构建,提高了效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

题目:
输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表。要求不能创建任何新的结点,只调整指针的指向。


转换成双向链表


二元查找树节点的数据结构如下:

struct BSTreeNode
{
	BSTreeNode():m_pLeft(0),m_pright(0){}
	int m_nValue;
	BSTreeNode *m_pLeft;
	BSTreeNode *m_pright;
};

二元查找树的定义

二元查找树: 它首先要是一棵二元树,在这基础上它或者是一棵空树;或者是具有下列性质的二元树: 

1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;

2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值; 

3. 左、右子树也分别为二元查找树

算法思想

根据二元查找树的定义,不难推出二元查找树的一种极端情况是等价与一个有序的单链表。



这与题目的要求的双向链表只有一步之遥。对于这个单向链表我们可以遍历一遍就可以将其改造成双向链表了。所以一个简单的实现就是先将其变成单向链表,然后再改造成双向链表。

如何将一棵普通的二元查找树改造成这种极端的二元查找树呢?很容易可以想到利用递归的方法来实现。算法如把大象装进冰箱那么简单一样:

1. 如果右子树不为空,将右子树变为单链表。

2. 如果左子树不为空,将左子树变为单链表。

3. 将左子树,当前结点及右子树变成一个单链表。

不难从上述算法描述中看出,关键将第三步实现即可。先来看看完成1,2步奏之后树的形状。


从上图中可以直观的看出,步奏3只需要将结点7和结点8串起来即可。代码如下:

	BSTreeNode **it = &top->m_pLeft;
	for ( ; *it; it = &(*it)->m_pRight )
	{
		;
	}
	*it = top;

算法实现

通过上述分析,算法实现代码如下:

struct BSTreeNode
{
	BSTreeNode():m_pLeft(0),m_pright(0){}
	int m_nValue;
	BSTreeNode *m_pLeft;
	BSTreeNode *m_pright;
};
void ConvertToSingleList( BSTreeNode **p )
{
	if ( !*p )
	{
		return;
	}
	ConvertToSingleList( &(*p)->m_pright );
	ConvertToSingleList( &(*p)->m_pLeft );

	BSTreeNode **it = &(*p)->m_pLeft;
	if ( *it )
	{
		for( ; *it; it = &(*it)->m_pright )
		{
			;
		}
		*it = *p;
		*p = (*p)->m_pLeft;
	}
}

void ConvertToDoubleList( BSTreeNode **p )
{
	ConvertToSingleList(p);
	BSTreeNode *it = *p;
	for ( ; it; it = it->m_pright )
	{
		if ( it->m_pright )
		{
			it->m_pright->m_pLeft = it;
		}
	}
}

算法改进

上述算法需要遍历两次,第一次构建单链表,第二次构建双向链表,有没有办法将只一次遍历就构建好双向链表呢?答案是肯定的。假设对于当前的top结点的左右子结点都已经是双向链表了,如下图所示:


据上图所示,不难看出,若要将此树转换成双向链表,只需下面两步即可:

1. 将右子结点的left指向top

2. 将左子结点的最后一个结点的rigt指向toptopleft指向它。

同时我们发现在前面算法的实现当中,需要通过遍历左子树找到最后一个结点。这种方法效率不高,我们可以稍作改进,在将左右子树转换为双向链表的同时,返回各自的最后一个结点,以避免遍历左子树。

改进的算法实现如下:

struct BSTreeNode
{
	BSTreeNode():m_pLeft(0),m_pright(0){}
	int m_nValue;
	BSTreeNode *m_pLeft;
	BSTreeNode *m_pright;
};
void ConvertToDoubleList( BSTreeNode **p, BSTreeNode **tail )
{
	if( !(*p) )
	{
		return;
	}
	BSTreeNode *rtail = NULL;
	if( (*p)->m_pright )
	{
		ConvertToDoubleList( &(*p)->m_pright, &rtail );
		(*p)->m_pright->m_pLeft = *p;
	}
	else
	{
		//再没有右子树了,因此它就是末尾结点
		*tail = *p;
	}

	BSTreeNode *ltail = NULL;
	if ( (*p)->m_pLeft )
	{
		ConvertToDoubleList( &(*p)->m_pLeft, <ail );
		ltail->m_pright = *p;
		*p = (*p)->m_pLeft;
		ltail->m_pright->m_pLeft = ltail;
		//现在是一个完整的双向链表了,末尾结点自然是之前右子树的末尾结点。
		*tail = rtail;
	}
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值