欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

树中两个节点的最近公共祖先 多种情况分析

程序员文章站 2022-07-14 14:51:25
...
1.求树中两个节点的最近公共祖先
这个问题按 对题意解析思路 的不同, 可以有许多的变种问题 和 解决思路
首先我们来看比较简单的一种: 如果这颗树是 二叉搜索树( 假设这个二叉搜索树中没有两个值相同的结点, 并且我们 考虑的这两个结点不是空结点 )
根据二叉搜索树的性质( 左子树的值都比根节点小, 右子树的值都比根节点大 )
我们的思路为: 从根节点开始, 如果两个节点的值 都比当前节点(此时为根节点)小, 则 它们的 最低公共祖先在 当前节点的左子树中, 否则在 当前节点的右子树中
接下来 我们只需要, 将 需要判断的节点 更新为 当前节点的 左孩子结点 或 右孩子结点即可。
直到 遇到一个节点的值 介于我们的两个结点之间, 即它为这两个节点的 最近公共祖先。

当然别忘了 处理 特殊情况:  这两个结点, 其中一个节点 为 另一个结点的 祖先



#include <iostream>
#include <windows.h>
using namespace std;
#include "BinarySearchTree.h"					//我包含了这个头文件 来测试我们所写的函数是否能达到预期的 效果.
												//这个头文件中 只保留了 测试这个函数 需要用到的接口, 如想查看 二叉搜索树 更完整定义, 可以看我的另一篇博客 ---> 二叉搜索树
												//因为我们要用到 二叉树结点 来定义 变量, 所以在头文件中, 修改将 结点定义 放在了 类外面。 在 我的 原博客中(即二叉搜索树), 二叉搜索树的结点定义 是属于 类的私有成员.


template <typename T>
BSTNode<T>* GetLastCommonNode( BSTNode<T>* root, BSTNode<T>* node1, BSTNode<T>* node2 )//注意, BSTNode*是错的, 返回类型是 BSTNode*<T>
{
	if ( NULL != root && NULL != node1 && NULL != node2 )
	{
		T data = root->_data;
		T data1 = node1->_data;
		T data2 = node2->_data;

		if ( data < data1 && data < data2 )
			GetLastCommonNode( root->_right, node1, node2 );
		else if ( data > data1 && data > data2 )
			GetLastCommonNode( root->_left, node1, node2 );
		else
		{
			//第一种情况: node1 与 node2 有一个结点与当前节点相等, 即 node1 与 node2 中有一个结点为另一个结点祖先( 注意, 并不是我们的树中出现了重复的数字, 这点要注意 )
			if ( data1 == data )
				return node1;
			else if ( data2 == data )
				return node2;
			else//此时 为第二种情况, 即 当前结点的值 在 两个结点之间.
				return root;
		}
	}
	else//注意, 这里必须用 else 来 匹配上面的 if.  不要抱 :  如果  执行 if 则上面 就返回了 ,如果没返回, 则这里返回 NULL 就好了.  说明上面逻辑 没执行.  不能抱这种想法。 因为上面是含有递归的, 所以 有可能是 子Fun( ) 返回了, 所以 ,如果不用 else 匹配, 有可能 返回值 被 NULL 代替 返回 ,程序挂掉. 
	{
		//如果程序运行到这, 即所给数据有问题, 如 root node1 node2 可能有一个结点为空.
		return NULL;
	}
}


void TestFun( )
{
	int a[] = { 7, 4, 1, 8, 5, 9 ,2, 3, 10 };
	BinarySearchTree<int> t;

	for ( size_t i = 0; i < sizeof(a)/sizeof(a[0]); ++i )
		t.Insert( a[i] );

	t.PrintTree( );
	cout << endl;

	BSTNode<int>* root = t.GetRoot( );

	//因为我们的 BinarySearchTree.h 头文件中 未实现 Find( ) 函数 ,我们就在这, 造几个我们想测试的 找最近祖先数据 的结点 
	//其实 node1 和 node2 只是用了 它们中的数据 进行测试.
	//还要注意的是, 本来我们的 结点指针来源于 Find( ) 函数的 返回值, 因为我们没实现 Find( ) 函数 ,所以自己造结点, 但是要注意的 一点是  我们的数据 必须是 二叉搜索树中 已有的数据 , 因为我们是模拟 Find( )函数的返回值 ,所以不能造出, 二叉搜索树中没有数据的结点指针
	BSTNode<int>* tTest1 = new BSTNode<int>( 1 );
	BSTNode<int>* tTest2 = new BSTNode<int>( 2 );
	BSTNode<int>* tTest3 = new BSTNode<int>( 3 );
	BSTNode<int>* tTest5 = new BSTNode<int>( 5 );
	BSTNode<int>* tTest9 = new BSTNode<int>( 9 );
	BSTNode<int>* tTest10 = new BSTNode<int>( 10 );

	//非法情况
	if ( NULL == GetLastCommonNode<int>( NULL, NULL, NULL ) )
		cout << "No" << endl;

	//正常情况
	cout << "4? : " << GetLastCommonNode<int>( root, tTest5, tTest3 )->_data << endl;
	cout << "9? : " << GetLastCommonNode<int>( root, tTest9, tTest10 )->_data << endl;
	cout << "7? : " << GetLastCommonNode<int>( root, tTest2, tTest10 )->_data << endl;
	cout << "4? : " << GetLastCommonNode<int>( root, tTest1, tTest5 )->_data << endl;
}


int main( )
{
	TestFun( );

	system( "pause" );
	return 0;
}

BinarySearchTree.h:

#include <iostream>
using namespace std;


template <typename T>
struct BSTNode
{
	T _data;
	BSTNode* _left;
	BSTNode* _right;

	BSTNode( const T& x, BSTNode* left = NULL, BSTNode* right = NULL )
		: _data( x )
		, _left( left )
		, _right( right )
		{}
};


template<typename T>
class BinarySearchTree
{
public:
	BinarySearchTree( )
		: _root( NULL )
	{}

	~BinarySearchTree( )
	{
		MakeEmpty( );
	}

	void PrintTree( ) const						//采用中序遍历打印树
	{
		_PrintTree( _root );
	}

	void MakeEmpty( )
	{
		_MakeEmpty( _root );
	}

	void Insert( const T& x )
	{
		_Insert( x, _root );
	}

	BSTNode<T>* GetRoot( )							//注意 , 这里也需要是 BSTNode<T>*.    在 public上面   typedef 时, 是 --->   typedef BSTNode<T> Node   ???
	{
		return _root;
	}

private:
	BSTNode<T>* _root;

	void _Insert( const T& x, BSTNode<T>*& root )//想, 它是怎么链起来的, 因为下一个root是 它父亲的左或右孩子的引用, 如果传引用, new后,它的父亲的左右孩子就被链起来了。  否则的话, 临时变量,   孩子被new值赋予后不会影响到调用它的函数的值(父亲的左or右孩子的值)
	{
		if ( NULL == root )
		{
			root = new BSTNode<T>( x, NULL, NULL );
		}
		else if ( x < root->_data )
		{
			_Insert( x, root->_left );
		}
		else if ( x > root->_data )
		{
			_Insert( x, root->_right );
		}
		else
		{
			;									//相等,目前什么都不做,当然,也可以更新值什么的.
		}
	}

	void _MakeEmpty( BSTNode<T>*& root ) const
	{
		if ( NULL == root )
		{
			;
		}
		else
		{
			_MakeEmpty( root->_left );		//后序删除
			_MakeEmpty( root->_right );

			delete root;
		}

		root = NULL;							//别忘了置NULL
	}

	void _PrintTree( BSTNode<T>* root ) const			//中序
	{
		if ( NULL == root )
		{
			return;
		}

		_PrintTree( root->_left );
		cout << root->_data << " ";
		_PrintTree( root->_right );
	}
};
我们插入的这棵二叉搜索树简略图如下:  右边是我们 要测试的两个结点 及 它们最近的公共祖先
树中两个节点的最近公共祖先 多种情况分析