链表中倒数第k个结点
本题从1开始计数,即链表的尾结点是倒数第一个结点,例如链表从头结点依次是1,2,3,4,5,6,则倒数第3个结点是4
struct ListNode
{
int val;
ListNode* next;
};
利用快慢指针解题,先让一个指针走k-1步,然后两个指针同时走,当快指针走到链表尾部时,慢指针指向的位置就是倒数第k个结点所在位置
ListNode* FindKToTail(ListNode* pHead, unsigned int k)
{
if(pHead == NULL || k == 0)
return NULL;
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
for(unsigned int i = 0; i < k-1; i++)/*当k== 0时,此处循环次数增加好几倍,不符合*/
{
if(pFast->next != NULL)
pFast = pFast->next;
else /*结点数小于k*/
return NULL;
}
while(pFast->next != NULL)
{
pFast = pFast->next;
pSlow = pSlow->next;
}
return pSlow;
}
查找链表中间结点
思路:用快慢指针,块指针一次走两步,慢指针一次走一步。块指针到链表尾部时,慢指针刚好到中间结点。如果链表数为奇数,返回中间结点,如果为偶数,返回中间结点的任意一个。
ListNode* FindMiddleNode(ListNode* pHead)
{
if(pHead == NULL)
return NULL;
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
while(pFast != NULL && pFase->next != NULL)
{
pFast = pFast->next->next;
pSlow = pSlow->next;
}
return pSlow;
}
判断单链表是否有环
思路:用快慢指针,块指针一次走两步,慢指针一次走一步。如果走的块的指针追上走得慢的,说明有环,否则没有
bool isLoop(ListNode* pHead)
{
if(pHead == NULL)
return false;
ListNode* pFast = pHead;
ListNode* pSlow = pHead;
while(pFast != NULL && pFast->next != NULL)
{
pFast = pFast->next->next;
pSlow = pSlow->next;
if(pFast == pSlow)
break;
}
/* 如果无环,则pFast先走到终点,当链表长度为奇数时,pFast->next为空,当链表长度为偶数时,pFast为空*/
if(pFast == NULL || pFast->next == NULL)
<span style="white-space:pre"> </span>return false;
else
<span style="white-space:pre"> </span>return true;
}
求单链表环长
思路:在环上相遇后,记录第一次相遇点为Pos,之后指针slow继续每次走1步,fast每次走2步。在下次相遇的时候fast比slow正好又多走了一圈,也就是多走的距离等于环长。
设从第一次相遇到第二次相遇,设slow走了len步,则fast走了2*len步,相遇时多走了一圈:
环长=2*len-len。
int loopLength(ListNode* pHead)
{
if(!isLoop(pHead))
return 0;
ListNode* fast= pHead;
ListNode* slow = pHead;
int len = 0;
bool first = false; //标记第一次相遇
bool second = false; //标记第二圈相遇
while(fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
//超两圈后停止计数
if(fast == slow && second )
break;
//超一圈 开始计数
if(fast == slow && !second)
{
first = true;
second = true;
}
//计数
if(first)
++len;
}
return len;
}
求有环链表环结点位置
第一次碰撞点Pos到连接点Join的距离=头指针到连接点Join的距离,因此,分别从第一次碰撞点Pos、头指针head开始走,相遇的那个点就是连接点。
在环上相遇后,记录第一次相遇点为Pos,连接点为Join,假设头结点到连接点的长度为LenA,连接点到第一次相遇点的长度为x,环长为R。
第一次相遇时,slow走的长度 S = LenA + x;
第一次相遇时,fast走的长度 2S = LenA + n*R + x;
所以可以知道,LenA + x = n*R; LenA = n*R -x;
ListNode* findLoopEntrance(ListNode* pHead<span style="line-height: 20.16px; font-family: Verdana, Arial, Helvetica, sans-serif;">) </span>
{
ListNode* fast = pHead;
ListNode* slow = pHead;
while( fast != NULL && fast->next != NULL)
{
fast = fast->next->next;
slow = slow->next;
//如果有环,则fast会超过slow一圈
if(fast == slow) //跳出循环时处于第一次相遇位置
{
break;
}
}
if(fast == NULL || fast->next == NULL) //不是环
return NULL;
slow = pHead; //让慢指针指向头结点,两者下次相遇时就是环入口点位置
while(slow != fast)
{
slow = slow->next; //各走一步
fast = fast->next;
}
return slow;
}
扩展问题:
判断两个单链表是否相交,如果相交,给出相交的第一个点(两个链表都不存在环)。
比较好的方法有两个:
一、将其中一个链表首尾相连,检测另外一个链表是否存在环,如果存在,则两个链表相交,而检测出来的依赖环入口即为相交的第一个点。
二、如果两个链表相交,那个两个链表从相交点到链表结束都是相同的节点,我们可以先遍历一个链表,直到尾部,再遍历另外一个链表,如果也可以走到同样的结尾点,则两个链表相交。---推荐
这时我们记下两个链表length,再遍历一次,长链表节点先出发前进(lengthMax-lengthMin)步,之后两个链表同时前进,每次一步,相遇的第一点即为两个链表相交的第一个点。---找交点
int isCross(node *h1, node *h2)
{
node *p, *q;
for(p = h1; p->next!=NULL; p++);
for(q = h2; q->next!=NULL; q++);
return p == q ? 1 : 0;
}