真题题目
假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,例如,“loading”和“being”的存储映像如下图所示。
设str1
和str2
分别指向两个单词所在单链表的头结点,链表结点结构为{[data][next]},请设计一个时间上尽可能高效的算法,找出由str1
和str2
所指向两个链表共同后缀的起始位置(如图中字符i
所在结点的位置p
)。要求:
- 给出算法的基本设计思想。
- 根据设计思想,采用C或 C++语言描述算法,关键之处给出注释。
- 说明你所设计算法的时间复杂度。
(本文重点关注算法实现及思路分析,不含具体答题表述)
分析实现
和判断子串的算法不同,本题中明确是检查相同的后缀。因此,一个简单的思路是将两个单词逆置,然后逐步比较到不同的结点即可。
但以上思路会破坏两单词链表的结构,若要实现则需要新建链表进行模拟,再记录下共同后缀的起始位置完成搜索。虽然可以在 O ( n ) O(n) O(n)的时间复杂度下完成,但还是相对繁琐的。
至于优化的思路,可以“反过来想”——将较长的单词多出来的字母“舍去”,这样两单词从头开始扫描,遇到的第一个共同结点就是目标结点。
具体在代码中可以使用双指针来实现这一思路,具体实现如下:
// 定义链表结点结构体
struct LNode{
char data;
LNode *next;
};
// 辅助函数,计算链表的长度
int getLength(LNode *head){
int len=0;
while(head){
len++;
head=head->next;
}
return len;
}
// 查找第一个相同结点
LNode* findFirstCom(LNode *str1, LNode *str2){
int m = getLength(str1);
int n = getLength(str2);
// cur1和cur2分别指向两单词中要匹配的字母
LNode *cur1=str1, *cur2=str2;
// “舍去”单词1中的字母
while(m>n){
m--;
cur1=cur1->next;
}
// “舍去”单词2中的字母
while(m<n){
n--;
cur2=cur2->next;
}
// 扫描第一个相同结点
while(cur1!=cur2){
cur1=cur1->next;
cur2=cur2->next;
}
return cur1;
}
总结
以上就是运用双指针找出两链表共同后缀起始位置的实现,思路相对比较新颖,但操作并不复杂。