【剑指offer】近期公共祖先

转载请注明出处:http://blog.csdn.net/ns_code/article/details/28113959

剑指offer上的最后一题了,一个递归函数调了一下午,才得到正确的结果。

题目描写叙述:

给定一棵树,同一时候给出树中的两个结点,求它们的最低公共祖先。

输入:

输入可能包括多个測试例子。
对于每一个測试案例,输入的第一行为一个数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

这类的题目,方法蛮多的,思路也不难理解,基本都是各种遍历的变种,主要是写代码,尤其基于递归的代码。

首先假设是二叉排序树自然不用说了,推断的一句就是该节点的值是否位于输入的这两个节点之间,能够用前序遍历来做。

假设是普通的树或者二叉树,解题思路是一样的,能够考虑前序遍历,得到两个路径,用链表或数组保存起来,然后找出两条路径的最后一个公共节点就可以。也能够后序遍历的方式,遍历到输入的节点时,将该节点及其后面遍历到的节点都保存到一个链表或数组中,然后找出两条路径的第一个公共机节点就可以。

以下採用的是前序遍历,并用数组保存路径的代码。

#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>

typedef struct BTNode
{
	struct BTNode *lchild;
	struct BTNode *rchild;
	int data;
}BTNode,*BTree;

/*
前序遍历找出根节点到数据域为val的节点路径,保存在path数组中,
这里index是保存到path数组中的元素的下标,*len用来保存路径长度,
假设能找到val,则返回ture,找不到则返回false
*/
bool GetPreTraversePath(BTree pTree,int val,int *path,int index,int *len)
{
	if(pTree == NULL)
	{
		*len = 0;
		return false;
	}

	path[index] = pTree->data;
	if(pTree->data == val)
	{
		*len = index+1;
		return true;
	}
	else
	{
		bool can;
		can = GetPreTraversePath(pTree->lchild,val,path,index+1,len);
		if(!can)
			can = GetPreTraversePath(pTree->rchild,val,path,index+1,len);
		return can;
	}
}

/*
获取两个路径的最后一个公共节点,
因为对树的先序遍历的结果中,前面一定有些节点同样,因此一定能找到最后一个同样节点
*/
int GetFirstCommonNode(int *path1,int len1,int *path2,int len2)
{
	int shortLen = len1<len2 ? len1:len2;
	int i;
	for(i=0;i<shortLen;i++)
	{
		if(path1[i] != path2[i])
			break;
	}
	return path1[i-1];
}

/*
依据先序遍历序列创建二叉树,因为可能改变根节点的指向,因此传入BTNode的二级指针
*/
void CreateBTree(BTree *pRoot)
{
	int data;
	scanf("%d",&data);
	if(data == 0)
		*pRoot = NULL;
	else
	{
		*pRoot = (BTree)malloc(sizeof(BTNode));
		if(*pRoot == NULL)
			exit(EXIT_FAILURE);
		(*pRoot)->data = data;
		(*pRoot)->lchild = NULL;
		(*pRoot)->rchild = NULL;
		CreateBTree(&((*pRoot)->lchild));
		CreateBTree(&((*pRoot)->rchild));
	}
}

/*
销毁二叉树
*/
void DestroyBTree(BTree pRoot)
{
	if(pRoot != NULL)
	{
		if(pRoot->lchild != NULL)
			DestroyBTree(pRoot->lchild);
		if(pRoot->rchild != NULL)
			DestroyBTree(pRoot->rchild);
		free(pRoot);
		pRoot = NULL;
	}
}

int main()
{
	int path1[10000];
	int path2[10000];

	int n;
	while(scanf("%d",&n) != EOF)
	{
		int i;
		for(i=0;i<n;i++)
		{
			BTree pRoot = NULL;
			CreateBTree(&pRoot);

			int val1,val2;
			scanf("%d %d",&val1,&val2);

			int len1,len2;
			bool can1 = GetPreTraversePath(pRoot,val1,path1,0,&len1);
			bool can2 = GetPreTraversePath(pRoot,val2,path2,0,&len2);

			if(can1 && can2)
				printf("%d\n",GetFirstCommonNode(path1,len1,path2,len2));
			else
				printf("My God\n");

			DestroyBTree(pRoot);
		}
	}
	return 0;
}

/**************************************************************

    Problem: 1509

    User: mmc_maodun

    Language: C

    Result: Accepted

    Time:130 ms

    Memory:920 kb

****************************************************************/

PS:寻找路径的递归代码调了一个下午,总算搞定了,每次碰到关于树的问题,一般都是三种遍历方式的变种来实现,自然想到递推,有些写着非常easy,有些写着总非常纠结,尤其要返回bool变量的时候,还是太菜,回头要抽个时间把二叉树的递归实现的一些题目好好总结下。

【剑指offer】近期公共祖先

时间: 2024-10-18 18:59:15

【剑指offer】近期公共祖先的相关文章

【剑指offer】q50:树中结点的最近祖先

#@ root: the root of searched tree #@ nodeToFind: the tree-node to be found #@ path: the path from root to node #@@ #@@ search tree referenced by root, and return the path #@@ from root to node, if node not exist, path = [] #@@ def getPath(root, node

剑指offer (37) 两个链表的第一个公共结点

题目:输入两个链表,找出它们的第一个公共结点 如果两个链表有公共结点,那么公共结点一定出现在两个链表的尾部 如果两链表长度不相等,那么达到公共结点的步数就不一致,如何确保 两个链表从头开始遍历,同步达到公共结点? 这是关键所在 如果两链表长度相同,那么就可以同步达到了? 由此,我们就需要 让两个链表长度"相等" 我们假设 两链表长度分别为m和n,且m > n, 那么我们可以在较长链表中 先走 m - n 步,然后 两个链表游标同步走,如果有公共结点,那么就一定同时达到 ListN

【剑指offer】两个链表的第一个公共结点

转载请注明出处:http://blog.csdn.net/ns_code/article/details/26097395 简单题,剑指offer上的第37题,九度OJ上AC. 题目描述: 输入两个链表,找出它们的第一个公共结点. 输入: 输入可能包含多个测试样例.对于每个测试案例,输入的第一行为两个整数m和n(1<=m,n<=1000):代表将要输入的两个链表的元素的个数.接下来的两行,第一行为第一个链表的所有元素,中间用空格隔开.第二行为第二个链表的所有元素,中间用空格隔开. 输出: 对应

剑指Offer——Trie树(字典树)

剑指Offer--Trie树(字典树) Trie树 Trie树,即字典树,又称单词查找树或键树,是一种树形结构,是一种哈希树的变种.典型应用是统计和排序大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:最大限度地减少无谓的字符串比较,查询效率比哈希表高. Trie的核心思想是空间换时间.利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的. Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.可见,优

剑指 Offer 题目汇总索引

剑指 Offer 总目录:(共50道大题) 1. 赋值运算符函数(或应说复制拷贝函数问题) 2. 实现 Singleton 模式 (C#) 3.二维数组中的查找 4.替换空格               时间:O(n) 空间:O(1) 5.从尾到头打印链表 6. 重建二叉树          && 二叉树的各种遍历(BFS,DFS,DLR,LDR,LRD) 7.用两个栈实现队列 8.旋转数组的最小数字 9.斐波那契数列第 n 项        时间O(lgn) 10.一个整数的二进制表示中

《剑指offer》题解

有段时间准备找工作,囫囵吞枣地做了<剑指offer>提供的编程习题,下面是题解收集. 当初没写目录真是个坏习惯(-_-)||,自己写的东西都要到处找. 剑指Offer - 九度1524 - 复杂链表的复制 剑指Offer - 九度1509 - 树中两个结点的最低公共祖先 剑指Offer - 九度1508 - 把字符串转换成整数 剑指Offer - 九度1504 - 把数组排成最小的数 剑指Offer - 九度1503 - 二叉搜索树与双向链表 剑指Offer - 九度1390 - 矩形覆盖 剑

【剑指Offer学习】【所有面试题汇总】

剑指Offer学习 剑指Offer这本书已经学习完了,从中也学习到了不少的东西,现在做一个总的目录,供自已和大家一起参考,学如逆水行舟,不进则退.只有不断地学习才能跟上时候,跟得上技术的潮流! 所有代码下载[https://github.com/Wang-Jun-Chao/coding-interviews] 目录 第01-10题 [剑指Offer学习][面试题02:实现Singleton 模式--七种实现方式] [剑指Offer学习][面试题03:二维数组中的查找] [剑指Offer学习][面

剑指offer链表题的双指针法总结

本篇博客旨在总结双指针法在剑指offer链表题中的应用 包括删除链表中重复的节点.链表中倒数第k个节点.链表中环的入口节点.反转链表.合并两个排序的链表.两个链表的第一个公共节点. 根据双指针的类型,可以大致分为三种: 第一种是间隔一定距离的双指针法,包括删除链表中重复的节点.链表中倒数第k个节点两题 删除链表中重复的节点 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针. 例如,链表1->2->3->3->4->4->5

【剑指offer】翻转单词顺序

转载请注明出处:http://blog.csdn.net/ns_code/article/details/27372033 题目描写叙述: JOBDU近期来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上.同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看.但却读不懂它的意思.比如,"student. a am I".后来才意识到.这家伙原来把句子单词的顺序翻转了,正确的句子应该是"I am a student.". Cat对一一