两两交换链表中的结点
初始状态
本题需要注意的是交换结点时,应该用一个临时指针变量temp保存当前需要更改指针的next结点,防止断链。对应代码:
temp1 = pre->next;
temp2 = pre->next->next;
pre->next = temp2;
temp1->next = temp2->next;
temp2->next = temp1;
最后变成,然后将pre指针指向temp1即可开始新的一轮交换。
注意交换停止条件是:pre->next&&pre->next->next 当有偶数个结点时,pre->next不为NULL,当有奇数个结点时,pre->next->next不为NULL。
完整代码:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead =new ListNode(0,head);
ListNode* pre = dummyhead;
ListNode* temp1 = NULL;
ListNode* temp2 = NULL;
while (pre->next&&pre->next->next!=NULL)//四种形式pre->next pre->next!=0 pre->next!=NULL pre->next!=nullptr
{
temp1 = pre->next;
temp2 = pre->next->next;
pre->next = temp2;
temp1->next = temp2->next;
temp2->next = temp1;
pre = temp1;
}
head = dummyhead->next;
delete(dummyhead);
return head;
}
};
删除链表倒数第N个结点
通过双指针实现,先将快指针指向第N+1个结点(注意不是N个结点,因为我们要删除一个结点,需要将慢指针指向删除结点的前一个结点),然后慢指针指向虚拟头结点dummyhead,然后同时开始遍历,当快指针遍历到链表末尾,即fastptr==NULL时,慢指针也停止遍历,此时慢指针指向待删除结点的前一个结点。然后删除即可。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0, head);
ListNode* fastptr = dummyhead;
ListNode* slowprt = dummyhead;
ListNode* temp = NULL;
n++;//为了方便找到倒数第N个结点的前一个结点,将N+1
while (n--&&fastptr)
{
fastptr = fastptr->next;
}
while (fastptr)
{
fastptr = fastptr->next;
slowprt = slowprt->next;
}
temp = slowprt->next;
slowprt->next = temp->next;
delete(temp);
head = dummyhead->next;
delete(dummyhead);
return head;
}
};
链表相交
当两个链表相交时,后面的所有结点位置都相同,因此我们只需要判断两个链表是否有相同的结点(且只需要判断第一个),那么我们如何进行判断呢,这道题我们是通过双指针实现的,需要同时向后遍历判断是否相同,由于两个链表不相交的部分长度不同,因此不能直接进行遍历判断。需要先将长度更长链表的遍历指针指向与短链表相同的对应位置。然后两个指针index1和inedx2同时向后遍历,当两个指针相同时即相交,如果index遍历到结束都相等条件都没有成立,说明没有相同结点,返回空指针(返回index1也行,因为遍历完后index指向NULL)。
完整代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int lenA = 0;
int lenB = 0;
int len = 0;
ListNode* ptrA = headA;
ListNode* ptrB = headB;
while (ptrA)
{
ptrA = ptrA->next;
lenA++;
}
while (ptrB)
{
ptrB = ptrB->next;
lenB++;
}
ptrA = headA;
ptrB = headB;
if (lenA < lenB)
{
swap(ptrA,ptrB);
swap(lenA, lenB);
}
len = lenA - lenB;
while (len--)
{
ptrA = ptrA->next;
}
while (ptrA)
{
if (ptrA == ptrB) return ptrA;
ptrA = ptrA->next;
ptrB = ptrB->next;
}
return NULL;
}
};
环形链表II
通过双指针实现,设置一个快指针和一个慢指针,快指针每次走两步,慢指针每次走一步(与田径跑道长跑比赛类似),当链表存在环时,快指针比慢指针多跑1圈(一圈的情况是慢指针进入环后快指针还未跑完一圈环长度)或N圈(N圈的情况是慢指针进入环之前快指针已经跑了1圈或者多圈环长度)。然后我们证明若存在环,必定存在fastptr == slowptr的情况。由于快指针每次比慢指针多走一步,那么必然不可能直接在某一次循环直接越过慢指针的位置,即必定在某一次循环相遇。
在判断存在环之后,我们还要返回环的入口位置。假设环之前的长度为x,环的入口到第一次相遇的长度为y,环的剩余长度为z,那么环的长度就是y+z,当第一次相遇,慢指针走了x+y步,快指针走了x+y+n*(z+y)步,即多走了n圈,又由于快指针移动长度为慢指针的2倍,因此
2*(x+y)=x+y+n*(z+y)
通过移项可得到x+y=n*(z+y),又由于快慢指针当前位置退后y步就是环的入口,将y移到右边。得到x=n*(z+y)-y,我们将慢指针指向链表开头,然后快慢指针同时移动x步,(但我们并不知道x的值)由于该等式,我们可以推出当两个指针相遇的时候即慢指针走了x步,因为快指针走n*(z+y)-y步相当于当前位置退后y步,也刚好在环的入口处。此时就拿到了环的入口位置。
完整代码:
class Solution {
public:
ListNode* detectCycle(ListNode* head) {
int pos = 0;
ListNode* slowptr = head;
ListNode* fastptr = head;
while (fastptr && fastptr->next) {//不能在循环条件这里判断fastptr!=slowptr,因为第一次进入循环都指向第一个结点
fastptr = fastptr->next->next;
slowptr = slowptr->next;
if (fastptr == slowptr)
{
slowptr = head;
while (slowptr != fastptr)
{
slowptr = slowptr->next;
fastptr = fastptr->next;
pos++;
}
cout << pos;
return fastptr;
}
}
pos = -1;
return NULL;
}
};
也可以直接在快指针遍历条件中判断是否相遇:fastptr && fastptr->next&&(fastptr=fastptr->next->next)!=(slowptr=slowptr->next)。当while循环退出时,如果是第一种情况,则是不存在环,如果是第二种情况,则是存在环并且相遇退出。完整代码如下:
class Solution1 {
public:
ListNode* detectCycle(ListNode* head) {
int pos = 0;
ListNode* slowptr = head;
ListNode* fastptr = head;
while (fastptr && fastptr->next && (fastptr = fastptr->next->next) != (slowptr = slowptr->next))
{
//快指针走两步,慢指针走一步,可以保证有环情况下快指针比慢指针多走n圈且不直接越过慢指针(因为每次多走一步)
}//当循环退出时,分两种情况,一种是无环,即fastptr && fastptr->next不成立,
//一种是有环,即(fastptr = fastptr->next->next) != (slowptr = slowptr->next)不成立
if (fastptr == NULL || fastptr->next == NULL) {
pos = -1;
return NULL;
}//无环的情况,快指针遍历到NULL
else
{
slowptr = head;
while (slowptr != fastptr)
{
slowptr = slowptr->next;
fastptr = fastptr->next;
pos++;
}
cout << pos;
return fastptr;
}//有环的情况,令慢指针指向头结点,快指针指向相交的地方由于x=n*(y+z)-y,
//当快指针走了n*(y+z)-y,即当前位置退步y步,慢指针走了x步,两指针恰好在环的入口处相遇
}
};