在预备役的链表题组中徘徊了一天(前两天待在结构体题组),终于把链表题组写完。
下面进行总结:
1.感觉链表要在脑海中要有链表的结构图(链表的前后连接关系)。
2.在脑海里难有整个链表的关系图可以在纸上多进行推算。
3搞清链表的插入删除时候的链接顺序,找到插入点、删除点。
4我感觉链表改掉了我一些小毛病,纠正了我平时总是喜欢把代码一股脑往主函数丢(因为有的链表的操作很多并且都是重复的),这就不得不教你做人啦,哈哈!把每个操作写成一个函数会非常简便、清晰,看着自己也舒服。
5.链表因为操作多,实现有些冗余,写成函数。无意识中培养了我分析问题,拆解问题将每一个操作进行模块化(哈哈,看起来是不是有点高大上了。)
下面将链表题组的几个有意思的代码copy如下,可能我写的比起其他大佬的有点冗余,但是能ac的代码就是好代码,不是吗?我将最能教我做人的链表放最后面
问题 C: 链表排列(线性表)
(线性表)已知不带头结点的线性链表list,链表中结点构造为(data、link),其中data为数据域,link为指针域。请写一算法,将该链表按结点数据域的值的大小从小到大重新链接。要求链接过程中不得使用除该链表以外的任何链结点空间。
这个题是要求我们不能另外创建其他空间来完成。
以下是没有创建任何类型的空间的方法,在这里我就知道了链表比起数组的优点能够在不动用其他空间变量的前提下能够进行排序,但是数组不能,得用第三方空间来做临时存储变量。
以下是不带头结点的排序方法:
主要思路是类似于数组中的插入排序,首先将第一个结点看成是有序的(因为一个结点无论是升序还是降序都是有序的)将第一个结点断开,从第二个结点开始,将结点插入进前面那有序链表中去,而在找插入点的过程中要考虑三种情况:第一种是插入点是第一个结点的时候要单独处理,还有就是当插入点在中间和插入点在最后的时候,但是插入点在中间和插入点在最后的处理方式一样,就归为一体,所以可以分为两种情况。
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
int main()
{
int i,j,k,n;
struct node *p,*q,*head,*p1,*t;
scanf("%d",&n);
head=p=(struct node*)malloc(sizeof(struct node));
scanf("%d",&p->data);
for(i=1; i<n; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
q=head->next;
head->next=NULL;
while(q)
{
p1=NULL;//p1和p是一前一后的移动,所以p1最先赋值空
p=head;
while(p)
{
if(q->data<p->data)//说明找到插入点了,结束循环
break;
else
{
p1=p;//没找到插入点,p1记录p的位置,p就可以后移了
p=p->next;
}
}
t=q->next;//在插入的时候要保留q后面的那个值,所以要先用t来定位
if(p1==NULL)
{
q->next=head;
head=q;
}
else
{
q->next=p;
p1->next=q;
}
q=t;//将q还原
}
p=head;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
}
下面是带头结点的排序。带头结点用起来就要简单很多了。因为不需要考虑插入点在头,中间,和最后的情况,操作都是一致的,所以我想这就是大多数人做链表都用带头结点来做吧。我就用一个函数的形式来表示
struct node * sort(struct node *head)
{
struct node *p,*q,*q1,*t,*p1;
q=head->next->next;//定位到第二个结点
head->next->next=NULL;//这里的操作是将第一个结点的指针域赋值为空也就是将第一个结点与后面的结点断开
while(q)//一次将后面的结点插入进前面去所以是q不等于空
{
p=head;
while(p->next!=NULL)//这里是前面有序链表的循环插入点的查找,因为是带头结点的,都是引用后面结点的数值来判断,所以要保证后面有结点所以是P->next不等于空
{
if(q->data<p->next->data)//后面无序结点的数值比前面有序的链表中结点数值小,就说明找到了插入点,可以退出循环
break;
else
{
p=p->next;
}
}
t=q->next;//老样子,找到要插入结点的下一个结点的地址
q->next=p->next;
p->next=q;
q=t;//还原,实际上也后移了
}
}
问题 G: 合并链表(线性表)
描述
(线性表)假设有两个按元素值递增次序排列的线性表,均以单链表形式存储。请编写算法将这两个单链表归并为一个按元素值递减次序排列的单链表,并要求利用原来两个单链表的结点存放归并后的单链表。
格式
输入格式
输入长度n:5
输入数据:1 2 5 6 8
输入长度m:5
输入数据:3 4 7 9 10
输出格式
10 9 8 7 6 5 4 3 2 1
这道题我写的有点冗余,当初是想着稳中求胜来着,主要步骤是进行新建链表,然后对链表排序,再对两个链表进行归并,嗯~~,没错。
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
void sort (struct node *head)
{
struct node *p,*q,*n;
n=head->next;
q=head->next->next;
while(q)
{
p=head->next;
head->next=q;
q=q->next;
head->next->next=p;
}
n->next=NULL;
p=head->next;
return ;
}
void fun (struct node *head1,struct node*head2)
{struct node *p,*q,*Q,*head3;
head3=Q=(struct node*)malloc(sizeof(struct node));
p=head1->next;
q=head2->next;
while(p&&q)
{
if(p->data>q->data)
{
Q->next=p;
p=p->next;
Q=Q->next;
}
else { Q->next=q;
q=q->next;
Q=Q->next;
}
}
while(p)
{
Q->next=p;Q=Q->next;
p=p->next;
}
while(q)
{
Q->next=q;Q=Q->next;
q=q->next;
}
p=head3->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
}
int main()
{
int i,j,k,n,m;
struct node *p,*q,*head1,*head2,*p1,*t;
scanf("%d",&n);
head1=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<n; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
scanf("%d",&m);
head2=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<m; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
sort(head1);
sort(head2);
fun(head1,head2);
}
问题 F: 双向链表
描述
构建一个双向链表并进行删除和插入操作,按要求输出。
格式
输入格式
输入:
第一行输入元素个数M
第二行输入M个元素
第三行输入删除位置,位置为0时不删除
第四行输入插入位置和插入元素
第五行输入输出时的起始位置
输出格式
按要求的起始位置输出链表
样例
样例输入 Copy
8
1 2 3 4 5 6 7 8
6
6 6
5
这道题是个双向链表,就是在单向链表的尾结点的指针域赋值为一个head,将他们首尾相连,嗯~~没错。主要是按题目要求的步骤一步一步来。
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
} ;
struct node * fun (int m)//创建带头结点链表
{
int i;
struct node *p,*q,*head;
head=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<m; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=head->next;//双向链表,将链表尾链接到头去
return head;
}
struct node *sort( struct node *head,int n)//删除操作这里我断开了,但是没有Free释放掉它
{
int i=1;
struct node *p;
p=head->next;
while(i<n-1)
{
p=p->next;
i++;
}
p->next=p->next->next;
return head;
}
struct node *sort1( struct node *head,int n,int m)//插入操作
{
struct node *p,*q,*t;
int i=1;
p=head->next;
while(i<n-1)
{
p=p->next;
i++;
}
q=p->next;
t=(struct node*)malloc(sizeof(struct node));
t->data=m;
p->next=t;
t->next=q;
return head;
}
void sort2( struct node *head,int n)//循环输出
{
int i=1;
struct node *p,*q,*t;
p=head->next;
while(i<n)
{
p=p->next;
i++;
}
printf("%d ",p->data);
q=p->next;
while(q!=p)
{
printf("%d ",q->data);
q=q->next;
}
}
int main()
{
struct node *head,*p;
int i,n,m,k;
scanf("%d",&n);
head=fun(n);
scanf("%d",&m);
if(m!=0)
sort(head,m);
scanf("%d%d",&m,&k);
sort1(head,m,k);
scanf("%d",&n);
sort2(head,n);
}
问题 H: 链表遍历(JSU-ZJJ)
描述
小T昨天很不爽
昨天路过体彩销售点买了2注生日号码玩,当晚开奖后……
小T号码: 8902088
中奖号码:3902888
一个号码不对就差了十几万……
小T想:为什么规则是那样玩的啊……为什么5个号码要连续才能中二等奖啊……
我自己创建个规则,开N个数字,只要数字位置对了不一定要连续就可以有奖。
现在有一个中奖号码,有一个兑奖号码,小T想知道一共有多少个数字位置正确的,N太大了……
要求用链表做
格式
输入格式
输入数据第一行包含一个整数T表示有T组测试数据
对于每组测试数据
第一行包含一个整数N,表示有N个数字
第二行包含N个中奖号码
第三行包含N个兑奖号码
输出格式
对于每组测试数据输出有多少个号码位置正确了
样例
样例输入 Copy
2
7
3902888
8902088
10
1234567890
0987654321
样例输出 Copy
5
0
这道题,题目给出了每组输入数据的个数,都一致的话,就直接两个链表同时进行循环比较,相等的话累加器加一就行。
#include <stdio.h>
#include <stdlib.h>
struct node
{
char data;
struct node *next;
} ;
struct node * fun (int m)//创建带头结点链表
{
int i;
struct node *p,*q,*head;
head=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<m; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%c",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
return head;
}
void sort(struct node *head1,struct node *head2)//将两个链表进行比较,算出相同位置上的数据的个数
{ int sum=0;
struct node *p,*q;
p=head1->next;
q=head2->next;
while(p&&q)
{
if(p->data==q->data)
sum++;
p=p->next;
q=q->next;
}
printf("%d\n",sum);
}
int main()
{
struct node *head1,*head2,*p;
int i,n,m,k;
scanf("%d",&k);
while(k--)
{
scanf("%d",&n);
getchar();//在这里我的链表数据域是以字符形式创建的所以将回车读掉
head1=fun(n);
getchar();
head2=fun(n);
sort(head1,head2);
}
}
它来了,没错,它走来了~~~~~教会我分解问题,和用函数来模块化的问题,教育大师。
问题 A: 链表(软工)
描述
有两个链表a和b,从a、b中删除它两重复的元素(只要重复就删除),并对删除后的a链表进行升序排序,b链表进行降序排序。a 的长度为m,b 的长度为n
格式
输入格式
输入a,b的长度m,n
a、b链表
输出格式
处理后的a、b链表
样例
样例输入 Copy
9
6
1 2 3 4 5 6 7 8 6
5 6 7 10 0 5
样例输出 Copy
1 2 3 4 8
10 0
提示
本题是多组输入。
这道题,一开始我写了168行代码,但是由于看错题了,格式错误。QWQ
以为是自身链表重复的数字也要删掉,例如
5 5
1 2 2 2 2
3 4 4 4 4
我一开始认为输出应该是
1
3
但是因为两个链表中相对对方而言没有重复的数据,其实应该是
1 2 2 2 2
3 4 4 4 4
下面我先将第一种情况的代码展示(毕竟推了好久又写了好久)
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node * fun (int m)//创建带头结点的链表
{
int i;
struct node *p,*q,*head;
head=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<m; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
return head;
}
struct node * sort(struct node *head)//在这里用的是带头结点的插入法排序
{
struct node *p,*q,*q1,*t,*p1;
q=head->next->next;
head->next->next=NULL;
while(q)
{
p=head;
while(p->next!=NULL)
{
if(q->data<p->next->data)
break;
else
{
p=p->next;
}
}
t=q->next;
q->next=p->next;
p->next=q;
q=t;
}
}
struct node * sort1(struct node *head)//带头结点的带头结点降序
{
struct node *p,*q,*q1,*t,*p1;
q=head->next->next;
head->next->next=NULL;
while(q)
{
p=head;
while(p->next!=NULL)
{
if(q->data>p->next->data)
break;
else
{
p=p->next;
}
}
t=q->next;
q->next=p->next;
p->next=q;
q=t;
}
}
void del( struct node *head)//将每个链表进行去重处理
{
struct node *p,*q;
p=head->next;
q=head->next->next;
while(q)
{
if(p->data==q->data)//因为链表是有序的,重复的数据都是在一起的,如果重复了就直接断开这个结点,当然,这里没free掉
{
p->next=q->next;
}
else//如果不重复p就后移到q的位置
{
p=q;
}
q=q->next;
}
}
void delall( struct node *head1, struct node *head2)//将两个链表中重复的数据删除
{
struct node *p,*q,*r,*t;
p=head1;
while(p->next)
{
q=head2;
while(q->next)//因为是带头结点的
{
if(p->next->data==q->next->data)如果两个链表中的数据相同
{
p->next=p->next->next;//两个数据都进行删除,因为是进行去重操作的,只可能有一个,删除过后后面就不会再有相同数据了,所以可以直接退出循环
q->next=q->next->next;
break;
}
else
q=q->next;//不相同就可以直接后移
}
if(q->next==NULL)
//这里是重点,检查它有没有到最后一个结点是看它有没有进行删除操作,
//如果不等于空说明进行过删除操作,它在删除过后将重复数据断开,
//连的是后面的后面那个数据实际上是相当于后移过的所以就不用后移了,
//如果没有进行删除操作,就会一直循环到最后一个,就需要后移。
p=p->next;
}
p=head1;
while(p->next)
{
printf("%d ",p->next->data);
p=p->next;
}
printf("\n");
p=head2;
while(p->next)
{
printf("%d ",p->next->data);
p=p->next;
}
}
int main()
{
struct node *p,*q,*head1,*head2;
int i,j,k,n,m;
while(~scanf("%d%d",&n,&m))
{
head1=fun(n);
head2=fun(m);
sort(head1);
sort1(head2);
del(head1);
del(head2);
delall(head1,head2);
}
}
好了下面来放上AC的代码啦
#include <stdio.h>
#include <stdlib.h>
struct node
{
int data;
struct node *next;
};
struct node * fun (int m)//同样,创建带头链表
{
int i;
struct node *p,*q,*head;
head=p=(struct node*)malloc(sizeof(struct node));
for(i=0; i<m; i++)
{
q=(struct node*)malloc(sizeof(struct node));
scanf("%d",&q->data);
p->next=q;
p=q;
}
q->next=NULL;
return head;
}
struct node * sort(struct node *head)//进行升序排序处理
{
struct node *p,*q,*q1,*t,*p1;
q=head->next->next;
head->next->next=NULL;
while(q)
{
p=head;
while(p->next!=NULL)
{
if(q->data<p->next->data)
break;
else
{
p=p->next;
}
}
t=q->next;
q->next=p->next;
p->next=q;
q=t;
}
}
struct node * sort1(struct node *head)//降序排序处理
{
struct node *p,*q,*q1,*t,*p1;
q=head->next->next;
head->next->next=NULL;
while(q)
{
p=head;
while(p->next!=NULL)
{
if(q->data>p->next->data)
break;
else
{
p=p->next;
}
}
t=q->next;
q->next=p->next;
p->next=q;
q=t;
}
}
void del( struct node *head)
{
struct node *p,*q;
p=head->next;//对与第一个数据进行查找并删除,这里也模拟了带头结点,所以第一个结点是head->next
q=head->next->next;//从第二个结点开始查找相同数据
while(q)
{
if(q->data!=p->data)//如果不相同就后移
{
q=q->next;
}
else//找到了就可以删除了
{
p->next=q->next;
q=q->next;
}
}
head->next=head->next->next;因为找与第一个结点相同的数据并删除,第一个结点也要删除,所以直接把第一个结点断开
}
void delall( struct node *head1, struct node *head2)
{
struct node *p,*q,*r,*t;
int flag=1;//Flag是用来检测是否在另一个链表有相同数据,
//如果有也要对自身链表的相同数据进行删除
p=head1;
while(p->next)
{
flag=1;
q=head2;
while(q->next)
{
if(p->next->data==q->next->data)//两个链表进行比较
{
q->next=q->next->next;//对另一个链表中相同的数据删除
flag=0;//同时标明找到了相同数据,
}
else
q=q->next;
}
if(flag==0)//如果在对面里面找到相同数据
del(p);//对自身进行删除,其实将后面不重复的数据向前移,实际上就不需要后移了
if(flag==1)//如果没有相同数据就可以后移了
p=p->next;
}
p=head1->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
p=head2->next;
while(p)
{
printf("%d ",p->data);
p=p->next;
}
printf("\n");
}
int main()
{
struct node *p,*q,*head1,*head2;
int n,m;
while(~scanf("%d%d",&n,&m))
{
head1=fun(n);
head2=fun(m);
sort(head1);
sort1(head2);
delall(head1,head2);
}
}
呜呜呜~~~~~~~~终于可以复习了