程序员面试金典: 9.4树与图 4.7找出二叉树种某两个结点的第一个共同祖先---O(N^2)解法

#include <iostream>
#include <stdio.h>
#include <vector>
#include <string>

using namespace std;

/*
问题:设计并实现一个算法,找出二叉树中某两个结点的第一个共同祖先。不得将额外的结点储存在另外的数据结构中。
     注意:这不一定是二叉查找树。
分析:一种方式是根据结点,向上遍历,得到从当前结点到根节点的一个链表,设长链表的长度为L1,设短链表的长度为L2,
     将两个链表中较长的链表先走L1-L2步,然后寻找共同结点。
	 问题中说明:不得将结点额外存储。而采用链表的方式是需要额外存储结构的。如果不能用额外存储结构只能用这样的方式。
	 O(n^2)方法,设两个结点分别为n1,n2,
	 遍历n1,设当前结点为c,如果n1!=n2,令n2=n2->parent,

输入:
7(元素个数) 2(待寻找共同祖先的第一个结点的值) 5(待寻找共同祖先的第二个结点的值)
6 3 9 1 4 2 5(所有各个结点的值)
d(表示当前第1个结点有两个孩子,后续跟两个孩子结点下标) 2 3
d 4 5
z(表示当前第i个结点没有孩子结点)
r(表示当前第i个结点只有一个右孩子结点,后面跟该右孩子结点下标) 6
r 7
z
z

7 2 9
6 3 9 1 4 2 5
d 2 3
d 4 5
z
r 6
r 7
z
z
输出:
3(共同祖先的值,如果没有,返回NULL)
6
*/

typedef struct TreeNode
{
	TreeNode* _pLeft;
	TreeNode* _pRight;
	TreeNode* _pParent;
	int _value;
}TreeNode;
const int MAXSIZE = 10000;
int g_index;
TreeNode g_treeNodeArray[MAXSIZE];

TreeNode* createTreeNode()
{
	++g_index;
	g_treeNodeArray[g_index]._pLeft = g_treeNodeArray[g_index]._pRight = g_treeNodeArray[g_index]._pParent = NULL;
	return &g_treeNodeArray[g_index];
}

//建树
void buildTree(vector<int>& vecData)
{
	int size = vecData.size();
	g_index = 0;
	if(vecData.empty())
	{
		return;
	}
	string childFlag;
	int leftChild, rightChild;
	for(int i = 1 ; i <= size ; i++ )
	{
		cin >> childFlag;
		g_treeNodeArray[i]._value = vecData.at(i-1);
		if("d" == childFlag)
		{
			cin >> leftChild >> rightChild;
			g_treeNodeArray[i]._pLeft = &g_treeNodeArray[leftChild];
			g_treeNodeArray[leftChild]._pParent = &g_treeNodeArray[i];
			g_treeNodeArray[i]._pRight = &g_treeNodeArray[rightChild];
			g_treeNodeArray[rightChild]._pParent = &g_treeNodeArray[i];
		}
		else if("r" == childFlag)
		{
			cin >> rightChild;
			g_treeNodeArray[i]._pRight = &g_treeNodeArray[rightChild];
			g_treeNodeArray[rightChild]._pParent = &g_treeNodeArray[i];
		}
		else if("l" == childFlag)
		{
			cin >> leftChild;
			g_treeNodeArray[i]._pLeft = &g_treeNodeArray[leftChild];
			g_treeNodeArray[leftChild]._pParent = &g_treeNodeArray[i];
		}
	}
}

//根据结点值找到指定结点,如果同时出现多个结点的值与给定值相同,就返回最先找到的结点
TreeNode* findNode(TreeNode* head , int value)
{
	if(NULL == head)
	{
		return NULL;
	}
	if(value == head->_value)
	{
		return head;
	}
	TreeNode* leftChild = NULL;
	TreeNode* rightChild = NULL;
	if(head->_pLeft)
	{
		leftChild = findNode(head->_pLeft , value);
	}
	//在左子树找到,就直接返回结果;否则,递归查找右子树
	if(leftChild != NULL)
	{
		return leftChild;
	}
	else
	{
		if(head->_pRight)
		{
			return findNode(head->_pRight , value);
		}
		else
		{
			return NULL;
		}
	}
}

//寻找根节点
TreeNode* findRoot(TreeNode* node)
{
	if(NULL == node)
	{
		return NULL;
	}
	if(NULL == node->_pParent)
	{
		return node;
	}
	else
	{
		return findRoot(node->_pParent);
	}
}

//寻找两个结点的最近公共祖先,不使用额外的数据结构存放结点
TreeNode* findCommonAncestor(TreeNode* node1 , TreeNode* node2)
{
	TreeNode* tempNode2 = node2;
	TreeNode* tempNode1 = node1;
	while(tempNode2)
	{
		//遍历第一个结点链表,看是否找到与当前结点相同的结点
		while(tempNode1)
		{
			if(tempNode1->_value == tempNode2->_value)
			{
				return tempNode1;
			}
			else
			{
				tempNode1 = tempNode1->_pParent;
			}
		}
		//到这里就是把结点1所在链表的所有结点都遍历完,且没有找到共同祖先,此时就应该使得结点2等于结点2的父节点
		tempNode2 = tempNode2->_pParent;
		tempNode1 = node1;
	}

	//如果最终没有找到共同祖先,返回空
	return NULL;
}

void process()
{
	int nodeNum ; 
	int firstValue;
	int secondValue;
	int value;
	vector<int> vecData;
	while(cin >> nodeNum >> firstValue >> secondValue)
	{
		vecData.clear();
		for(int i = 0 ; i < nodeNum ; i++)
		{
			cin >> value;
			vecData.push_back( value );
		}
		//输入完成接下来需要建树
		buildTree(vecData);
		//建树完成后,就是寻找共同父节点
		TreeNode* root = findRoot(&g_treeNodeArray[1]);
		TreeNode* node1 = findNode(root , firstValue);
		TreeNode* node2 = findNode(root , secondValue);
		TreeNode* ancestorNode = findCommonAncestor(node1 , node2);
		if(ancestorNode)
		{
			cout << ancestorNode->_value << endl;
		}
		else
		{
			cout << "NULL" << endl;
		}
	}
}

int main(int argc , char* argv[])
{
	process();
	getchar();
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值