leetcode24:两两交换
文章讲解:leetcode24
leetcode19:删除倒数第N个节点
文章讲解:leetcode19
面试题02.07:链表相交
文章讲解:链表相交
leetcode142:环形链表2
文章讲解:环形链表2
目录
1,leetcode24 两两交换链表中的节点
2,删除链表倒数第N个节点
3,面试题02.07 链表相交:
4,环形链表2
今天这些题都没见过,看上去第一眼没有太好的思路,加上这两天比较忙,就直接看文章和视频,总结一下算法,下周忙完了再回来看看
这道题关键的地方在于理清楚应该是几个节点一组,每一组中间怎么动,cur指针怎么往下走三个问题。
1,从操作的角度看,两两交换,cur每次应该移动两个,相当于一组进行操作
2,怎么动和怎么往下走是一个问题。就是我该记录哪些节点,节点的指针怎么操作。
3,首先,cur指针应该是从虚拟头节点出发,每次都是在待操作的两个节点之前,这样才能保存住第一个待操作的节点的地址
4,到这里,思路已经清晰了,cur保存了第一个待操作的节点的去向,那只要保存着第二个待操作的节点的去向,也就是第一个节点(temp1),和这一组节点的下一个节点,也就是第三个(temp2),就记录了所有的指针,已经可以完整交换了
5,循环终止条件,不能简单的认为下一个是空就行,因为奇数个节点最后一个没办法操作,因此cur的下一个和下下个都为空才能进行移动。
6,画一下图,移动顺序就出来了
代码按顺序写出来即可:
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* cur = dummyhead;
while(cur->next!=nullptr&&cur->next->next!=nullptr){
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = temp1->next;
temp1->next->next = temp1;
temp1->next = temp2;
cur = cur->next->next;
}
return dummyhead->next;
}
};
核心是移动的思路,思路有了,代码自然好写。
这道题数据结构考过,还是有点印象,快慢指针,一个在前先走N步,一个在后,前面的走到头后面的就到了。
没通过:
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
看了半天,才反应过来,不能去指向第n个,slow应该去找第n-1个,才能删掉第n个,那么fast应该往前走n+1个就对了。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
n = n+1;
while(n-- && fast != NULL) {
fast = fast->next;
}
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
return dummyHead->next;
}
};
还差一点,去掉的节点的内存应该释放掉。很明显,释放的应该是slow的下一个节点的位置和dummyhead。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
n = n+1;
while(n-- && fast != NULL) {
fast = fast->next;
}
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
ListNode* ondelete = slow->next;
slow->next = slow->next->next;
delete ondelete;
ListNode* result = dummyHead->next;
delete dummyHead;
return result;
}
};
这样就完备了。
这道题我只有O(nm)的思路,挨个遍历比较地址是否相同。但显然应该有更好的的算法
确实,这道题关键的破题点在于,两个链表相交后的部分应该是一致的,这意味着,如果两个链表相交了,相交的节点一定在一致的几个中。而找一致的方法就是,链表的末尾对齐。
代码如下:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, 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;
}
};
和卡哥的代码是一致的。
链表找环这个思路我见过,就是一个指针一次走两步,一个指针一次走一步,如果相遇了,就说明有环,问题在于如何找到环的入口:
在这道题里,z+y是环的长度,相遇的时候,两个链表的数量关系是
slow:x+y
fast:x+n*(y+z)
他们相遇,即
2*slow = fast => x = (n - 1) (y + z) + z
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
这道题核心就在于这个数学证明。思路很简单,先一快一慢,相遇了,就两个指针同时前进,再相遇时就是环形节点的入口。
这个数学过程还得在理解一下
代码如下:
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2;
}
}
return NULL;
}
};
和卡哥的是一致的。
1,今天这几道题的思路很重要,交换方法,寻找方法,证明过程还需要再理解理解。