PTA—— 两个有序链表序列的交集C]
题目:
已知两个非降序链表序列S1与S2,设计函数构造出S1与S2的交集新链表S3。
输入格式:
输入分两行,分别在每行给出由若干个正整数构成的非降序序列,用−1表示序列的结尾(−1不属于这个序列)。数字用空格间隔。
输出格式:
在一行中输出两个输入序列的交集序列,数字间用空格分开,结尾不能有多余空格;若新链表为空,输出NULL。
输入样例:
1 2 5 -1
2 4 5 8 10 -1
输出样例:
2 5
这道题看着简单,做起来有好多坑啊(呜呜呜)
先说说我的思路吧
首先,用一个结构体存储第一个序列每个节点信息,1).数值 2).cnt 3).指向下一结点的指针。cnt表示数组出现的次数。
struct node{
int num;
int cnt;
struct node* next;
};
typedef struct node LISTNODE;
typedef LISTNODE* LISTPTR;
接下来就是读取第一个序列中的元素创建链表了,那么出现了重复的元素呢?跳过还是cnt++呢。这个问题暂时不说,下面随着分析就知道了。
对于第二个序列,刚开始我用一个insert函数对每一个读入的x,如果是第一个序列的元素的话,对应元素cnt++,否则把x插入链表。但是这样做最后一组数据运行超时。想想如果x不是第一个序列中的元素的话直接跳过就?干嘛还要连接在原来的序列上?
所以我就改了insert函数,确实不超时了,但是!但是!答案错误!
这就引出了这道题又一处特别坑的地方:当一个数在两个序列中都不止一次出现时,举个例子,第一个序列含有1、1,第二个序列也含有1、1,那么对应交集元素是1、1而不是1。啊,原来是这样。原来和数学中的集合元素互异性矛盾了呀。做到这回到题目,题目说的是非降序序列,貌似不是非降序集合啊。好吧,只能承认,这是自己的问题。
所以到这,所有问题都有答案了。cnt只有1、2取值。1表示只在第一个序列出现过,2表示在第两个序列都出现过。建第一个链表是把每个值的cnt置为1(出现相同元素先不管就直接连在后面)。max是记录序列最后一个书也就是最大的那个数。这个在后面的主函数中可以看到,对运行时间还是有一定帮助的。
代码就是这样的
LISTPTR creat(int *max){
int x;
LISTPTR head=NULL,p=NULL,pre=NULL,last=NULL;
head=(LISTPTR)malloc(sizeof(LISTNODE));head->num=0;head->cnt=0;
pre=head;last=head;
scanf("%d",&x);
while(x!=-1){
p=(LISTPTR)malloc(sizeof(LISTNODE));
p->num=x; p->cnt=1;pre->next=p;pre=pre->next;last=p;
scanf("%d",&x);
}
last->next=NULL;*max=last->num;
return head;
}
建好以后就可以对第二个序列每一个x查找插入了。
LISTPTR insert(LISTPTR head,int x){
LISTPTR pre=head,p=head->next;
while(p!=NULL&&(x>p->num||(x==p->num&&p->cnt==2))){//注意22 和22的交集是22不是2啊啊啊啊啊
pre=pre->next;p=p->next;
}
if(p!=NULL){
if(x==p->num) p->cnt++;
}
return head;
}
下面是完整代码
#include<stdio.h>
#include<stdlib.h>
struct node{
int num;
int cnt;
struct node* next;
};
typedef struct node LISTNODE;
typedef LISTNODE* LISTPTR;
LISTPTR creat(int *max);//创建带头结点的链表
LISTPTR insert(LISTPTR head,int x);
void print_intersection(LISTPTR head);//输出交集
void destroylist(LISTPTR head);//释放
int main(){
LISTPTR head;
int x,max;
head=creat(&max);
scanf("%d",&x);
while(x!=-1){
if(x>max) break;//还有这里也是 减少运行时间的一步 第二个序列大于max的部分直接咔掉
head=insert(head,x);
scanf("%d",&x);
}
print_intersection(head->next);
destroylist(head);
return 0;
}
LISTPTR creat(int *max){
int x;
LISTPTR head=NULL,p=NULL,pre=NULL,last=NULL;
head=(LISTPTR)malloc(sizeof(LISTNODE));head->num=0;head->cnt=0;
pre=head;last=head;
scanf("%d",&x);
while(x!=-1){
p=(LISTPTR)malloc(sizeof(LISTNODE));
p->num=x; p->cnt=1;pre->next=p;pre=pre->next;last=p;
scanf("%d",&x);
}
last->next=NULL;*max=last->num;
return head;
}
LISTPTR insert(LISTPTR head,int x){
LISTPTR pre=head,p=head->next;
while(p!=NULL&&(x>p->num||(x==p->num&&p->cnt==2))){//注意22 和22的交集是22不是2啊啊啊啊啊
pre=pre->next;p=p->next;
}
if(p!=NULL){
if(x==p->num) p->cnt++;
}
return head;
}
void print_intersection(LISTPTR head){//交集重新组一条链
LISTPTR headPtr=NULL,ptr=head,lastPtr=NULL;//headPtr、lastPtr是交集链表的头、尾
while(ptr!=NULL){
if(ptr->cnt>1){
if(headPtr==NULL){//当前的Ptr指向交集第一个元素
headPtr=ptr;lastPtr=ptr;
}
else{
lastPtr->next=ptr;
lastPtr=lastPtr->next;
}
}
ptr=ptr->next;
}
if(headPtr!=NULL)lastPtr->next=NULL;//直接=NULL会出现段错误,有可能交集是空集
if(headPtr==NULL) printf("NULL");
else{
while(headPtr->next!=NULL){
printf("%d ",headPtr->num);
headPtr=headPtr->next;
}
printf("%d",headPtr->num);
}
}
void destroylist(LISTPTR head){
LISTPTR tempPtr;
while(head!=NULL){
tempPtr=head;
head=head->next;
tempPtr->next=NULL;
free(tempPtr);
}
}