一、题目描述
设指针la和lb分别指向两个无头结点单链表中的首元结点,试编写算法,从表la中删除自第i个元素起共len个元素,并将它们插入表lb的第j个元素之后。
函数接口定义:
void MoveLaToLb(LinkList *pa,int i,int len,LinkList *pb,int j);
其中 pa 和 pb 分别为两个单链表的头指针la和lb的指针。 i, j, len的意义与题目描述部分相同。注意:对参数的合法性进行必要的判断,以下几种情况认为参数不合法,不进行移动。
(1)当la表中不存在第i个元素,或者自第i个元素起后面不足 len个元素。
(2)当lb表中不存在第j个元素。(特别注意: 当 j值为0时,为合法参数,则在lb表第1个元素之前进行插入)
类型定义如下:
typedef int DataType;
typedef struct node
{
DataType data;
struct node *next;
}LNode,*LinkList;
裁判测试程序样例:
在这里插入代码片#include <stdio.h>
#include <stdlib.h>
typedef int DataType;
typedef struct node
{
DataType data;
struct node *next;
}LNode,*LinkList;
LinkList CreatLinkList(); //创建无头结点单链表,并返回头指针。
void PrintLinkList(LinkList H);//依次输出无头结点单链表H中各个元素结点,若为空表则输出NONE。
void MoveLaToLb(LinkList *pa,int i,int len,LinkList *pb,int j);
main()
{
LinkList la,lb;
int i,j,len;
la = CreatLinkList();
lb = CreatLinkList();
scanf("%d %d %d",&i,&j,&len);
MoveLaToLb(&la,i,len,&lb,j);
PrintLinkList(la);
PrintLinkList(lb);
}
LinkList CreatLinkList()
{
int n,i;
LNode *nw,*rear=NULL,*head=NULL;
scanf("%d",&n);//接收结点总数
for(i=0;i<n;i++)
{
nw=(LNode*)malloc(sizeof(LNode));
scanf("%d",&nw->data);
if(rear==NULL)
rear=head=nw;
else
{
rear->next=nw;
rear=nw;
}
}
if(rear)
rear->next=NULL;
return head;
}
void PrintLinkList(LinkList H)
{
LNode *p;
if(!H)
{
printf("NONE\n");
return;
}
for(p=H;p;p=p->next)
printf("%d ",p->data);
printf("\n");
}
/* 请在这里填写答案 */
二、示例与提示
示例 1:
输入:
7
1 2 3 4 5 6 7
6
11 22 33 44 55 66
3 4 2
输出:
1 2 5 6 7
11 22 33 44 3 4 55 66
示例 2:
输入:
7
1 2 3 4 5 6 7
6
11 22 33 44 55 66
1 0 4
输出:
5 6 7
1 2 3 4 11 22 33 44 55 66
示例 3:
输入:
7
1 2 3 4 5 6 7
6
11 22 33 44 55 66
6 2 4
输出:
1 2 3 4 5 6 7
11 22 33 44 55 66
示例 4:
输入:
3
1 2 3
4
11 22 33 44
1 4 3
输出:
NONE
11 22 33 44 1 2 3
三、思路
1.排除不合法情况
2.删除指定长度元素,重连LA
3.插入LB指定位置
本题意为将链表LA某指定位置起n个元素删除,并将其插入到链表LB指定位置之后。根据题目所给两种不合法情况,我们可以先通过遍历链表来计算两链表长度从而排除这两种不合法情况。之后通过遍历LA开始寻找LA第i个元素,找到该元素后标记为头,继续遍历len个元素找到要删除的结尾元素标记为尾,最后将LA重新拼接,完成删除操作,并且可以拿到被删除的新链表的头和尾,万事俱备,只剩插入LB最后一步!
(提示:看到这里可以自己先去尝试写写代码捏)
有的同学可能会遇到和我一样的问题,如果i=1,那么删除操作将出现错误。那么遇到这种情况,我们很自然会想到分类讨论将i=1这种情况单独列出来即可解决问题。之后再进行第三步,插入LB指定位置:
依题意:当j值为0时,为合法参数,则在LB表第1个元素之前进行插入(头插)。另一种情况则是插入链表中间某处,所以我们再次进行分类讨论即可。
四、代码
void MoveLaToLb(LinkList *pa,int i,int len,LinkList *pb,int j) {
// m:遍历链表L;n:被删除链表头;p:被删除链表尾;q:遍历链表LB;r:若i不为1,则为被删除链表头结点的前一个元素
LinkList m, n, p, q, r;
int length = 1;
// 遍历LA记录链表长度
m = *pa;
while (m) {
length++;
m = m->next;
}
// 若不存在第i个元素或第i个元素后不足len个元素,则返回
if (i + len > length)
return;
// 遍历LB记录链表长度
q = *pb;
length = 0;
while (q) {
length++;
q = q->next;
}
// 若不存在第j个元素则返回
if (j > length)
return;
m = *pa;
// 1.从首元素开始删除
if (i == 1) {
// n标记被删除链表头结点
n = m;
for (int k=0;k<len-1;k++) {
m = m->next;
}
// p标记被删除链表尾结点
p = m;
// LA链表重构,链表头结点为被删除尾元素的下一结点
*pa = m->next;
}
else {
// 2.从中间元素开始删除
for (int k=0;k<i-2;k++) {
m = m->next;
}
// r用来标记被删除链表头结点的前一结点(方便重新连接LA链表)
r = m;
// n标记被删除链表头结点
n = m->next;
for (int k=0;k<len;k++) {
m = m->next;
}
// p标记被删除链表尾结点
p = m;
// 重新连接LA链表
r->next = p->next;
}
q = *pb;
if (!j) {
// 1.头插:直接连接LB头结点
p->next = q;
// LB头结点设置为n
*pb = n;
}
else {
for (int k=0;k<j-1;k++) {
q = q->next;
}
// 2.从中间插入(先连接右边,再连接左边)
p->next = q->next;
q->next = n;
}
}
复杂度分析
- 时间复杂度:O(n)