文章目录
203.移除链表元素
题意:删除链表中等于给定值 val 的所有节点。
示例 1: 输入:head = [1,2,6,3,4,5,6], val = 6 输出:[1,2,3,4,5]
示例 2: 输入:head = [], val = 1 输出:[]
示例 3: 输入:head = [7,7,7,7], val = 7 输出:[]
虚拟头节点
/**
* 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)
* }
* }
*/
function removeElements(head: ListNode | null, val: number): ListNode | null {
const dummyHead = new ListNode();
dummyHead.next = head;
let temp = dummyHead;
while (temp.next) {
if (temp.next.val === val) {
temp.next = temp.next.next
} else {
temp = temp.next;
}
}
return dummyHead.next;
};
707.设计链表
在链表类中实现这些功能:
- get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
- addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点。
- addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
- addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
- deleteAtIndex(index):如果索引 index 有效,则删除链表中的第 index 个节点。
虚拟头节点
时间复杂度:初始化O(1),get消耗O(index),addAtHead消耗O(1),addAtTail消耗O(1),addAtIndex消耗O(index)。
空间复杂度:所有函数单词调用的空间复杂度均为O(1),总体空间复杂度O(n),其中n为addAtHead,addAtTail和addAtIndex的调用次数之和。
![1663892191-cHQUiq-LeetCode707插入头结点](https://img-blog.csdnimg.cn/img_convert/6ef9ba531161139df0513fe17eeb0b91.gif)
class N {
next: N;
val: number;
constructor(val?: number) {
this.val = val;
}
}
class MyLinkedList {
dummyNode: N = new N(0);
size = 0;
constructor() {
}
get(index: number): number {
if (index < 0 || index >= this.size) return -1;
let temp = this.dummyNode;
for (let i = 0; i <= index; i++) {
temp = temp.next;
}
return temp.val;
}
addAtHead(val: number): void {
this.addAtIndex(0, val);
}
addAtTail(val: number): void {
this.addAtIndex(this.size, val);
}
addAtIndex(index: number, val: number): void {
if (index > this.size) return;
index = Math.max(0, index);
this.size++;
let pred = this.dummyNode;
for (let i = 0; i < index; i++) {
pred = pred.next;
}
const n = new N(val);
n.next = pred.next;
pred.next = n;
}
deleteAtIndex(index: number): void {
if (index < 0 || index >= this.size) return;
this.size--;
let pred = this.dummyNode;
for (let i = 0; i < index; i++) {
pred = pred.next;
}
pred.next = pred.next.next;
}
}
/**
* Your MyLinkedList object will be instantiated and called as such:
* var obj = new MyLinkedList()
* var param_1 = obj.get(index)
* obj.addAtHead(val)
* obj.addAtTail(val)
* obj.addAtIndex(index,val)
* obj.deleteAtIndex(index)
*/
206.反转链表
题意:反转一个单链表。
![]()
示例: 输入: 1->2->3->4->5->NULL 输出: 5->4->3->2->1->NULL
双指针
时间复杂度: O ( n ) O(n) O(n),其中n是链表的长度。需要遍历链表一次。
空间复杂度: O ( 1 ) O(1) O(1)。
/**
* 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)
* }
* }
*/
function reverseList(head: ListNode | null): ListNode | null {
let prev: ListNode = null;
let cur: ListNode = head;
while (cur) {
const temp: ListNode = cur.next;
cur.next = prev;
prev = cur;
cur = temp;
}
return prev;
};
递归
时间复杂度:
O
(
n
)
O(n)
O(n),其中n是链表的长度。需要对链表的每个节点进行反转操作。
空间复杂度:
O
(
n
)
O(n)
O(n),其中几是链表的长度。空间复杂度主要取决于递归调用的栈空间,最多为n层。
![8951bc3b8b7eb4da2a46063c1bb96932e7a69910c0a93d973bd8aa5517e59fc8](https://img-blog.csdnimg.cn/img_convert/1aaebda8d84a04cd61ca78548e01ace2.gif)
/**
* 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)
* }
* }
*/
function reverseList(head: ListNode | null): ListNode | null {
if (!head || !head.next) return head;
const ret = reverseList(head.next);
head.next.next = head;
head.next = null;
return ret;
};
24.两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
![]()
输入:head = [1,2,3,4] 输出:[2,1,4,3] 输入:head = [] 输出:[] 输入:head = [1] 输出:[1]
迭代
- 时间复杂度: O ( n ) O(n) O(n),其中 n是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度: O ( 1 ) O(1) O(1)。
/**
* 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)
* }
* }
*/
function swapPairs(head: ListNode | null): ListNode | null {
if (!head || !head.next) return head;
const dummyNode = new ListNode();
dummyNode.next = head;
let temp = dummyNode;
while (temp.next && temp.next.next) {
let node1 = temp.next;
let node2 = temp.next.next;
temp.next = node2;
node1.next = node2.next;
node2.next = node1;
temp = node1;
}
return dummyNode.next;
};
递归
- 时间复杂度: O ( n ) O(n) O(n),其中n是链表的节点数量。需要对每个节点进行更新指针的操作。
- 空间复杂度: O ( n ) O(n) O(n),其中n是链表的节点数量。空间复杂度主要取决于递归调用的栈空间。
/**
* 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)
* }
* }
*/
function swapPairs(head: ListNode | null): ListNode | null {
if (head === null || head.next === null) return head;
const newHead = head.next;
head.next = swapPairs(newHead.next);
newHead.next = head;
return newHead;
};
19.删除链表的倒数第N个结点
给你一个链表,删除链表的倒数第
n
个结点,并且返回链表的头结点。![]()
输入:head = [1,2,3,4,5], n = 2 输出:[1,2,3,5] 输入:head = [1], n = 1 输出:[] 输入:head = [1,2], n = 1 输出:[1]
栈
- 时间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。
- 空间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。主要为栈的开销。
![iShot_2023-05-15_11.03.27](https://img-blog.csdnimg.cn/img_convert/e1ba242ad45e8c8deac3cbac124866de.gif)
/**
* 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)
* }
* }
*/
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
if (!head) return head;
const dummyNode = new ListNode(0);
dummyNode.next = head;
const stack: ListNode[] = [];
let cur = dummyNode;
let prev: ListNode = null;
while (cur.next) {
stack.unshift(cur);
cur = cur.next;
}
while (n > 0) {
n--;
prev = stack.shift();
}
prev.next = prev.next.next;
return dummyNode.next;
};
双指针
- 时间复杂度: O ( L ) O(L) O(L),其中 L L L 是链表的长度。
- 空间复杂度: O ( 1 ) O(1) O(1)。
![p3](https://img-blog.csdnimg.cn/img_convert/a1206421fda88dd6baf1096874c46a0b.png)
/**
* 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)
* }
* }
*/
function removeNthFromEnd(head: ListNode | null, n: number): ListNode | null {
if (!head) return head;
const dummyNode = new ListNode(0);
dummyNode.next = head;
let first: ListNode = head;
let second: ListNode = dummyNode;
for (let i = 0; i < n; i++) {
first = first.next;
}
while(first) {
first = first.next;
second = second.next;
}
second.next = second.next.next;
return dummyNode.next;
};
160.链表相交
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
图示两个链表在节点 c1 开始相交:
![]()
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
双指针
- 时间复杂度: O ( m + n ) O(m+n) O(m+n),其中 m m m 和 n n n 是分别是链表 h e a d A headA headA 和 h e a d B headB headB 的长度。两个指针同时遍历两个链表,每个指针遍历两个链表各一次。
- 空间复杂度: O ( 1 ) O(1) O(1)。
![iShot_2023-05-15_11.51.45](https://img-blog.csdnimg.cn/img_convert/ca0ac0eeabd7ec3f050457ad20266986.gif)
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA;
ListNode pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
}
142.环形链表Ⅱ
题意: 给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
说明:不允许修改给定的链表。
![]()
![]()
哈希表
思路:遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。
- 时间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。
- 空间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。
/**
* 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)
* }
* }
*/
function detectCycle(head: ListNode | null): ListNode | null {
if (!head) return head;
const set = new Set();
let temp = head;
while (temp) {
if (set.has(temp)) {
return temp;
}
set.add(temp);
temp = temp.next;
}
return null;
};
快慢双指针
- 时间复杂度: O ( N ) O(N) O(N),其中 N N N 为链表中节点的数目。在最初判断快慢指针是否相遇时, s l o w slow slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O ( N ) + O ( N ) = O ( N ) O(N)+O(N) =O(N) O(N)+O(N)=O(N)。
- 空间复杂度: O ( 1 ) O(1) O(1)。我们只使用了 s l o u , f a s t slou,fast slou,fast 两个指针。
![iShot_2023-05-16_10.15.12](https://img-blog.csdnimg.cn/img_convert/85349e0465be510cbfbce6574a725c57.gif)
/**
* 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)
* }
* }
*/
function detectCycle(head: ListNode | null): ListNode | null {
if (!head) return null;
let slow = head, fast = head;
while (true) {
if (!fast || !fast.next) return null;
slow = slow.next;
fast = fast.next.next;
if (slow === fast) break;
}
fast = head;
while (slow !== fast) {
slow = slow.next;
fast = fast.next;
}
return fast;
};