【04】链表(linkedList)

24 阅读4分钟

链表

  • 多个元素组成的列表
  • 元素存储不连续,用next指针连在一起

数组 VS 链表

  • 数组:增删非首尾元素时往往需要移动元素
  • 链表:增删非首尾元素,不需要移动元素,只需要更改 next 的指向即可

JS 中的链表

  • JavaScript 中没有链表
  • 可以用 Object 模拟链表
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: 'e' }
c.next = e;
e.next = d

// 删除 e
c.next = d

LeetCode:237 删除链表中的节点

link

解题思路

  • 无法直接获取被删除节点的上个节点
  • 将被删除节点转移到下个节点

解题步骤

  • 将被删除节点的值改成下个节点
  • 删除下个节点
var deleteNode = function(node) {
    node.val = node.next.val
    node.next = node.next.next
};
// 时间复杂度 O(1)
// 空间复杂度 O(1)

LeetCode:206 反转链表

link

解题思路

  • 反转两个节点:将 n + 1 的next指向 n
  • 反转多个节点:双指针遍历链表,重复上述操作

解题步骤

  • 双指针一前一后遍历链表
  • 反转双指针
var reverseList = function(head) {
    let p1 = head;
    let p2 = null;
    while(p1) {
        // console.log(p1.val, p2 && p2.val)
        const temp = p1.next
        p1.next = p2
        p2 = p1
        p1 = temp
    }
    console.log(p1,p2)
    return p2
};
// 时间复杂度 O(n)
// 空间复杂度 O(1)

LeetCode:2 两数相加

link

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.

解题思路

  • 小学数学题,模拟相加操作
  • 需要遍历链表

解题步骤

  • 新建一个空链表
  • 遍历被相加的两个链表,模拟相加操作,将个位数追加到新链表上,将十位数留到下一位去相加
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
        p3 = p3.next
    }
    if(carry) {
        p3.next = new ListNode(carry)
    }
    return l3.next
};
// 时间复杂度: O(n)
// 空间复杂度:O(n)

LeetCode:83 删除排序链表中的重复元素

link

解题思路

  • 因为链表是有序的,所以重复元素一定相邻
  • 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值

解题步骤

  • 遍历链表,如果发现当前元素和下个元素值相同,就删除下个元素值
  • 遍历结束,返回原链表的头部
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
};
// 时间复杂度:O(n)
// 空间复杂度:O(1)

LeetCode:141 环形链表

解题思路

  • 两个人在圆形操场上的起点同时起跑,速度快的人一定会超过速度慢的人一圈
  • 用一快一慢两个指针遍历链表,如果指针能够相逢,那么链表就有圈

解题步骤

  • 用一快一慢两个指针遍历链表,如果指针能够相逢,就返回true
  • 遍历结束后,还没有相逢就返回false
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
};
// 时间复杂度:O(n)
// 空间复杂度:O(1)

前端与链表:JS 中的原型链

原型链介绍

  • 原型链的本质是链表
  • 原型链上的节点时各种原型对象,比如 Function.prototype、Object.prototype...
  • 原型链通过 proto 属性连接各种原型对象

原型链长啥样?

  • obj -> Object.prototype -> null
  • func -> Function.prototype -> Object.prototype -> null
  • arr -> Array.prototype -> Object.prototype -> null
const obj = {}
const func = () => {}
const arr = []

const obj1 = {}
Object.prototype.x = 'x'
console.log(obj1.x)

const instanceOf = (A, B) => {
    let p = A;
    while(p) {
        if(p === B.prototype) {
            return true
        }
        p = p.__proto__;
    }
    return false
}

console.log(1)

var foo = {},
F = function() {}

Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

console.log(foo.a) // value a
console.log(foo.b) // undefined

console.log(F.a)  // value a
console.log(F.b)  // value b

原型链知识点

  • 如果 A 沿着原型链能找到 B.prototype,那么A instanceof B 为true
  • 如果 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性

面试题

简述 instanceof 的原理,并用代码实现
  • 知识点:如果 A 沿着原型链能找到 B.prototype,那么A instanceof B 为true
  • 解法:遍历 A 的原型链,如果找到 B.prototype,返回 true, 否则返回 false
const instanceOf = (A, B) => {
    let p = A;
    while(p) {
        if(p === B.prototype) {
            return true
        }
        p = p.__proto__;
    }
    return false
}
题二
  • 知识点:如果 A 对象上没有找到 x 属性,那么会沿着原型链找 x 属性
var foo = {},
F = function() {}

Object.prototype.a = 'value a';
Function.prototype.b = 'value b';

console.log(foo.a) // value a
console.log(foo.b) // undefined

console.log(F.a)  // undefined
console.log(F.b)  // value b

前端与链表:使用链表指针获取 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) // 1