24. 两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表
Head->1->2->3
思路:head先指向2,2指向1,1指向3
cur指向需要交换节点的前一个节点,当链表数量为偶数的时候,cur->next = NULL结束遍历;当链表数量为奇数的时候,cur->next->next = NULL结束遍历。(特殊情况:当链表为空时,也是cur->next为空,会结束遍历)
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution { //swap every two adjacent nodes
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead; //don't iterate head node
while(cur->next!=NULL&&cur->next->next!=NULL){
ListNode* temp = cur->next;
ListNode* temp1 = cur->next->next->next; //define the node 1 and node 3
cur->next = cur->next->next;//link cur to node 2
cur->next->next = temp; //link node 2 to node 1
temp->next = temp1; //link node 1 to node 3
cur = cur->next->next; //cur moved to node 2(previous the next two nodes needed to swap)
}
return dummyhead->next;
}
};
- 时间复杂度:O(n):因为要遍历每个节点
- 空间复杂度:O(1):链表的空间复杂度
19.删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
能够尝试一趟遍历实现吗?考虑快慢指针!
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* fast = dummyhead;
ListNode* slow = dummyhead;
while(n&&fast!=NULL){
fast = fast->next;
n--;
}
while(fast!=NULL&&fast->next!=NULL){ //遍历到最后一个节点(防止出错可以画图)
fast = fast->next;
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp; //C++释放内存的逻辑
return dummyhead->next;
}
};
- 时间复杂度: O(n)
- 空间复杂度: O(1)
面试题 02.07. 链表相交
思路:我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置。如图所示:
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
否则循环退出返回空指针。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int A = 0, B = 0;
while(curA!=NULL){
curA = curA->next;
A++;
}
while(curB!=NULL){
curB = curB->next;
B++;
} //得到A和B链表的长度
curA = headA;
curB = headB; //remember to initialize
if(B>A){
swap(A,B);
swap(curA, curB);
} //将长度更长的赋值到A
int gap = A-B;
while(gap--){ //从gap的位置开始遍历
curA=curA->next;
}
while(B){
if(curA == curB){
return curA; //返回交点
}
curA = curA->next;
curB = curB->next;
B--;
}
return NULL;
}
};
142.环形链表II
主要考察
- 该链表是否有环
- 如果有环,怎么判断该入口?
对于是否有环,可以使用快慢指针。从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
对于怎么判断该入口:
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
建立等式,fast指针走过的节点数 = slow指针走过的节点数 * 2,如下:
为了使得x与一个正数相关:
这就意味着,从头结点出发一个指针(index1),从相遇节点 也出发一个指针(index2),这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
n如果大于1和n为1的时候,效果是一样的。index1 指针在环里多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点
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 = head;
ListNode* index2 = fast;
while(index1!=index2){ //对应公式x = (n-1)(y+z)+z
index1=index1->next; //不管转几圈,只用考虑x和z相遇
index2=index2->next;
}
return index1;
}
}
return NULL;
}
};
- 时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
- 空间复杂度: O(1)