今日习题来自B站up:白话拆解数据结构
题目如下:
1、判断B链表的值是否是A链表值的连续子序列
2、假定采用带头结点的单链表保存单词,当两个单词有相同的后缀时,则可共享相同的后缀存储空间,例如,"loading"和"being"的存储映像如下图所示。
设str1和str2分别指向两个单词所在单链表的头结点,链表结点结构为{[data][next]},请设计一个时间上尽可能高效的算法,找出由str1和str2所指向两个链表共同后缀的起始位置(如图中字符i所在结点的位置p)。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或 C++语言描述算法,关键之处给出注释。
(3)说明你所设计算法的时间复杂度。
题1
这个好说,因为加了一堆限制条件,连续子序列就是得一个个比,比不成功就从头开始比,就完全类似于模式串的暴力匹配。
bool xulie(Linklist A,Linklist B){
Lnode *p,*q,*s;
p=A->next; //p、q用来遍历
q=B->next;
s=A->next; // s用来指示a表的下一次匹配开始位置
while(p&&q){
if(s->data==q->data){ //第一个值匹配成功了,就接着匹配
s=s->next;
q=q->next;
}
else{ // 匹配不成功,就重新来
p=p->next;
s=p;
q=B->next;
}
}
if(!q) return true; // 匹配成功一定是q指向空了
if(q) return false;
}
演示一下:
完整代码如下:
cpp
#include <iostream>
#include <cstdio>
#include <ctime>
using namespace std;
// 单链表结构体定义
typedef struct Lnode{
int data;
Lnode *next;
}Lnode,*Linklist;
Linklist list_insertbytail(Linklist &L){
Lnode *s;
int x;
L = (Lnode*)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *r = L;
cin >> x;
while(x!=9999){
s = (Lnode*)malloc(sizeof(Lnode));
s->data=x;
s->next=NULL;
r->next = s;
r=r->next;
cin >> x;
}
return L;
}
// 判断B链表是否是A链表的连续子序列
bool xulie(Linklist A,Linklist B){
Lnode *p,*q,*s;
p=A->next;
q=B->next;
s=A->next;
while(p&&q){
if(s->data==q->data){
s=s->next;
q=q->next;
}
else{
p=p->next;
s=p;
q=B->next;
}
}
if(!q) return true;
if(q) return false;
}
int main(){
Linklist A,B;
list_insertbytail(A);
list_insertbytail(B);
printf("\n");
if(xulie(A,B))
printf("success find\n");
else printf("None");
return 0;
}
题2
结合题目图片来看,我们要做的事情如下:
(1)确定两个表的表长
(2)遍历两个表,找到第一个地址相等的位置
现在来说为什么要做(1),我们假设两个表表长不相等,而我们找到p结点位置只能通过指针向后遍历,如果从表头开始找,因为表长不相等,一定会错过;如果嵌套循环来找,复杂度又太高了。
所以我们可以先计算表长,让两个指针对齐开始往后找,可以省略很多不必要的麻烦。
通过(1),我们对齐了指针,就可以让指针同步往后找,因为本题让我们找的是位置,所以我们拿一个指针指向它,返回这个指针就行了,因为指针存的不就是地址嘛
int listlength(Linklist L){
Lnode *p=L->next;
int i=1;
for(;p;i++,p=p->next);
return i;
}
//找两个链表公共后缀的起始位置
Linklist loc(Linklist str1,Linklist str2){
Lnode *p,*q;
p=str1->next;
q=str2->next;
int str1_length=listlength(str1); // 求表长
int str2_length=listlength(str2);
int s = str1_length-str2_length;
// 指针对齐
if(s > 0){
for(int i=0;i<s;i++,p=p->next);
}
else{
for(int i=0;i<-s;i++,q=q->next);
}
while(p&&q){
if(p==q) return p; // 返回p还是q都可以
else{
p=p->next;
q=q->next;
}
}
return nullptr; //没找到
}
演示一下:
完整代码如下:
cpp
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
using namespace std;
// 结构体定义
typedef struct Lnode{
char data;
Lnode *next;
}Lnode,*Linklist;
Linklist list_insertbytail(Linklist &L){
Lnode *s;
char x;
L = (Lnode*)malloc(sizeof(Lnode));
L->next = NULL;
Lnode *r = L;
cin >> x;
while(x!='z'){
s = (Lnode*)malloc(sizeof(Lnode));
s->data=x;
s->next=NULL;
r->next = s;
r=r->next;
cin >> x;
}
return L;
}
int listlength(Linklist L){
Lnode *p=L->next;
int i=1;
for(;p;i++,p=p->next);
return i;
}
//找两个链表公共后缀的起始位置
Linklist loc(Linklist str1,Linklist str2){
Lnode *p,*q;
p=str1->next;
q=str2->next;
int str1_length=listlength(str1);
int str2_length=listlength(str2);
int s = str1_length-str2_length;
if(s > 0){
for(int i=0;i<s;i++,p=p->next);
}
else{
for(int i=0;i<-s;i++,q=q->next);
}
while(p&&q){
if(p==q) return p;
else{
p=p->next;
q=q->next;
}
}
return nullptr;
}
int main(){
Linklist A, B;
cout << "请输入链表A的元素(以'z'结束):" << endl;
list_insertbytail(A);
cout << "请输入链表B的元素(以'z'结束):" << endl;
list_insertbytail(B);
Lnode *p = loc(A, B);
if (p) {
cout << "公共序列为:" << endl;
for (; p; p = p->next)
printf("%c -> ", p->data);
printf("NULL\n");
} else {
cout << "没有公共后缀。" << endl;
}
return 0;
}