24.两两交换链表中的节点
其实思路还蛮简单的,但我自己想是想不出的,会把自己绕晕。卡哥解决的关键在于引入虚拟节点,这样就可以保证每一次工作的循环条件是相同的。具体的连接方式就是按照图中的一样,把两个看成一组来解决,就是绕了一下然后把cur->2->1的线拉直,然后cur再往后移动两位,如图中的话就是下一次会移动到2。注意,结束的条件要分析链表为奇数个或者偶数个的情况,如果链表个数为奇数的话,奇数个的最后一个是不用交换的,那么停止条件就是cur->next->next==NULL,如果个数为偶数的话,那么停止条件就是cur->next==NULL,所以分析可以知道循环条件为cur->next!=NULL&&cur->next->next!=NULL,两个中但凡有一个是等于NULL,说明已经到了链表尾部了,不用再执行了。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode*dummyHead=new ListNode(0);
dummyHead->next=head;
ListNode*cur=dummyHead;
while(cur->next!=NULL&&cur->next->next!=NULL)
{
ListNode*temp=cur->next;
ListNode*temp1=cur->next->next->next;
cur->next=cur->next->next;
cur->next->next=temp;
temp->next=temp1;
cur=cur->next->next;
}
return dummyHead->next;
}
19.删除链表的倒数第n个节点
这道题有点需要开阔性思维,正常来讲我自己会想到先走完一遍知道链表的长度有多少,那么我就知道它所谓的倒数第n个节点应该在哪里。卡哥的思路非常巧妙,我们解决问题的关键在哪?就是因为不知道链表长度,所以才需要走完一遍再走到倒数第n个节点,那可不可以一遍就知道应该走到哪里呢?就需要用到数学的知识了,假设链表的长度是x,那么如果想要走到倒数第n个节点,需要走x-n步,那我们其实可以设置两个指针,一个快指针,一个慢指针,一个快的先走n步,那它到达最后一个节点就只需x-n步,那么慢指针跟着走x-n步,就到达了要删除节点的前面一个节点,这时候让slow->next=slow->next->next即可实现删除链表的倒数第n个节点,非常妙!
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode*dummyhead=new ListNode(0);
dummyhead->next=head;
ListNode*quick=dummyhead;
ListNode*slow=dummyhead;
while(n--)
{
quick=quick->next;
}
while(quick->next!=NULL)
{
quick=quick->next;
slow=slow->next;
}
ListNode*tmp=slow->next;
slow->next=tmp->next;
delete tmp;
return dummyhead->next;
}
面试题 02.07. 链表相交
这道题理解题意很重要,所谓的链表相交,相交后两个链表的内容其实都是同一块内容,所以题目要找的其实是找指向链表节点的一样的指针,只要找到指针内容相同的两个指针,那么他们后面的东西都会是一样的。
还需要注意的是,由于相交后相同的部分会一直蔓延到最后,所以我们其实可以从长度比较小的链表开始找(想像两根线是从后端对齐的,所以是从长度小的线开始找),那我们就需要先知道两个链表分别的长度,让指向长度长的链表的指针直接移动到和长度小的链表的头结点对齐的位置,然后两个指针一起移动,就可以找相交的节点了。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode*curA=headA;
ListNode*curB=headB;
int lenA=0;int lenB=0;
while(curA!=NULL)
{
lenA++;
curA=curA->next;
}
while(curB!=NULL)
{
lenB++;
curB=curB->next;
}
curA=headA;
curB=headB;
if(lenB>lenA)
{
swap(lenA,lenB);
swap(curA,curB);
}
int gap=lenA-lenB;
while(gap--)
{curA=curA->next;}
while(curA!=NULL)
{
if(curA==curB)
return curA;
curA=curA->next;
curB=curB->next;
}
return NULL;
}
};
142.环形链表
这是一道很可怕的题目,我觉得我自己做绝对不会做出来,居然还是要运用数学的推导,推导如下
推导的假设是令快指针的速度是慢指针的2倍(其实只要快的比慢的快就行),因为假设有环,那么快指针和慢指针一定会相遇(可以用物理的追击相遇思考),然后我们推导得出x肯定是z+环长度的整数倍,那么也就是说当找到快指针和慢指针相遇的那个点,此时再设立两个指针,一个指向head,一个指向相遇的那个点,两个指针同时移动,肯定会在环的入口处相遇。(因为x总会等于z+环长度的整数倍,所以从相遇那个点出发的指针肯定会和从头结点出发的指针同时到达入口的)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode*quick=head;
ListNode*slow=head;
while(quick!=NULL&&quick->next!=NULL)
{
quick=quick->next->next;
slow=slow->next;
if(quick==slow)
{
ListNode*index1=quick;
ListNode*index2=head;
while(index1!=index2)
{
index1=index1->next;
index2=index2->next;
}
return index1;
}
}
return NULL;
}
};