LeetCode 24.两两交换链表中的节点
首先本题需要使用虚拟头节点,因为交换两个节点的操作时,cur指针一定要指向这两个节点的前一个节点,所以在完成一次交换之后,cur指针应该指向当前交换完成的两个节点中的后一个节点。其次因为需要断开两个节点,所以需要用两个temp指针来保存,具体需要保存的节点经过模拟发现一个时交换的前一个结点和后一个结点的next结点。
下面给出代码:
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* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = cur->next->next;
cur->next->next = temp1;
temp1->next = temp2;
cur = cur->next->next;
}
return dummyhead->next;
}
};
需要注意的是:while条件中注意判断顺序会导致空指针异常,需要先判断next再判断next的next。
LeetCode 19. 删除链表的倒数第 N 个结点
- 最暴力的方法就是,先反转,然后删除第n个结点,再反转即可。
- 第二种方法就是首先找到链表的长度len,然后删除第
len - n
个结点即可 - 第三种方法是卡哥的方法,使用双指针法,首先让
fast
先走n个结点,然后让快慢指针同时移动,当fast
指向NULL
时,就可以找到需要删除的结点。下面给出代码:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* fast = dummyhead;
ListNode* slow = dummyhead;
n = n + 1;
while(n--){
fast = fast->next;
}
while(fast != NULL){
fast = fast->next;
slow = slow->next;
}
ListNode* temp = slow->next;
slow->next = slow->next->next;
delete temp;
return dummyhead->next;
}
};
代码在处理细节的时候会有不同,不过只要是删除结点,使用虚拟头节点即可,最后让slow
指针指向需要删除的结点的前一个结点。
面试题 02.07. 链表相交
本题还是使用双指针法,首先算出两个链表的长度,然后再进行指针移动。需要注意的是:
- 只要两个指针指向的结点
cura == curb
就代表后续所有的结点都相同 - 需要找到较长的那个链表,并让其先移动两个链表长度差值的长度
下面给出代码:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int sizea = 0;
int sizeb = 0;
ListNode* cura = headA;
ListNode* curb = headB;
while(cura != NULL){
sizea++;
cura = cura->next;
}
while(curb != NULL){
sizeb++;
curb = curb->next;
}
cura = headA;
curb = headB;
if(sizeb > sizea){
swap(sizea,sizeb);
swap(cura,curb);
}
int cnt = sizea -sizeb;
while(cnt--){
cura = cura->next;
}
while(cura != NULL){
if(cura == curb){
return cura;
}
cura = cura->next;
curb = curb->next;
}
return NULL;
}
};
注意这里直接使用swap()
交换两个变量内存地址。
142.环形链表II
看过卡哥的视频后,此题分为两部分:
- 判断链表是否为环形链表:使用双指针(快慢指针),快指针每次移动两格,满指针每次移动一格,如果为环形,那么快指针会在慢指针在环中未转完一圈的时候与慢指针相遇。
- 找到环的入口:
这里直接上卡哥的图!其实不难理解,只要自己模拟一遍就行。
总的处理逻辑就是,首先判断是否为环,如果为环,使两个指针分别在相遇点和head
位置同时移动到相遇就是环的入口,直接给出代码:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL){
fast = fast->next->next;
slow = slow->next;
if(fast == slow){
ListNode* index1 = fast;
ListNode* index2 = head;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return NULL;
}
};
这里外层while循环时也是要注意首先需要判断fast
是否为NULL
再判断他的next。
在AC完后查看提交记录,发现自己之前用hashmap AC过一次,自己都忘了,代码如下:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return NULL;
}
};
回忆了一下,就是遍历链表,遍历的时候将结点一个个存入hashmap中,如果遍历结束的话那么就直接返回NULL
;如果在遍历过程中发现已经存入,就直接返回这个结点,这里使用unordered_set
中的count()
函数来查询。