> 文章列表 > 十五周算法训练营——链表专题

十五周算法训练营——链表专题

十五周算法训练营——链表专题

今天是十五周算法训练营的第三周,主要讲链表专题,包含:反转链表、移除链表、交换链表、链表相交、删除链表中的倒数第N个节点、环形链表II。(欢迎加入十五周算法训练营,与小伙伴一起卷算法——文章末尾进群)

反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

e41aa49ad7c11cafcc3e3d61fbad2b5b.jpeg

img

输入:head = [1,2,3,4,5] 输出:[5,4,3,2,1]

function reverseList(head) {if (head === null || head.next === null) {return head;}// 设置初始的前一个节点为空,当前节点为headlet pre = null;let cur = head;while (cur) {const next = cur.next;cur.next = pre;pre = cur;cur = next;}return pre;
}

移除链表

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 「新的头节点」

「示例 1:」

23b99c8fd72e62bd378e96a0d6aa28a5.jpeg

img
输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
function ListNode(val, next) {this.val = val;this.next = next || null;
}
function removeElement(head, val) {// 借用一个空节点,简化删除头节点const dummy = new ListNode(null, head);let pre = dummy;let cur = dummy.next;while (cur) {// 如果找到该节点,改变节点关系,并将当前节点改变为next节点if (cur.val === val) {const next = cur.next;pre.next = next;cur = next;} else {// 如果不是该值节点,pre和cur节点都要改变pre = cur;cur = cur.next;}}return dummy.next;
}

交换链表

给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。

「示例 1:」

00b3be74a153c8623444f07ffbff952a.jpeg

img
输入:head = [1,2,3,4]
输出:[2,1,4,3]
function swapPairs(head) {if (head === null || head.next === null) {return head;}let k = 2;let start = head;let end = head;// 找到[start, end),其是一组待反转区间for (let i = 0; i < k; i++) {if (end === null) {return head;}end = end.next;}// 反转该部分const reverse = (start, end) => {if (start === null || start.next === null) {return start;}let pre = null;let cur = start;while (cur !== end) {const next = cur.next;cur.next = pre;pre = cur;cur = next;}return pre;};// 进行反转,返回反转后的开始节点const newHead = reverse(start, end);// 通过递归,与后续节点连接start.next = swapPairs(end);return newHead;
}

链表相交

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。

图示两个链表在节点 c1 开始相交:

十五周算法训练营——链表专题

img

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

示例 1:

十五周算法训练营——链表专题

img

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3 输出:Intersected at '8' 解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

二、题解

// 双指针解决
function getIntersectionNode(headA, headB) {// p1指向headA的表头结点;p2指向headB的表头结点let p1 = headA;let p2 = headB;while (p1 !== p2) {// p1走一步,如果走到A链表末尾,转到B链表if (p1.next !== null) {p1 = p1.next;} else {p1 = headB;}// p2走一步,如果走到B链表末尾,转到A链表if (p2.next !== null) {p2 = p2.next;} else {p2 = headA;}}return p1;
}

删除链表中的倒数第N个节点

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

示例 1:

f373811217e67a8f1dd4eb0f15d10b54.jpeg

img

输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5]

function ListNode(val, next) {this.val = val;this.next = next === undefined ? null : next;
}function removeNthFromEnd(head, n) {// 创建一个空节点,方便删除第一个节点的情况const dummy = new ListNode(null, head);let p1 = dummy;let p2 = dummy;// 因为要删除倒数第n个节点,则p1则必须先走n + 1步,否则找到的则是倒数第n个,不能进行删除for (let i = 0; i < n + 1; i++) {p1 = p1.next;}// p1和p2一起往后走,知道p1走到终点,这样p2就是要删除的点while (p1 != null) {p1 = p1.next;p2 = p2.next;}// 删除倒数第n个节点p2.next = p2.next.next;return dummy.next;
}

环形链表II

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

示例 1:

十五周算法训练营——链表专题

输入:head = [3,2,0,-4], pos = 1 输出:返回索引为 1 的链表节点 解释:链表中有一个环,其尾部连接到第二个节点。

// 快慢指针
function detectCycle(head) {if (head === null) {return head;}let slow = head;let fast = head;while (fast !== null && fast.next !== null) {slow = slow.next;fast = fast.next.next;// 快慢指针相遇说明有环if (slow === fast) {break;}}// fast遇到空则表示不存在环if (fast === null || fast.next === null) {return null;}// 将慢指针赋值给头部,然后快慢指针都一起移动,相交点就是环形链表的入口节点slow = head;while (slow !== fast) {fast = fast.next;slow = slow.next;}return slow;
}

a1ebbce244defa2f931bb4fbdca39ce8.jpeg