【算法】代码随想录训练营Day4打卡,24. 两两交换链表中的节点 19.删除链表的倒数第N个节点 面试题 02.07. 链表相交 142.环形链表II
链表
代码随想录算法训练营Day4任务。
两两交换链表中的节点
力扣链接:https://leetcode.cn/problems/swap-nodes-in-pairs/submissions/
这一道题的意思就是交换两个相邻的节点,并且以两个节点为一组;
这样我们就按照题例来思考,这道题要求我们返回一个链表,那么我们最好的做法就是创建一条新的链表,所以我们首先要穿件一个新的头部节点 newHead;
其次我们需要两个指针,一个cur指向我们每一次要操作的节点组(从头节点开始两个节点为一组)的第一个节点
这时候我们就要思考了,只有每一组有两个节点我们才会进行反转操作,如果只有一个节点直接将这个节点拼到新链表之后就可以
那么接下来我们就通过两个指针进行反转操作,实际细节如下图
C语言版本
虚拟头节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* swapPairs(struct ListNode* head){
struct ListNode newHead = {0,NULL};
struct ListNode* cur = head;
struct ListNode* pre = &newHead;
while(cur){
if(cur->next){ //说明有一对要交换的
pre->next = cur->next;
struct ListNode* node = pre->next;
pre = cur;
cur=cur->next->next;
node->next = pre;
pre->next = NULL;
}else{
pre->next = cur;
cur= cur->next;
}
}
return newHead.next;
}
JS版本
方法1
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var swapPairs = function(head) {
let newHead = new ListNode();
let cur = head;
let pre = newHead;
while(cur){
if(cur.next){ //说明有一对要交换的
pre.next = cur.next;
let node = pre.next
pre = cur;
cur=cur.next.next;
node.next = pre;
pre.next = null;
}else{
pre.next = cur;
cur= cur.next;
}
}
return newHead.next;
};
19.删除链表的倒数第N个节点
题目链接:https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
这道题的要求是删除链表的倒数第n个节点,会传入一个数值n
这道题的话会有一点反向思路的感觉,其实实际上我们只需要两个指针,一个快指针,一个慢指针,快指针先走n步,之后快指针再和慢指针同步走,当快指针走到链表尽头时,慢指针所指向的节点就是倒数第n个节点
我们先画一个图理解一下
这里我们找到了倒数第n个节点了,但是注意题目,我们要的是删除倒数第n个节点
既然是删除节点,那么我们要找到的是这个节点的前一个节点才能删除,
所以我们只需要小小的修改一下就行了,我们要让slot少走1步,那么我们就让fast多走一步就可以了
如图
这时候我们只需要把slot.next 指向 slot.next.next就可以了
C语言版本
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeNthFromEnd(struct ListNode* head, int n){
struct ListNode* fast = head;
struct ListNode* slot = head;
while(n>0){
fast = fast->next;
n--;
}
if(fast == NULL) return head->next;
fast = fast->next;
while(fast){
fast = fast->next;
slot = slot->next;
}
slot->next = slot->next->next;
return head;
}
JS版本
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} n
* @return {ListNode}
*/
var removeNthFromEnd = function(head, n) {
let fast = head;
let slot = head;
while(n>0){
fast = fast.next;
n--
}
if(fast == null) return head.next;//如果这时候fast已经走到头了 就直接删除头结点就好了
fast = fast.next; // 多走一步
while(fast){
fast = fast.next;
slot = slot.next;
}
slot.next = slot.next.next;
return head;
};
LeetCode 面试题 02.07. 链表相交
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
如图
所以我们首先要做的是求出两个链表的长度,然后再进行求差异
然后把长度长的链表的指针移动差异值个位置,然后两个指针对比每个节点的地址是否一致
JS解法
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
let getLength = (head)=>{
let len = 0;
while(head){
head=head.next;
len++;
}
return len
}
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
let lenA = getLength(headA);
let lenB = getLength(headB);
let sub;
sub = lenA-lenB;
if(sub>0){
while(sub--){
headA = headA.next;
}
}
else{
let loop = Math.abs(sub) // 注意 这时候sub是<0的 最好取一下绝对值
while(loop--){
headB = headB.next;
}
}
while(headA !== headB){
headA = headA.next;
headB = headB.next;
}
return headA;
};
142.环形链表II
链接:https://leetcode.cn/problems/linked-list-cycle-ii/
这道题就是判断链表是否有环,并返回入口的节点,
其实判断链表是否有环很简单,我们做一个快慢指针,快指针一次走两步 慢指针一次走一步,不停的循环下去,若快指针走到null,则证明链表无环,若是当快指针和慢指针相遇,那么就证明链表有环
这个时候 我们的 慢指针走了k步 而快指针走了 2k步
而这个时候我们拆解 k 走了什么 k = 头结点到入口节点 m + 入口节点到相遇节点 n;
而快指针其实也走了这一段 k+m 但是快指针是走了2k的 所以快指针走了两个 k+m
所以 2*(n+m) = n+m + (环内走的距离)
所以环内走的这一段距离也是n+m;
而 n是入口节点到相遇节点的距离
n+m 又是环内的距离
那么剩下的 相遇节点到入口节点的距离 就是 m
所以我们将慢指针再从头开始走 快指针从相遇节点开始走 一次走一步,当 两个指针相遇的时候,他们都走了m步; 就是入口节点的位置
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {ListNode}
*/
var detectCycle = function(head) {
// 快慢指针初始化指向 head
let slow = head;
let fast = head;
// 快指针走到末尾时停止
while (fast && fast.next) {
// 慢指针走一步,快指针走两步
slow = slow.next;
fast = fast.next.next;
// 快慢指针相遇,说明含有环
if (slow == fast) {
// 任一一节点指向头节点
fast = head;
// 同步向前进
while (fast != slow) {
fast = fast.next;
slow = slow.next;
}
// 返回入口节点
return fast;
}
}
// 不包含环
return null;
};
今日心得
今天做题+写文章一共4小时30分钟,环形链表2这道题是在环形链表上的一个提升,但难度会比光求环形链表要难上不少,你得想得通为什么第二次他们相遇就是入口节点这个问题。也是第一次做这一道题,在这里卡了不少时间,但是做算法,画图真的是最有助于理解的工具,让我们可以快速的梳理思路,所以尽量在做题的时候画图。