在c语言中,链表是一个重要的板块,也是一个较难的板块,这里我这下我平时写链表题目的一些小心得。
1:链表的是由一个一个节点相连而成,每一个节点都由数据域以及指针域组成,这里有个重要的地方是指针域应该是指向自己的一个指针变量,如下:
struct node{
datatype data;
struct node *next;
}*Node;
此处的datatype指的是数据的类型,可以是int,float之类的,也可以是自定义的机构体类型。
2:在最开始的时候,需要为链表的头结点开辟一个空间,如下:
struct Node head,
head=(node*)malloc(sizeof(node));
head->next=NULL;
这样就开辟了一个头结点,这个头结点没有数据,只是用来指向链表而已,方便后续对链表操作而已。
3:之后一个需要解决的操作是为链表添加后续指针,这里就需要开辟一个新的空间,并且没添加一次就需要开辟一次,如下:
struct Node p,s;
s=head;
p=(node*)malloc(sizeof(node));
scanf(“%d”,p->data);
s->next=p;
s=p;
这里用的尾插法,需要注意的是,当你不想继续输入数据或者已经跳出循环后,要将s->next设为NULL,否则这个链表就出错啦(我之前也在这犯过错)
之后就是输出了,可以用while循环,操作如下:
struct node e;
e=head->next;//此处因为head没有数据,所
//以指向next
while(e!=NULL)
{
printf(“%d”,e->data);
e=e->next;
}
这样就可以啦,一个新鲜的链表就出来了
但是你以为能够熟练创建一个链表就已经结束了吗?nonono,还有好多难题呢,这里有一个问题:如何查看两个单链表是否相交,并且返回相交节点?
这个时候大家估计都在想,两个链表相交还不简单?直接找到最末端的地方,看地址相不相同不就行了吗?确实,这是一般的思路,但是,要是这两个链表都成环了呢?你怎么知道他们相交了呢?
想迷糊了吧。
其实也很简单。
首先,这问题分三种情况讨论:
1)两个链表都没成环,那么就直接比较最末端的地址就是了,相同则相交
2)一个成环,一个未成环,这种情况是绝对不会相交的,大家可以自己想想为什么。
3)一种是两个都成环了,而这里又分相交与没相交两种情况,而相交又分两种情况,是在入环前相交还是在入环后相交的?
把这三种情况考虑到了后再去做题。
首先是如何查看链表是否成环:
这就要利用快慢指针了,让快慢指针依次遍历这个链表,若是会相遇,则一定成环,然后让慢指针继续遍历,快指针从头重新遍历,但是快指针的速度和慢指针一样,它们之后一定会在入环节点相遇,然后返回这个入环节点就是了,思路很简单,代码也是:
struct Node findloop(struct Node head)
{
if(head==NULL)
return NULL;
if(head->next==head)
return head;
struct Node fast,slow;
fast=head->next;
slow=head;
int flag=1;//flag=1则表示为成环
while(fast!=NULL&&fast->next!=NULL)
{
fast=fast->next;
slow=slow->next;
if(slow==fast)
{
flag=0;
break;//相等就跳出循环
}
}
if(flag==0)
{
fast=head;
while(slow!=fast)
{
slow=slow->next;
fast=fast->next;
}
return fast;//返回成环结点
}
else
return false;
}
这里是我借鉴了其他大佬的代码写出来的,大家可以画图来自己思考为什么这样写。
这里就找到了入环结点,当两个链表都返回了一个结点说明都成环,一个为NULL一个有结点就可以直接说明未相交了,而都为NULL就说明都不成环。
接着我们来检测未成环的链表是否相交
检测代码如下:
struct Node fun(struct Node head1,struct Node head2)
{
struct Node s1,s2;
s1=head1->next;
s2=head2->next;
while(s1->next!=NULL)
s1=s1->next;
while(s2->next!=NULL)
s2=s2->next;
if(s1==s2)
return 1;
else
return 0;
}
若是检测出有相同,就要去寻找相交结点在哪儿,代码如下:
struct Node fun(struct Node head1,struct Node head2)
{
struct Node s1,s2;
s1=head1->next;
s2=head2->next;
int n=0;
while(s1!=NULL)
{
s1=s1->next;
n++;
}
while(s2!=NULL)
{
s2=s2->next;
n--;
}
s1=n>0?head1:head2;//让s1指向长的链表。
s2=(s1==head1)?head2:head1;//s2指向s1之外的链表
n=fabs(n);
while(n!=0)
{
s1=s1->next;
n--;
}
while(s1!=s2)
{
//两个结点相等了就跳出循环
s1=s1->next;
s2=s2->next;
}
return s1;
}
解决完未成环的链表相交问题后,就得解决成环链表相交问题了。
首先需要判断成环链表是否相交,利用上面的求入环结点的函数,判断入环结点是否相同,相同则是在成环之前就相交了,若是成环之前就相交,就将入环结点看成是NULL来找相交结点
代码如下:
struct Node find1(struct Node head1,struct Node head2,struct Node loop)
{
struct Node s1,s2;
s1=head1->next;
s2=head2->next;
int n=0;
while(s1!=loop)
{
s1=s1->next;
n++;
}
while(s2!=loop)
{
s2=s2->next;
n--;
}
s1=n>0?head1:head2;
s2=(s1==head1)?head2:head1;
n=fabs(n);
while(n!=0)
{
n--;
s1=s1->next;
}
while(s1!=s2)
{
s1=s1->next;
s2=s2->next;
}
return s1;
}
之后就是另一种情况,那就是成环后相交;
这种情况需要和成环不相交进行区别,所以需要将两个链表的入环结点来判断,使得一个入环结点不动,另一个移动,若是相遇到了,则是成环后相交,若是转了一圈还是没找到,那就是成环后不相交;
代码如下:
struct Node (struct Node head1,struct Node head2,struct Node loop1,struct Node loop2)
{
struct Node s1,s2,s3;
s1=loop1;
s2=loop2;
s3=loop1->next;
while(s3!=s1)
{
s3=s3->next;
if(s3==s2)
return s2;//若是s3在循环路上遇到了s2说明相交,返回一个就行
}
return NULL;
}
以上就是链表相交问题的大概思路了,新人还不是很熟练,希望大佬们多多指点。