1. 链表是什么?
- 多个元素组成的链表
- 元素存储不连续,用next指针连在一起
1-1 数组 VS 链表
数组: 增删非首尾元素时往往需要移动元素
链表: 增删非首尾元素,不需要移动元素,只需要更改next的指向即可
1-2 JS中的链表
- js中没有链表
- 可以用Object模拟链表
const a = { value: 'a' }
const b = { value: 'b' }
const c = { value: 'c' }
const d = { value: 'd' }
a.next = b;
b.next = c;
c.next = d;
// 遍历链表
let p = a;
while (p) {
console.log(p.value);
p = p.next;
}
// 插入
const e = { value: 'e' }
c.next = e;
e.next = d;
// 删除
c.next = d;
2. 删除链表中的节点
2-1 题目
- 有一个单链表的
head,我们想删除它其中的一个节点node。 - 给你一个需要删除的节点
node。你将 无法访问 第一个节点head。 - 链表的所有值都是 唯一的,并且保证给定的节点
node不是链表中的最后一个节点。 - 删除给定的节点。注意,删除节点并不是指从内存中删除它。这里的意思是:
- 给定节点的值不应该存在于链表中。
- 链表中的节点数应该减少 1。
node前面的所有值顺序相同。node后面的所有值顺序相同。
题目来源: 力扣-删除链表中的节点
2-2 解题思路
- 如果是常规的链表,找到被删除节点的上个节点,把上个节点的next指针指向被删除节点的下个节点
- 因为我们无法直接获取被删除节点的上个节点,因为链表中的节点只会指向下个节点
- 将被删除的节点转移到下个节点,删除下个节点之前把下个节点的值转移到现在的节点上
- 将5替换为1 那么此时就是4119 再删除下个节点
2-2 题解
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} node
* @return {void} Do not return anything, modify node in-place instead.
*/
var deleteNode = function(node) {
node.val = node.next.val;
node.next = node.next.next;
};
2-3 时间空间复杂度
- 因为这个方法没有任何循环所以它的时间复杂度为O(1)
- 因为这个方法中没有数组和矩阵,所以空间复杂度也是O(1)
3. 反转链表
3-1 题目
- 给你单链表的头节点
head,请你反转链表,并返回反转后的链表
题目来源: 力扣-反转链表
3-2 解题思路
- 反转俩个节点: 将n+1的next指针指向n (俩个节点)
- 反转多个节点:双指针遍历链表,重复上述操作 (多个节点)
- 双指针一前一后遍历链表
- 反转双指针
3-3 题解
/**
* 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 reverseList = function(head) {
let p1 = head;
let p2 = null;
while (p1) {
console.log(p1.val, p2 && p2.val);
let temp = p1.next;
p1.next = p2;
p2 = p1;
p1 = temp;
}
return p2;
};
3-4 时间空间复杂度
- 因为有个while循环事件复杂度为O(n)
- 算法中,临时变量是单个值,没有数组没有矩阵,空间复杂度为O(1)
4. 俩数相加
4-1 题目
-
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
-
请你将两个数相加,并以相同形式返回一个表示和的链表。
-
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
题目来源: 力扣-俩数相加
4-2 解题思路
- 模拟相加操作
- 因为是在链表中进行操作,需要进行遍历链表
4-3 题解
- 新建一个空链表
- 遍历被相加的俩个链表
- 在链表元素上模拟相加操作
- 将个位数追加到新链表上,将十位数留到下一位去相加
var addTwoNumbers = function(l1, l2) {
// 创建一个新的链表
const l3 = new ListNode(0);
// 遍历俩个链表
let p1 = l1;
let p2 = l2;
let p3 = l3;
let carry = 0;
// 判断两个链表是否为空
while (p1 || p2) {
// 获取两个链表的值
const v1 = p1 ? p1.val : 0;
const v2 = p2 ? p2.val : 0;
// 相加
const val = v1 + v2 + carry;
// 进位
carry = Math.floor(val / 10);
// 将相加的结果放入新的链表
p3.next = new ListNode(val % 10);
// 判断两个链表是否为空,不为空则指向下一个节点
if (p1) p1 = p1.next;
if (p2) p2 = p2.next;
if (p3) p3 = p3.next;
}
// 判断是否有进位
if (carry) {
p3.next = new ListNode(carry);
}
// 返回新的链表
return l3.next;
};
4-4 时间空间复杂度
- 时间复杂度l1 l2 俩个链表的最大值 O(n)
- 空间复杂度l1 l2 俩个链表的最大值 O(n)
5. 删除排序链表中重复元素
5-1 题目
给定一个已排序的链表的头 head , 删除所有重复的元素,使每个元素只出现一次 。返回 已排序的链表 。
题目来源: 力扣-删除排序链表中重复元素
5-2 解题思路
- 因为链表是有序的,所以重复元素一定相邻
- 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值
5-3 题解
- 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值
- 遍历结束后,返回原链表的头部
/**
* 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 deleteDuplicates = function(head) {
// 遍历链表,删除重复的元素
let p = head;
while (p && p.next) {
if (p.val === p.next.val) {
p.next = p.next.next; // remove duplicates 更改链表指向删除链表中的元素
} else {
p = p.next;
}
}
return head;
};
5-4 时间空间复杂度
- 时间复杂度: O(n) while循环体
- 空间复杂度: O(1) 没有数组 矩阵 链表
6. 环形链表
6-1 题目
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
题目来源: 力扣-环形链表
6-2 解题思路
- 两个人在圆形操场上的起点同时起跑, 速度快的人一定会超过速度慢的人一圈.
- 用一快一慢俩个指针遍历链表,如果指针能够相逢,那么链表就有圈
6-3 题解
- 用一快一慢俩个指针遍历链表,如果指针能够相逢,就返回true
- 遍历结束后,还没有相逢就返回false
/**
* Definition for singly-linked list.
* function ListNode(val) {
* this.val = val;
* this.next = null;
* }
*/
/**
* @param {ListNode} head
* @return {boolean}
*/
var hasCycle = function(head) {
let p1 = head;
let p2 = head;
while (p1 && p2 && p2.next) {
p1 = p1.next;
p2 = p2.next.next;
if (p2) {
p2 = p2.next;
}
if (p1 === p2) {
return true;
}
}
return false
};
6-4 时间空间复杂度
- 时间复杂度: O(n) while循环体
- 空间复杂度: O(1) 没有数组 矩阵 链表 线性增长