# 定义
- 多个元素组成的列表
- 元素存储不连续,用 next 指针连在一起。
# 数组 vs 链表
- 数组:增删非首尾元素时往往需要移动元素;
- 链表:增删非首尾元素,不需要移动元素,只需要更改 next 的执向即可;
# 基本用法
const a = { val: 'a'}
const b = { val: 'b' }
const c = { val: 'c' }
const d = { val: 'd' }
a.next = b
b.next = c
c.next = d
// 遍历链表
let p = a
while(p) {
console.log(p.val)
p = p.next
}
// 插入
const e = { val: 'd' }
c.next = e
e.next = d
// 删除
c.next = d
# 使用场景
# 算法题:删除链表中的节点
请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点。传入函数的唯一参数为要被删除的节点
。
https://leetcode-cn.com/problems/delete-node-in-a-linked-list/
- 解题思路:如果无法直接获取被删除节点的上个节点(如果直接找到上个节点,可以将上个节点的 next 指针指向被删除节点的 next),可以将被删除节点转移到下个节点。
- 将被删除节点的值改为下个节点的值。
- 直接删除下个节点。
/**
* 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
}
# 算法题:反转链表
定义一个函数,输入一个链表的头节点,反转该链表并输出反转后链表的头节点。
- 示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
- 解题思路:反转两个节点:将 n + 1 的 next 指向 n;反转多个节点:双指针遍历链表,重复上述操作。
- 解题步骤:双指针一前一后遍历链表,然后进行反转双指针。
https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
- 代码:
var reverseList = function(head) {
let p1 = head;
let p2 = null;
while(p1) {
const tmp = p1.next;
p1.next = p2;
p2 = p1;
p1 = tmp;
}
return p2;
};
# 算法题:两数相加
给出两个非空
的链表用来表示两个非负的整数。其中,它们各自的位数是按照逆序
的方式存储的,并且它们的每个节点只能存储一位
数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
链接:https://leetcode-cn.com/problems/add-two-numbers
解题思路:就是简单的数学相加操作,需要用到遍历链表进行操作。
解题步骤:新建一个空链表作为最后结果输出链表,遍历被相加的两个链表,模拟相加操作;将个位数追加到新链表上,将十位数留到下一位去相加。
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
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 add = v1 + v2 + carry;
carry = Math.floor(add / 10);
// 获取个位上的数值
p3.next = new ListNode(add % 10);
if (p1) p1 = p1.next;
if (p2) p2 = p2.next;
p3 = p3.next;
}
if (carry) {
p3.next = new ListNode(carry);
}
return l3.next;
};
# 算法题:删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例:
输入: 1->1->2
输出: 1->2
输入: 1->1->2->3->3
输出: 1->2->3
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/
解题思路:因为链表是有序的,所以重复元素一定相邻。所以遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值。
代码:
var deleteDuplicates = function(head) {
let p = head;
while(p && p.next) {
if (p.val === p.next.val) {
p.next = p.next.next;
} else {
p = p.next;
}
}
return head
};
# 算法题:环形链表
给定一个链表,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
如果链表中存在环,则返回 true 。 否则,返回 false 。
链接:https://leetcode-cn.com/problems/linked-list-cycle
解题思路:用一快一慢两个指针遍历链表,如果指针能够相逢,那么链表就有环。
代码:
/**
* 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 (p1 === p2) {
return true
}
}
return false
};
# 算法题:相交链表
编写一个程序,找到两个单链表相交的起始节点。
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
/**
* @param {ListNode} headA
* @param {ListNode} headB
* @return {ListNode}
*/
var getIntersectionNode = function(headA, headB) {
if (!headA || !headB ) { return; }
let p1 = headA;
let p2 = headB;
while(p1 !== p2) {
p1 = (p1 === null ? headB : p1.next);
p2 = (p2 === null ? headA : p2.next);
}
return p1;
};
# JavaScript 中的原型链
- 原型链的本质是链表
- 原型链上的接地那是各种原型对象,比如:Function.prototype、Object.prototype
- 原型链通过
__proto__
属性连接各种原型对象(相当于 next 指针)
实现 instanceof 操作符:
const instanceOf = (A, B) => {
let p = A
while(p) {
if (p === B.prototype) {
return true
}
p = p.__proto__
}
return false
}
const res = instanceOf([], Array)
const res1 = instanceOf(obj, Object)
const res2 = instanceOf([], Object)
console.log(res, res1, res2)
# 使用链表指针获取 JSON 的节点值
const json = {
a: {
b: {
c: 1
}
},
d: {
e: 2
}
}
const path = ['a', 'b', 'c']
let p = json
path.forEach(k => {
p = p[k]
})
console.log(p)