单链表中经常考的题型中的四种题型:
1.单链表逆置并打印,逆置又分为两种(1)就地逆置(2)头插法逆置
(1)就地逆置顾名思义就是在不改变单链表存储的基础上进行逆置,下面的图片就是就地逆置的思想,具体见代码
void Reverse(pList list)
{
assert(list != NULL);
struct Node *p = NULL;
struct Node *q = list->head.next;
if (q == NULL) // 只有一个头结点
{
return;
}
struct Node *m = q->next; //保存即将断开的结点
while (q != NULL)
{
q->next = p;
p = q; //记录被逆置后的最后一个结点
if (m == NULL)
{
break;
}
q = m;
m = q->next;
}
list->head.next = p;
}
(2)头插法逆置,思想如图,具体内容见代码
void Reverse_Linklist(Linklist *head)
{
Linklist *q, *p;
p = head->next; //依次取链表中的结点然后采用头插的方法,该语句是想记录首结点
head->next = NULL;//让头指针指空然后再首插
q = p;
while(q != NULL)
{
p = p->next ;
q->next = head->next ; //把q置空
head->next = q;
q = p;
}
}
2.找出倒数第K个结点,有两种方法,第一种是找倒数第Count(链表长)- K个结点,一个是用两个指针p、q,第一个指针p从首结点跑K个结点之后,另一个指针q开始从首结点和p指针同时跑,当p指针跑到它指向结点的指针域为空的时候,那么此时的q针指向的结点就是倒数第K个结点。第二种就是,找第count-k+1的结点,在这里我们只提供第一种方法的代码,代码如下:
int FindKAfter(pList list, int k, ElemType *res)
{
assert(list != NULL);
if (k <= 0 || k > list->count)
{
return 0;
}
struct Node *p = list->head.next;
struct Node *q = p;
while (k > 0)
{
k--;
p = p->next;
}
while (p != NULL)
{
p = p->next;
q = q->next;
}
*res = q->data;
return 1;
}
3.判断链表是否有环并找出入环点
(1)是否有环:判断是否有环的时候使用快慢指针,假设q是快指针(一次跑两个结点),p是慢指针(一次跑一个结点),当两个结点相遇的时候就能判断出该链表是有环的,当然两个指针一直能跑下去的条件是快指针指向结点的指针域不为空,此时就已经判断出有环了。
(2)计算链表中环的长度,此时定义的指针i,j每次都指跑一个结点,不使用快慢指针,第(1)步的时候p、q已经在环内,所以让i=p,j=p->next,并且定义一个变量count记录走过的结点个数,当且仅当i==j的时候,环走完且计算出了环的长度count。
(3)判断入环点,此时我们利用(1)(2)求出的环的长度,让p、q都指向首结点,假设链表总长度为m,然后先让p走环的长度count,那么此时p没走的长度就是m-count,q到入环点的长度也是m-count,现在让p、q(还在首结点)同时走,两个结点最后相等的时候就是入环点(当两个指针走了相同长度的时候如果相等了那么就是入环点,该处可以和第四种类型两个链表相交做对比考虑把入环点也用Y型解决),转换成Y型图片如下:
代码如下:
#include "list.h"
Node* FindFirstNode(PList list)
{
// 判断是否有环
Node *p = list->head.next;
Node *q = p;
while (q != NULL &&
q->next != NULL)
{
p = p->next;
q = q->next->next;
if (p == q)
{
break;
}
}
// 判断上面代码从那块退出的
if (q == NULL || q->next == NULL)
{
return NULL;
}
// 求链表中环的长度
int clen = 1; // n
while (p->next != q)
{
clen++;
p = p->next;
}
// p指针先走clen个结点
p = q = list->head.next;
while (clen > 0)
{
p = p->next;
clen--;
}
// 两个指针同步朝后走,相遇的结点就是入环的第一个结点
while (p != q)
{
p = p->next;
q = q->next;
}
return p;
}
4.判断两个链表是否相交,如果两个链表中有一个链表的最后一个指针指向了NULL,那么两个链表肯定不相交,解题步骤如下:
(1)先计算两个链表的长度,并且相减,得出的值为k,
(2)让p指向较长链表的指针先走K个结点,此时p指向的这个链表剩余长度就和q指向较短链表的长度是一样的,走完一样长度的指针,指向的结点如果相等的话那么两个链表肯定是相交的。(Y型结构)
int TwoListMeet(pList list1, pList list2)
{
assert(list1 != NULL && list2 != NULL);
struct Node *p = list1->head.next;
struct Node *q = list2->head.next;
if (list1->count > list2->count)
{
for (int i = 0; i < list1->count - list2->count; ++i)
{
p = p->next;
}
}
else
{
for (int i = 0; i < list2->count - list1->count; ++i)
{
q = q->next;
}
}
while (p != NULL && p != q)
{
p = p->next;
q = q->next;
}
if (p == NULL) //对上面退出循环的条件进行逐一判断,到底是因为p==NULL退出,还是p==q退出
{
return 0; //不相交
}
return 1; //相交
}