文章简要概述
- 本文主要是进行链表相关的算法题刷题题解记录,带你进一步了解链表相关算法如何解。
- 将接着上一篇文章数据结构与算法--链表一的内容;
- 这篇文章主要有以下六个题目的刷题题解,
K个一组翻转链表
、旋转链表
、两两交换链表中的节点
、删除链表的倒数第 N 个结点
、删除排序链表中的重复元素
、删除排序链表中的重复元素 II
与链表相关算法
K个一组翻转链表
题目大意:
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
输入: head = [1,2,3,4,5], k = 2
输出: [2,1,4,3,5]
解题思路:
- 首先这道题和之前反转链表的题中有相同部分,特别点在与指定了链表区间进行反转。
- 我首先将链表按照k的数量进行切分,然后反转,再与原链表切分的首尾部分链表。
- 需要借助一个虚拟头节点的概念,在原链表头节点之前在加一个我们自己定义的节点进行链接。
- 定义虚拟头节点hair,prev指针,从虚拟头节点开始,tail指针是待反转k个节点的位置,nex是tai指针的下一个节点,即k个节点反转区域为head到tail节点,prev和nex为反转区域的首尾连接节点。
代码:
function reverse (prev, cur) {
if (!cur) return prev;
const tem = cur.next;
cur.next = prev;
return reverse(cur, tem);
}
function reverseKGroup (head, k) {
const VNode = new ListNode(-1);
VNode.next = head;
let prev = VNode;
while (head) {
let tail = prev;
for (i = 0; i < k; i++) {
tail = tail.next;
if (!tail) {
return VNode.next;
}
}
const succ = tail.next;
const leftNode = prev.next;
prev.next = null;
tail.next = null;
reverse(null, leftNode);
prev.next = tail;
leftNode.next = succ;
head = succ;
prev = leftNode;
}
return VNode.next;
};
旋转链表
题目大意: 给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 k
**个位置。
输入: head = [1,2,3,4,5], k = 2
输出: [4,5,1,2,3]
解题思路:
- 假设链表的长度为n,向右移动n的倍数相当于在原位置(没有移动)
- 当移动k >= n时,我们仅需要向右移动k%n次即可。
- 将链表首尾相连形成闭合的环,再将链表向右移动(n-1)-(k%n)个节点就是链表的最后一个节点,然后断开链接即可得到。
代码:
function rotateRight (head, k) {
if (!head || !head.next || k === 0) return head;
let n = 1;
let cur = head;
while(cur.next) {
cur = cur.next;
n++;
}
let removeNum = n - k % n;
if (removeNum === n) {
return head;
}
cur.next = head;
while(removeNum) {
cur = cur.next;
removeNum--;
}
const result = cur.next;
cur.next = null;
return result;
};
两两交换链表中的节点
题目大意: 给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
输入: head = [1,2,3,4]
输出: [2,1,4,3]
解题思路:
- 这道题和前面的指定k个一组反转链表其实是一样的,只要将k写成2就可以实现了
- 这道题可以采用简单的写法,因为是固定的数字。如下图,先定义一个虚拟头节点,指向head,定义一个中间节点tem,指向node2,node1的next指向node2的next,node2的next再指向node1,反复进行上面步骤,直到node1和node2不存在为止即可得到答案
**代码: **
function swapPairs (head) {
const VNode = new ListNode(-1);
VNode.next = head;
let prev = VNode;
while(prev.next && prev.next.next) {
const node1 = prev.next;
const node2 = prev.next.next;
prev.next = node2;
node1.next = node2.next;
node2.next = node1;
prev = node1;
}
return VNode.next;
};
删除链表的倒数第 N 个结点
题目大意: 给你一个链表,删除链表的倒数第 n
**个结点,并且返回链表的头结点
输入: head = [1,2,3,4,5], n = 2
输出: [1,2,3,5]
解题思路:
- 方法一: 求的链表的长度L,倒数n的位置等于 L-n+1的位置,将这个位置指针向下next.next位,即可删除倒数n位的节点
- 方法二:设置两个指针,两个指针相隔n个节点,当first指针到达链表的尾部时,second节点到达待删除节点前一位,设置其指针位second.next.next,即可删除节点。
代码:
// 方法一: 链表长度 - n + 1的位置就是要删除的节点
function getLength (node) {
let n = 0;
while (node.next) {
node = node.next
n++;
}
return n;
}
var removeNthFromEnd = function(head, n) {
const l = getLength(head);
const VNode = new ListNode(-1);
VNode.next =head;
let prev = VNode;
for (let i = 0; i < l - n + 1; i++) {
prev = prev.next;
}
prev.next = prev.next?.next;
return VNode.next;
};
// 方法二 双指针,两指针间隔n个节点
function removeNthFromEnd (head, n) {
const VNode = new ListNode(-1);
VNode.next =head;
let first = VNode;
let second = VNode;
for (let i = 0; i < n; i++) {
second = second.next;
}
while(second?.next) {
first = first.next;
second = second.next;
}
first.next = first.next.next;
return VNode.next;
};
删除排序链表中的重复元素
题目大意: 存在一个按升序排列的链表,给你这个链表的头节点 head
,请你删除所有重复的元素,使每个元素 只出现一次
输入: head = [1,1,2]
输出: [1,2]
解题思路:
- 链表是升序的,因此相同的数值时连续出现的
- 只要比较cur指针与cur.next指针的值,如果值相同,将cur的next指向cur.next.next,然后重复当前操作,直到cur的next不存在为止,即可得到。
代码:
function deleteDuplicates (head) {
if (!head) return head;
let cur = head;
while(cur.next) {
if (cur.val === cur.next.val) {
cur.next = cur.next.next;
} else {
cur = cur.next;
}
}
return head;
};
删除排序链表中的重复元素 II
题目大意: 存在一个按升序排列的链表,给你这个链表的头节点 head
,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 **的数字。
输入: head = [1,2,3,3,4,4,5]
输出: [1,2,5]
解题思路:
- 链表是升序的,因此相同的数值时连续出现的
- 这道题与上一题的区别是,重复数值本身也会被删除,因此,头节点也可能被删除,需要借助虚拟头节点。
- 只要比较cur指针与cur.next指针的值,如果值相同,记录当前值,将cur的指针向后移,继续比较,如果仍相同,next指向cur.next.next。如果不相同,cur指向向后移动,然后重复当前操作,直到cur.next和cur.next.next不存在为止,即可得到。
代码:
function deleteDuplicates (head) {
const VNode = new ListNode(-1, head);
let cur = VNode;
while(cur?.next?.next) {
if (cur.next.val === cur.next.next.val) {
const val = cur.next.val;
while(cur?.next?.val === val) {
cur.next = cur.next.next;
}
} else {
cur = cur.next;
}
}
return VNode.next;
};
结束语
数据结构与算法相关的练习题会持续输出,一起来学习,当前是链表部分,后期还会有其他类型的数据结构,题目来源于leetcode。往期文章: 数据结构与算法-链表一
如果文章对你有帮助,欢迎点赞,关注!
文中图片资源来源于leetcode。