运用求最近公共祖先思想秒杀2012考研408数据结构算法大题

22王道数据结构P39 T22
【2012统考真题】假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,可共享相同的后缀存储空间,例如"loading"和"begin"的存储映像如下图所示。

在这里插入图片描述
设str1和str2分别指向两个单词所在单链表的头结点,链表的结点结构为data,next,请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀的起始位置(如图中字符i所在结点的位置p)

拿到题目我们可以先把链表结点结构的结构体写出来

struct LNode{
     
	char data;//数据域
	LNode *next;//指针域 
};
typedef LNode* LinkList;

然后我们很容易的就想到了一个很暴力的算法,str1这个链表中的每一个结点和str2中的每一个结点进行比较,假设两个链表的长度分别是len1和len2,那么这个算法的时间复杂度为 O ( len ⁡ 1 × len ⁡ 2 ) \mathrm{O}(\operatorname{len} 1 \times \operatorname{len} 2) O(len1×len2)
下面我们用代码来实现一下

LNode* search_start_suffix(LinkList &str1,LinkList &str2){
     
	//寻找str1和str2两个链表中共同后缀的起始位置
	LNode *p1=str1->next,*p2=str2->next;
	while(p1!=NULL){
     
		p2=str2->next;
		while(p2!=NULL){
     
			if(p1==p2)
				return p1;//找到共同的结点
			p2=p2->next; 
		}
		p1=p1->next;
	} 
	return NULL;//没有共同结点 		
} 

作为跨考的同学和基础不好的同学,能写出上述算法已经很不错了,该题也能拿到大部分分数,毕竟标准答案给出的算法如果没有接触过的话的确很难想到,但如果作为本科就是计算机专业的同学,结合本科课程学的一些知识,有能力能快速准确的秒杀本题。

下述内容需要一定的专业知识,跨考生可跳过

如果本科数据结构学的还可以的,第一眼看过去可以发现本题有点类似于Trie(前缀树,字典树),但是Trie的前缀是公用的,而本题公用的是后缀。如果忘了Trie是什么的同学可以看一下下面这道题目

10. 英文拼写检查工具
目的:
通过设计一个英文拼写检查工具,提高学生通过所学知识解决实际问题。
内容:
设计一个“英文拼写检查工具”,具体描述如下:
(1) 待检查的英文文本文件存放在checked.txt文本中;
(2) 对checked.txt文本中的单词进行拼写检查,将检查结果输出到checkout.txt文件中。其格式为:
单词 判断结果
This 0
Thee 1
ada -1
(0 表示正确 1表示错误 -1表示无法判断)
(3) 在checkout.txt文件最后给出统计果:
总词汇数:xxxx 正确词汇数:xxxx 错误词汇: xxx 无法判断:xxxx。
报告要求:
(1)仔细观察设计中的各种现象及出现的问题。分析产生各种现象的原因。寻找解决问题的办法。
(2)报告应至少包括带注释的程序清单、输出的结果及对各种现象的分析意见。

相信你们在学数据结构这门课程的时候一定做过这种类似的题目,具体实现方法就是运用Trie。虽然本题看似是Trie的样子,但是并不需要用Trie的知识解答,由于不是本文的重点,所以就不再详细展开讲解。
如果想复习Trie相关知识的可以参考我这篇文章leetcode208. 实现 Trie (前缀树)

相信你们在学习图论的时候,一定会遇到一道题目,求树上两个结点x,y的最短距离,回想一下你们离散数学,数据结构,算法设计与分析这些课,一定遇到过这个问题,这个问题的解法是求这两个结点的最近公共祖先,该结点就是结点x与y路上深度最小的结点,也就是路上的拐弯点。
运用求最近公共祖先思想秒杀2012考研408数据结构算法大题_第1张图片
图片来自于李煜东著作的《算法竞赛进阶指南》

求LCA的方法有很多,本题运用的就是一种“同步深度,一起往上爬”的思想,同步深度就是如果结点x与y深度不一样,那么先让深度深的往上爬,爬到和另一个结点深度相同的位置,这样他们到最近公共祖先的结点的距离是一样的,一起往上爬就是一边比较一边往上爬直到到达相同结点,该结点就是结点x与y的最近公共祖先。
下面我们把题目的图片旋转逆时针旋转90°,是不是就可以用求LCA的方法做本题。
所以本题的做法我们可以总结为“同步长度,一起往后走

算法设计思想如下:
如果两个结点x和y是公共结点,那么这两个结点到达尾结点的距离是相等的,所以只有两个距离尾结点距离相等的结点才有机会成为公共结点。
用指针p1,p2分别指向两个链表的头结点,然后分别遍历两个链表求出链表的表长len1,len2,如果链表的表长不相等的话,让指向表长较长的链表的指针先往后走,直到剩余长度和另一个链表相同,然后p1,p2一边比较一边往后走,直到指向相同结点或链表尾为止。

代码实现如下:

int length(LinkList &L){
     
	//求链表L的结点个数
	Lnode *p=L;//工作指针 
	int count=0;//计数 
	while(p->next!=NULL){
     
		p=p->next;
		count++;
	} 
	return count;
} 

LNode* search_start_suffix(LinkList &str1,LinkList &str2){
     
	//寻找str1和str2两个链表中共同后缀的起始位置
	LNode *p1=str1,*p2=str2;
	int len1=length(str1),len2=length(str2);
	
	//同步长度,长的先走
	if(len1<len2)
		for(int i=0;i<len2-len1;i++)
			p2=p2->next;
	if(len1>len2)
		for(int i=0;i<len1-len2;i++)
			p1=p1->next;
	
	//一起往前走
	while(p1!=NULL){
     
		if(p1!=p2){
     
			p1=p1->next;
			p2=p2->next
		}
		else
			return p1;//找到共同结点 
	}
	return NULL;//没有共同结点	
} 

本算法的时间复杂度为 O ( l e n 1 + l e n 2 ) O(len1+len2) O(len1+len2),可见该算法优于第一种算法。
总结:作为本科就是计算机专业的考生,如果本科有扎实的基本功那么遇到这种类型的题目可以达到快速秒杀,从而和其他考生拉开差距。

你可能感兴趣的:(数据结构(考研),数据结构)