看到这里的朋友,首先恭喜你们,终于把《剑指Offer》看完了。你们不容易,我也不容易,学习编程是要下一番功夫的,没有人能够随随便便成功。
献给在奋斗路上的你们和我。
下一个专题预告:《编程之美》
/*
树中两个节点的最低公共祖先:
题型1:求二叉搜索树中两个结点的最低公共祖先
思路:如果当前值比两个结点的值都大,那么只需要向当前节点的左子树继续向下遍历即可,直到找到介于两个结点
值中间的结点。如果当前值比两个结点的值都小,那么向当前结点的右子树继续向下遍历即可。
题型2:求普通树(含有指向父节点的指针)中两个结点的最低公共祖先
这样的话,就可以转化为起始结点为待求节点,终点为根节点的两个链表的第一个公共结点为题。
题型3:无指向父结点指针的普通树中求两个结点的最低公共祖先。
用两个链表分别保存从根节点到输入的两个结点的路径,把问题转化为求两个链表的最后公共结点。
比如我们得到从根节点到达F结点的路径是:A->B->D,而从根节点到达H结点的路径是:A->B->E,因此公共结点是B
由于要不断的弹入弹出,因此设定一个数组和一个栈来解决
输入:
输入可能包含多个测试样例。
对于每个测试案例,输入的第一行为一个数n(0<n<1000),代表测试样例的个数。
其中每个测试样例包括两行,第一行为一个二叉树的先序遍历序列,其中左右子树若为空则用0代替,其中二叉树的结点个数node_num<10000。
第二行为树中的两个结点的值m1与m2(0<m1,m2<10000)。
输出:
对应每个测试案例,
输出给定的树中两个结点的最低公共祖先结点的值,若两个给定结点无最低公共祖先,则输出“My God”。
样例输入:
2
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 8
1 2 4 6 0 0 7 0 0 5 8 0 0 9 0 0 3 0 0
6 12
样例输出:
2
My God
题目转化为如何利用先序序列来建立二叉树,注意凡是碰到一个数字的最后
用一个栈来保存父节点
栈为空,且第一个节点不为叶结点,那么第一个结点为父节点
一旦碰到0,表明栈中的当前节点是该0的父节点,即该父节点的左右子女均为空,此时弹出该父节点,并设置
该父节点的左右子女为空。判断如果有一个结点的左右孩子均设置(不为空)了,那么弹出该节点
总结:弹出栈中节点的条件
1对于叶节点,即后面碰到两个0,直接弹出
2对于非叶节点,当该节点的左右孩子均不空,或者当时设定一个计数器变量,进来一个增加一个计数,当有两个
计数,表明左右孩子均建立了,此时可以弹出该结点
总过程:
1寻找路径
2获取两条路径上最后一个公共的节点
*/
/*
关键:
1 题型1:求二叉搜索树中两个结点的最低公共祖先
思路:如果当前值比两个结点的值都大,那么只需要向当前节点的左子树继续向下遍历即可,直到找到介于两个结点
值中间的结点。如果当前值比两个结点的值都小,那么向当前结点的右子树继续向下遍历即可。
题型2:求普通树(含有指向父节点的指针)中两个结点的最低公共祖先
这样的话,就可以转化为起始结点为待求节点,终点为根节点的两个链表的第一个公共结点为题。
题型3:无指向父结点指针的普通树中求两个结点的最低公共祖先。
用两个链表分别保存从根节点到输入的两个结点的路径,把问题转化为求两个链表的最后公共结点。
比如我们得到从根节点到达F结点的路径是:A->B->D,而从根节点到达H结点的路径是:A->B->E,因此公共结点是B
2 pHead = createNode();
//pHead = &nodeArr[iPos+1];
pHead->_iVal = pArr[iPos];
iPos++;
pHead->_left = buildTree(pArr,iLen,iPos);//用先序序列递归来建立二叉树
pHead->_right = buildTree(pArr,iLen,iPos);
3 if(pArr[iPos] == 0)//叶子节点标记
{
iPos++;//注意,这里碰到0之后一定要累加,否则陷入死循环中
return NULL;
}
4 if(!pRoot || !pFind)//如果为空,直接返回false,并且不加入这个节点
{
return false;
}
if(pFind == pRoot)//如果根节点就是要找的节点,就直接返回
{
return true;
}
listPath.push_back(pRoot);
5 if(!isFind)//注意,如果始终没有找到,就退回到上一层
{
listPath.pop_back();
}
6 while(EOF != scanf("%d",&iDataArr[iCnt++]))//以换行作为分隔,如何判定输入结束
{
ch = getchar();
if(ch == '\n')//用gtechar() != '\n' 来判断一个数组是否结束输入
*/
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <list>
#include <string>
using namespace std;
const int MAXSIZE = 1e4 + 1;
const int INF = 0x7fffffff;
typedef struct Node
{
int _iVal;
Node* _left;
Node* _right;
}Node;
Node nodeArr[MAXSIZE];
int _iIndex;
Node* createNode()
{
++_iIndex;
nodeArr[_iIndex]._left = nodeArr[_iIndex]._right = NULL;
nodeArr[_iIndex]._iVal = INF;
return &nodeArr[_iIndex];
}
Node* buildTree(int* pArr,int iLen,int& iPos)
{
if(!pArr || iLen < 0 )//鲁棒性
{
return NULL;
}
if(iPos == iLen)//递归出口
{
return NULL;
}
Node* pHead;
if(pArr[iPos] == 0)//叶子节点标记
{
iPos++;//注意,这里碰到0之后一定要累加,否则陷入死循环中
return NULL;
}
else
{
pHead = createNode();
//pHead = &nodeArr[iPos+1];
pHead->_iVal = pArr[iPos];
iPos++;
pHead->_left = buildTree(pArr,iLen,iPos);//用先序序列递归来建立二叉树
pHead->_right = buildTree(pArr,iLen,iPos);
}
return pHead;
}
void frontVisit(Node* pHead)
{
if(!pHead)//递归出口
{
return;
}
printf("%d ",pHead->_iVal);
frontVisit(pHead->_left);
frontVisit(pHead->_right);
}
bool getPath(Node* pRoot,Node* pFind,list<Node*>& listPath)
{
if(!pRoot || !pFind)//如果为空,直接返回false,并且不加入这个节点
{
return false;
}
if(pFind == pRoot)//如果根节点就是要找的节点,就直接返回
{
return true;
}
listPath.push_back(pRoot);
bool isFind = false;
if(!isFind)
{
isFind = getPath(pRoot->_left,pFind,listPath);
}
if(!isFind)
{
isFind = getPath(pRoot->_right,pFind,listPath);
}
if(!isFind)//注意,如果始终没有找到,就退回到上一层
{
listPath.pop_back();
}
return isFind;
}
Node* getLastCommonNode(list<Node*>& listPath1,list<Node*>& listPath2)
{
list<Node*>::iterator it1 = listPath1.begin();
list<Node*>::iterator it2 = listPath2.begin();
Node* pLast = NULL;
while(it1 != listPath1.end() && it2 != listPath2.end())
{
if(*it1 == *it2)
{
pLast = *it1;
}
it1++;
it2++;
}
return pLast;
}
//注意,还需要建立节点值与节点下标的映射,不然的话,在创建的时候,直接返回第节点值对应的节点地址
Node* findNode(Node* pHead,int iVal)
{
if(!pHead || iVal <= 0 || iVal >= MAXSIZE)
{
return NULL;
}
else
{
Node* pNode;
if(pHead->_iVal != iVal)
{
pNode = findNode(pHead->_left,iVal);
if(pNode)//如果已经找到,直接返回
{
return pNode;
}
pNode = findNode(pHead->_right,iVal);
if(pNode)
{
return pNode;
}
}
else
{
return pHead;
}
}
}
void process()
{
int n;
while(EOF != scanf("%d",&n))
{
while(n--)
{
memset(nodeArr,NULL,sizeof(nodeArr));
_iIndex = 0;
int iDataArr[MAXSIZE];
int iCnt = 0;
char ch;
while(EOF != scanf("%d",&iDataArr[iCnt++]))//以换行作为分隔,如何判定输入结束
{
ch = getchar();
if(ch == '\n')//用gtechar() != '\n' 来判断一个数组是否结束输入
{
break;
}
}
int iPos = 0;
Node* pHead = buildTree(iDataArr,iCnt,iPos);//根据先序序列,调用递归函数来建立二叉树
//frontVisit(pHead);
//printf("\n");
int m1,m2;
scanf("%d %d",&m1,&m2);
Node* pNode1 = findNode(pHead,m1);
if(!pNode1)
{
printf("My God\n");
continue;
}
Node* pNode2 = findNode(pHead,m2);
if(!pNode2)
{
printf("My God\n");
continue;
}
list<Node*> listPath1;
list<Node*> listPath2;
if(getPath(pHead,pNode1,listPath1) && getPath(pHead,pNode2,listPath2))//如果找到路径1,2,4与1,2,5(不包含本身节点)
{
Node* pAncestor = getLastCommonNode(listPath1,listPath2);
printf("%d\n",pAncestor->_iVal);
}
else
{
printf("My God\n");
continue;
}
}
}
}
int main(int argc,char* argv[])
{
process();
getchar();
return 0;
}
//需要一个判断是左还是右的标记
//void buildTree(Node** pHead,int* pArr,int iLen)
//{
// bool isLeft = true;
// bool isVisitLeft = false;
// bool isVisitRight = false;
// stack<Node*> stackNode;
// int i = 0;
// while(i < iLen)
// {
// if(pArr[i]== 0)//如果碰到了0,就再判断下一个是否是0,如果两个都是0
// {
// if(isLeft)//左边的0
// {
// isLeft = false;
// }
// else//如果碰到的是右边的0,弹出栈中的当前节点
// {
// if(stackNode.size() > 0)
// {
// stackNode.pop();
// }
// }
// i++;
// }
// else//如果碰到的不是0
// {
// if(!i)
// {
// Node* pCurNode = createNode();
// pCurNode->_iVal = pArr[i];
// Node* pParentNode = stackNode.top();
// if(isLeft)//如果碰到的是左节点,那么设定栈中的父节点指向该节点
// {
//
// pParentNode->_left = pCurNode;
// isLeft = false;
// }
// else//如果碰到的是右结点
// {
// pParentNode->_right = pCurNode;
// isLeft = true;
// }
// }
// else//如果是根节点
// {
// (*pHead)->_iVal = pArr[i];
// stackNode.push(*pHead);
// }
// }
// }
// }
//}