拒绝摆烂ヾ(◍°∇°◍)ノ゙
从今天开始(2023/02/12
),定一个小目标,先刷个 300
道 Leetcode
题目(之前刷的不计入)。
当然作为一个小前端,我选择的语言是 TS
,而且刷的题目的难度会偏中等一些,大概按照 简单3
中等6
困难1
这样的题型分布吧。嗯,目前是这么打算的。
本题 Github 地址:因为比较喜欢 vscode
的界面,而且方便调试,所以 AC
完就顺便提到 github
了,也算做一个记录吧。
本篇的题目是这个系列的第
NO.12
:24. 两两交换链表中的节点NO.13
:61. 旋转链表NO.14
:82. 删除排序链表中的重复元素 IINO.15
:86. 分隔链表no.16
:92. 反转链表 II给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
示例 1:
输入: head = [1,2,3,4] 输出: [2,1,4,3]
示例 2:
输入: head = [] 输出: []
示例 3:
输入: head = [1] 输出: [1]
提示:
[0, 100]
内0 <= Node.val <= 100
思路:
prev
curr
next
三个变量。一般来说交换两个节点我们只需要定义 当前节点 curr
以及 下一个节点 next
即可,通过 curr.next = next.next.next; next.next = curr
,但是考虑到交换两个节点对前后节点的影响,我们还需要一个 prev
表示上一个节点。curr
和 next
为 null
时,直接返回 head
head
的指向tsfunction swapPairs(head: ListNode | null): ListNode | null {
let prev: ListNode | null = null;
let curr: ListNode | null = head;
let next: ListNode | null = head?.next ?? null;
let flag: boolean = true;
if (!curr || !next) return head;
while (curr && next) {
if (flag) {
flag = false;
curr.next = next.next;
next.next = curr;
head = next;
} else {
curr.next = next.next;
next.next = curr;
prev!.next = next;
}
prev = curr;
curr = curr.next;
next = curr?.next || null;
}
return head;
}
复杂度分析:
O(n)
O(1)
我们可以用递归的方法解决
递归要考虑的三点:
tsfunction swapPairs(head: ListNode | null): ListNode | null {
if (!head || !head.next) return head;
let one: ListNode | null = head;
let two: ListNode | null = one.next;
let three: ListNode | null = two?.next ?? null;
two!.next = one;
one.next = swapPairs(three);
return two;
}
复杂度分析:
O(n)
O(n)
给你一个链表的头节点 head
,旋转链表,将链表每个节点向右移动 k
**个位置。
示例 1:
输入: head = [1,2,3,4,5], k = 2 输出: [4,5,1,2,3]
示例 2:
输入: head = [0,1,2], k = 4 输出: [2,0,1]
提示:
[0, 500]
内-100 <= Node.val <= 100
0 <= k <= 2 * 109
链表只要肯画图,而且舍得用变量就问题不大
思路:我们可以将题目中 最后一个节点往前移 k 个位置 的问题
转换为 第一个节点往后移动 len - k 个位置
。因为我们在移动完第一个节点后第二个节点是很好拿到的,而移动完最后一个节点,倒数第二个节点很难拿到(又要在遍历一遍)
步骤:
curr
变量 和指向尾部节点的 curr
变量len
tsfunction rotateRight(head: ListNode | null, k: number): ListNode | null {
if(!head || !head.next) return head
let curr: ListNode | null = head;
let tail: ListNode | null = null;
let len = 0;
while (curr) {
curr = curr.next;
len++;
if (curr && !curr?.next) {
tail = curr;
}
}
curr = head;
k = k % len;
for (let i = 0; i < len - k; i++) {
/* 前面的换后移 */
head = head?.next ?? null;
tail!.next = curr;
curr!.next = null;
tail = curr;
curr = head
}
return head;
}
复杂度分析:
O(n)
,最坏的情况下遍历两遍链表O(1)
,我们只需要常数的空间存储若干变量给定一个已排序的链表的头 head
, 删除原始链表中所有重复数字的节点,只留下不同的数字 。返回 已排序的链表 。
示例 1:
输入: head = [1,2,3,3,4,4,5] 输出: [1,2,5]
示例 2:
输入: head = [1,1,1,2,3] 输出: [2,3]
提示:
[0, 300]
内-100 <= Node.val <= 100
思路:
因为题目明确了链表时顺序排列的,所以我们只要循环一次遍历即可
步骤:
prevNode
currNode
nextNode
三个变量节点prevNode
为 null
,也就是没有前面的节点tsfunction deleteDuplicates(head: ListNode | null): ListNode | null {
let prevNode: ListNode | null = null;
let currNode: ListNode | null = head;
let nextNode: ListNode | null = head?.next ?? null;
if (!nextNode) return head;
while (currNode && nextNode) {
if (currNode.val === nextNode.val) {
while (nextNode.next && nextNode.next.val === currNode.val) {
nextNode = nextNode.next;
}
if (head!.val === currNode.val) {
head = nextNode.next;
prevNode = null
} else {
prevNode!.next = nextNode.next;
}
currNode = nextNode.next;
nextNode = nextNode.next?.next ?? null;
} else {
prevNode = currNode;
currNode = currNode.next;
nextNode = nextNode.next;
}
}
return head;
}
复杂度分析:
O(n)
O(1)
给你一个链表的头节点 head
和一个特定值 **x
,请你对链表进行分隔,使得所有 小于 x
的节点都出现在 大于或等于 x
的节点之前。
你应当 保留 两个分区中每个节点的初始相对位置。
示例 1:
输入: head = [1,4,3,2,5,2], x = 3 输出:[1,2,2,4,3,5]
示例 2:
输入: head = [2,1], x = 2 输出:[1,2]
提示:
[0, 200]
内-100 <= Node.val <= 100
-200 <= x <= 200
我可以直接说看完这题的时候我是没思路的,还在想要怎么将小的节点换到前面,打的节点换到后面,后来感觉太麻烦了,就去瞄了一眼答案,才恍然大悟。
思路:这题直接构建新的链表会简单很多,一个 small
链表,一个 large
链表,分别存放比 k
小的节点和比 k
大的节点,最后只要拼接一下这两个链表就行了。
tsfunction partition(head: ListNode | null, x: number): ListNode | null {
let small: ListNode | null = new ListNode();
const smallHead: ListNode | null = small;
let large: ListNode | null = new ListNode();
const largeHead: ListNode | null = large;
while (head) {
if (head.val < x) {
small.next = head;
small = small.next;
} else {
large.next = head;
large = large.next;
}
head = head.next
}
large.next = null
small.next = largeHead.next
return smallHead.next
}
给你单链表的头指针 head
和两个整数 left
和 right
,其中 left <= right
。请你反转从位置 left
到位置 right
的链表节点,返回 反转后的链表 。
示例 1:
输入: head = [1,2,3,4,5], left = 2, right = 4 输出: [1,4,3,2,5]
示例 2:
输入: head = [5], left = 1, right = 1 输出: [5]
提示:
n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
进阶: 你可以使用一趟扫描完成反转吗?
思路:这道题可以先将要反转的部分捞出来,在通过 206. 反转链表 的方法将捞出来的部分反转,再拼接回去。因为要拼接回去,就要记录拼接左边链表的尾节点以及拼接右边链表的头结点。
lefTail
和 rightHead
reverseHead
,移除不需要翻转的节点reverseList
翻转节点 再拿到翻转节点的头结点和尾节点 reverseHead
reverseTail
ts// Definition for singly-linked list.
class ListNode {
val: number;
next: ListNode | null;
constructor(val?: number, next?: ListNode | null) {
this.val = val === undefined ? 0 : val;
this.next = next === undefined ? null : next;
}
}
// 1 -> 2 -> 3 -> 4 -> 5
// 1 <- 2 <- 3 <- 4 <- 5
function reverseList(head: ListNode | null): ListNode | null {
if (!head) return head;
let prev: ListNode | null = null;
let curr: ListNode | null = head;
let next: ListNode | null = head.next;
while (curr && next) {
curr.next = prev;
prev = curr;
curr = next;
next = next.next;
}
curr.next = prev;
return curr;
}
// 1 -> 2 -> 3 -> 4 -> 5
// LEFT:2 RIGHT:4
// 1 <- 4 <- 3 <- 2 <- 5
function reverseBetween(
head: ListNode | null,
left: number,
right: number
): ListNode | null {
let curr: ListNode | null = head;
let count: number = 1;
let lefTail: ListNode | null = null;
let rightHead: ListNode | null = null;
let reverseHead: ListNode | null = null;
if ((curr && !curr.next) || left === right) return head;
while (curr) {
if (count === left - 1) lefTail = curr;
if (count === right + 1) rightHead = curr;
curr = curr.next;
count++;
}
curr = head;
count = 1;
while (curr) {
if (count === left) reverseHead = curr;
if (count === right) curr.next = null;
curr = curr.next;
count++;
}
reverseHead = reverseList(reverseHead);
if (lefTail) lefTail!.next = reverseHead;
else head = reverseHead;
let reverseTail: ListNode | null = reverseHead;
while (reverseTail && reverseTail.next) {
reverseTail = reverseTail.next;
}
reverseTail!.next = rightHead;
return head;
}
复杂度分析
时间复杂度:O(N)
,最坏遍历了三次链表
空间复杂度:O(1)
。只使用到常数个变量。
本文作者:叶继伟
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!