【数据结构每日一题】链表——共享后缀

该文详细介绍了如何通过双指针法解决数据结构中的链表问题,特别是寻找两个链表的共享后缀。首先,通过初始化链表、计算链表长度和对齐尾结点来准备操作,然后通过同步移动指针找到公共后缀的起始位置。最后,通过比较链表节点来构造共享后缀链。代码实现包括链表初始化、长度计算、对齐和存储公共后缀等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

[数据结构习题]链表——共享后缀



👉知识点导航💎:【数据结构】线性表——顺序存储

👉知识点导航💎:【数据结构】线性表——链式存储

👉[王道数据结构]习题导航💎: p a g e 41.23 page41.23 page41.23

本节为链表相关的习题

在这里插入图片描述


题目描述:

在这里插入图片描述



🎇思路:双指针

🔱思路分析:

其实,对于这道题来说,思路很简单,在我们已知了公共后缀链之后,要得到公共后缀的首位置,由于是后缀相同,于是我们 将两个链表的表尾对齐之后,依次扫描,直到指向两个链表的指针 p , q p,q p,q指向同一位置时,即为公共元素起始地址


step:

  1. 分别求出 L 1 和 L 2 L1和L2 L1L2所指的链表长度 m , n m,n m,n
  2. 将两个链表以表尾对齐,使指针 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 mn
    ③若 n > m n>m n>m,则让指针 q q q先走,移动步数: n − m n-m nm
  3. 不断将指针 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

图解:

在这里插入图片描述


于是,我们可以将完整的实现拆分为以下几个步骤:


①初始化链表

链表的初始化:

  1. 不断开辟新空间存放结点,直到输入回车;
  2. 结束后,不要忘记让尾结点指向 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 pq,在函数内部使用时,则需要对二级指针进行解引用,才为指针 p , q p,q pq,即 ∗ 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:

  1. 首先,我们仍需要先将两个链表对齐;

  2. 然后我们开始同时移动两个指针 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 或 两个辅助指针对应的下一个结点的值不相同:

    1. 若为前者(下一个结点 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

    1. 若为后者(两个辅助指针对应的下一个结点的值不相同):
      则说明此时深搜结果不是公共后缀(因为不满足完全相同)
      则让 p , q p,q p,q 从新位置 a 1 − > n e x t a_1->next a1>next开始继续移动

图解算法:

在这里插入图片描述


代码实现:

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;
}

输出结果:

在这里插入图片描述



🎇这是一道关于单链表操作的真题~🎇

如有错误,欢迎指正~!

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DAY Ⅰ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值