[数据结构习题]链表——共享后缀
👉知识点导航💎:【数据结构】线性表——顺序存储
👉知识点导航💎:【数据结构】线性表——链式存储
👉[王道数据结构]习题导航💎: p a g e 41.23 page41.23 page41.23
本节为链表相关的习题 |
题目描述:
🎇思路:双指针
🔱思路分析:
其实,对于这道题来说,思路很简单,在我们已知了公共后缀链之后,要得到公共后缀的首位置,由于是后缀相同,于是我们 将两个链表的表尾对齐之后,依次扫描,直到指向两个链表的指针 p , q p,q p,q指向同一位置时,即为公共元素起始地址
step:
- 分别求出 L 1 和 L 2 L1和L2 L1和L2所指的链表长度 m , n m,n m,n
- 将两个链表以表尾对齐,使指针
p
,
q
p,q
p,q到表尾的距离相同:
①令指针 p , q p,q p,q分别指向 L 1 , L 2 L1,L2 L1,L2的头结点;
②若 m > n m>n m>n,则让指针 p p p先走,移动步数: m − n m-n m−n;
③若 n > m n>m n>m,则让指针 q q q先走,移动步数: n − m n-m n−m; - 不断将指针 p , q p,q p,q后移,当 p − > n e x t = = q − > n e x t p->next==q->next p−>next==q−>next时,即为公共后缀的起始地址,返回 p − > n e x t p->next p−>next
图解:
于是,我们可以将完整的实现拆分为以下几个步骤:
①初始化链表
链表的初始化:
- 不断开辟新空间存放结点,直到输入回车;
- 结束后,不要忘记让尾结点指向 N U L L NULL NULL
代码实现:
bool InitList(LinkList& L) {
L = (LNode *)malloc(sizeof(LNode)); //开辟空间存放头结点
LNode* p = L;
int x;
while (cin >> x) {
LNode* s0 = (LNode*)malloc(sizeof(LNode));
s0->data = x;
p->next = s0;
p = s0;
if (cin.get() == '\n')
break;
}
//不要忘了尾结点连NULL
p->next = NULL;
return true;
}
②求链表长度
循环遍历即可
int length(LinkList& L) {
int count = 0;
LNode* p = L;
while (p->next != NULL) {
p = p->next;
count++;
}
return count;
}
③链表尾结点对齐
要让尾结点对齐,我们需要先传入指向两个链表的头结点的指针 p , q p,q p,q
在函数中修改指针:
由于要对指针本身进行修改,我们则需要传入指针的地址,也就是指针的指针 ∗ ∗ p 和 ∗ ∗ q **p和**q ∗∗p和∗∗q,在函数内部使用时,则需要对二级指针进行解引用,才为指针 p , q p,q p,q,即 ∗ p = ( ∗ p ) − > n e x t *p=(*p)->next ∗p=(∗p)−>next
代码实现:
void alignment(LinkList& L1, LinkList& L2,LNode **p,LNode **q) {
int l1 = length(L1);
int l2 = length(L2);
int start;
int count = 0;
//较长链表的指针位置
if (l1 >= l2)
{
start = l1 - l2 ;
while (count != start) {
*p = (* p)->next;
count++;
}
}
else
{
start = l2 - l1 ;
while (count != start) {
*q = (*q)->next;
count++;
}
}
}
④求解公共后缀的首元素地址:
在得到对齐后的指针 p , q p,q p,q后,我们不断移动两个指针,直到相等,若最后 p − > n e x t p->next p−>next为空,则表示无公共后缀
代码实现:
LNode* Findindex(LinkList& L1, LinkList& L2) {
LNode* p = L1; LNode* q = L2;
alignment(L1, L2, &p, &q);
//同时移动
while (p->next != NULL && p->next != q->next) {
p = p->next;
q = q->next;
}
return p->next;
}
⑤构造共享后缀链:
对于两个单独存在的链表,我们如何将他们的公共后缀存储在同一片内存空间呢?
step:
-
首先,我们仍需要先将两个链表对齐;
-
然后我们开始同时移动两个指针 p , q p,q p,q,当指针指向的下一个结点 p − > n e x t p->next p−>next不为 N U L L NULL NULL时:
①如果两个单链表中的下一个结点对应的值不相同,则指针 p , q p,q p,q继续向后移动;
②如果两个单链表中的下一个结点对应的值相同,则复制两个 p , q p,q p,q的辅助指针 a 1 , a 2 a1,a2 a1,a2向后深搜,直到下一个结点 a 1 − > n e x t a1->next a1−>next为 N U L L NULL NULL 或 两个辅助指针对应的下一个结点的值不相同:
- 若为前者(下一个结点
a
1
−
>
n
e
x
t
a1->next
a1−>next为
N
U
L
L
NULL
NULL):
则说明此时深搜结果是公共后缀
则让 p − > n e x t p->next p−>next 指向 q − > n e x t q->next q−>next
- 若为后者(两个辅助指针对应的下一个结点的值不相同):
则说明此时深搜结果不是公共后缀(因为不满足完全相同)
则让 p , q p,q p,q 从新位置 a 1 − > n e x t a_1->next a1−>next开始继续移动
- 若为前者(下一个结点
a
1
−
>
n
e
x
t
a1->next
a1−>next为
N
U
L
L
NULL
NULL):
图解算法:
代码实现:
void Storage(LinkList& L1, LinkList& L2) {
LNode* p = L1; LNode* q = L2;
alignment(L1, L2, &p, &q);
//存储公共后缀链
while (p->next != NULL ) {
//1.下一个元素相同
if (p->next->data == q->next->data) { //如果数据相同
LNode* a1 = p->next;
LNode* a2 = q->next;
while (a1->next!=NULL && a1->next->data==a2->next->data) { //不断比较后续结点是否相同
a1 = a1->next;
a2 = a2->next;
}
//1.有公共后缀
if (a1->next == NULL) {
p->next = q->next; //共享后缀,结束
break;
}
//2.暂时没有公共后缀 :也就是a1->next!=a2->next
else {
p = a1->next;
q = a2->next; //以新起点开始,继续往下比较
continue;
}
}
//2.下一个元素不同
else{
p = p->next;
q = q->next; //向后移动,继续找
}
}
}
完整代码实现:
#include<iostream>
using namespace std;
typedef struct LNode {
int data;
struct LNode* next;
}LNode,*LinkList;
//初始化
bool InitList(LinkList& L) {
L = (LNode *)malloc(sizeof(LNode)); //开辟空间存放头结点
LNode* p = L;
int x;
while (cin >> x) {
LNode* s0 = (LNode*)malloc(sizeof(LNode));
s0->data = x;
p->next = s0;
p = s0;
if (cin.get() == '\n')
break;
}
//不要忘了尾结点连NULL
p->next = NULL;
return true;
}
//求长度
int length(LinkList& L) {
int count = 0;
LNode* p = L;
while (p->next != NULL) {
p = p->next;
count++;
}
return count;
}
//1.对齐
void alignment(LinkList& L1, LinkList& L2,LNode **p,LNode **q) {
int l1 = length(L1);
int l2 = length(L2);
int start;
int count = 0;
//较长链表的指针位置
if (l1 >= l2)
{
start = l1 - l2 ;
while (count != start) {
*p = (* p)->next;
count++;
}
}
else
{
start = l2 - l1 ;
while (count != start) {
*q = (*q)->next;
count++;
}
}
}
//2.共享后缀链
void Storage(LinkList& L1, LinkList& L2) {
LNode* p = L1; LNode* q = L2;
alignment(L1, L2, &p, &q);
//存储公共后缀链
while (p->next != NULL ) {
//1.下一个元素相同
if (p->next->data == q->next->data) { //如果数据相同
LNode* a1 = p->next;
LNode* a2 = q->next;
while (a1->next!=NULL && a1->next->data==a2->next->data) { //不断比较后续结点是否相同
a1 = a1->next;
a2 = a2->next;
}
//1.有公共后缀
if (a1->next == NULL) {
p->next = q->next; //共享后缀,结束
break;
}
//2.暂时没有公共后缀 :也就是a1->next!=a2->next
else {
p = a1->next;
q = a2->next; //以新起点开始,继续往下比较
continue;
}
}
//2.下一个元素不同
else{
p = p->next;
q = q->next; //向后移动,继续找
}
}
}
//3.查找位置
LNode* Findindex(LinkList& L1, LinkList& L2) {
LNode* p = L1; LNode* q = L2;
alignment(L1, L2, &p, &q);
//同时移动
while (p->next != NULL && p->next != q->next) {
p = p->next;
q = q->next;
}
return p->next;
}
int main() {
LinkList L1;
LinkList L2;
//初始化
cout << "请输入链表L1的元素:" << endl;
InitList(L1);
cout << "请输入链表L2的元素:" << endl;
InitList(L2);
//合成为公共后缀链
Storage(L1, L2);
//查找存储链的公共后缀的首地址
if (Findindex(L1,L2))
{
cout << "公共后缀的首地址为:" << Findindex(L1, L2) << endl;
cout << "公共后缀的首元素为:" << Findindex(L1, L2)->data << endl;
}
else
cout << "不存在公共后缀" << endl;
system("pause");
return 0;
}
输出结果: