链表常见操作:判断是否有环
链表中很常见的一问题是,是否有环?如下图:
怎么办呢?正常链表的尾节点的链域是NULL,有环就不存在NULL了!对了,用一指针轮询,不断地 p=p->next; 若是看到了p为NULL,则表明无环!否则,就是有环。这个想法挺好,但是有环,会进入死循环的。有人说,那就设置一时间点,过了时间还没结束,就是有环。我想说,那万一链表真的很长呢,时间点设置多少才够了?……
办法是有的:使用两指针,一快一慢,都从头开始轮询,若有环,则慢的肯定可以被快的反超,因为此时大家都像是在围绕着环形跑道赛跑;若是一正常链表,则肯定会遇到NULL,好了,问题解决了。
该判断函数可以这样写:
bool hasLoop(Node *head)
{
if (head == NULL) //链表为空
return false;
Node *p,*q;
p = q = head; //两个指针从同一点出发,当然也可以一前一后
while (p && q)
{
p = p->next; //一次一步
q = q->next->next; //一次两步
if (p == q) //相遇,肯定有环
return true;
}
return false; //出现了NULL,也就无环
}
一测试用例;
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}Node;
bool hasLoop(Node *head)
{
if (head == NULL) //链表为空
return false;
Node *p,*q;
p = q = head; //两个指针从同一点出发,当然也可以一前一后
while (p && q)
{
p = p->next; //一次一步
q = q->next->next; //一次两步
if (p == q) //相遇,肯定有环
return true;
}
return false; //出现了NULL,也就无环
}
int main()
{
//构建一有环链表
Node *head1 = NULL;
Node p1, p2, p3;
p1 = { 1, &p2 };
p2 = { 2, &p3 };
p3 = { 3, &p1 };
head1 = &p1;
printf("链表一 ");
if (hasLoop(head1))
printf("有环!\n");
else
printf("无环!\n");
//构建一正常链表(无环)
Node *head2 = NULL;
Node p4, p5, p6;
p4 = { 4, &p5 };
p5 = { 5, &p6 };
p6 = { 6, NULL };
printf("链表二 ");
if (hasLoop(head2))
printf("有环!\n");
else
printf("无环!\n");
system("pause");
return 0;
}
链表一是这样的:head1->1->2->3->1 是有环的
链表二是这样的:head2->4->5->6->NULL 正常链表,无环
运行:
链表常见操作:输出倒数第k个节点(最后一个节点是倒数第一个)
看着很棘手,怎么才能数到倒数第k个?首先,链表长度不知,又是单向的。怎么办?
方法一:先求出链表长度,这个大家肯定会,只要 p!=NULL 就不断地 p=p->next; 移动一次,就记数 count++; 然后的事情大家都知道,从头再轮询 n-k 次就行了。
方法二:有比方法一,更好的方法吗?有的!还是两指针,一指针先从头移动k次,此时两指针间隔k(就是要这个差距),此时两指针同时移动,快慢一样,等到先走的指针移动到NULL,第二个指针不就是倒数第k个吗?对的!
方法二比方法一高效,下面就只给出方法二的代码:
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}Node;
void endOfK(Node *head, int k)
{
if (head == NULL)
{
printf("链表为空!");
return;
}
Node *p, *q;
p = q = head;
int count = 0;
while (p && count < k)
{
p = p->next;
count++;
}
if (p == NULL && count!=k) //update : 这里原先只有 p==NULL ,更新后加上 count!=k,大家想想为什么?
{
printf("k值过大!\n");
return;
}
while (p)
{
p = p->next;
q = q->next;
}
printf("倒数第%d个节点是 %d\n",k,q->data);
}
void clear(Node *head)
{
Node *q,*p = head;
while (p)
{
q = p->next;
free(p);
p = q;;
}
}
int main() //32
{
Node *p=NULL,*head = NULL;
int data;
printf("输入链表中的元素,以0结束!\n");
while (scanf_s("%d", &data) && data)
{
if (head == NULL)
{
p = (Node*)malloc(sizeof(Node));
head = p;
}
else
{
p->next = (Node*)malloc(sizeof(Node));
p = p->next;
}
p->data = data;
p->next = NULL;
}
int k;
printf("输入倒数第几个 ");
scanf_s("%d",&k);
endOfK(head, k);
clear(head);
system("pause");
return 0;
}
运行:
大家可以试着写写方法一的代码。多尝试,才会有进步!我写了一个在一楼,欢迎不吝赐教!
链表常见操作:是否相交
两链表相交情形如下:
看到图:其实思路就很明显了,一指针沿着其中一链表走到尾节点处等着,另一链表的指针也从头走到尾,若能在尾处相遇,则两链表在某节点处相交。逻辑比较简单,直接看代码。
测试代码如下:
#include<stdio.h>
#include<stdlib.h>
typedef struct node
{
int data;
struct node *next;
}Node;
bool isCross(Node *head1, Node *head2) //判断是否相交的函数
{
if (!head1 || !head2) //有一个为空,则不相交
return false;
Node *p = head1;
while (p->next)
p = p->next;
Node *q = head2;
while (q->next)
q = q->next;
return p == q;
}
int main() //33
{
Node *head1,*head2,*head3;
head1 = head2 = head3 = NULL;
Node p1, p2, p3, p4, p5, p6, p7, p8;
p1 = { 1, &p2 };
p2 = { 2, &p3 };
p3 = { 3, &p4 };
p4 = { 4, NULL };
p5 = { 5, &p6 };
p6 = { 6, &p3 };
p7 = { 7, &p8 };
p8 = { 8, NULL };
head1 = &p1;
head2 = &p5;
head3 = &p7;
printf("head1与head2相交与否? ");
isCross(head1, head2) ? printf("相交!\n") : printf("不相交!\n");
printf("head1与head3相交与否? ");
isCross(head1, head3) ? printf("相交!\n") : printf("不相交!\n");
system("pause");
return 0;
}
构建了三个链表:
head1->1->2->3->4->NULL
head2->5->6->3->4->NULL
head3->7->8->NULL
显然,head1和head2是相交的,head3与它们俩都是不相交的。
运行:
转载请注明出处,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/26838009
若是对你有所帮助,或是觉得有意思,希望顶一个哦。
专栏目录: