什么是链表

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

链表 - 图1

数组vs链表

  • 数组: 增删非首尾元素时,需要移动元素
  • 链表: 增删非首尾元素时, 更改next指针就行

模拟链表

  1. const a = {val: 'a'}
  2. const b = {val: 'b'}
  3. const c = {val: 'c'}
  4. const d = {val: 'd'}
  5. a.next = b;
  6. b.next = c;
  7. c.next = d;
  8. // 遍历链表
  9. // 定义指针
  10. let p = a
  11. while(p){
  12. console.log(p.val)
  13. p = a.next
  14. }
  15. // 插入e到c和d中间
  16. const e = {val: 'e'}
  17. c.next = e
  18. e.next = d
  19. // 删除e
  20. c.next = d

删除链表中的节点(leetcode 237)

解题思路

  • 无法直接获取被删除节点的上个节点
  • 将被删除节点转移到下个节点
  1. var deleteNode = function(node) {
  2. const next = node.next;
  3. node.val = next.val;
  4. node.next = next.next;
  5. };

反转链表

  • 如果反转两个节点,则将n+1的next指向n
  • 反转多个节点,双指针遍历链表,重复操作,直到结尾
  1. /**
  2. * @param {ListNode} head
  3. * @return {ListNode}
  4. */
  5. var reverseList = function (head) {
  6. var pre = null
  7. var current = head
  8. while (current !== null) {
  9. // 交换
  10. var next = current.next
  11. current.next = pre
  12. // 往后挪
  13. pre = current
  14. current = next
  15. }
  16. return pre
  17. };

链表相加(leetcode 2)

解题思路

  1. 用两个指针遍历两个节点,相加
  2. 如果相加值>9,则把十位数放到下一轮节点相加,个位数放在该节点

时间复杂度: O(n)
空间复杂度: O(n)

代码

  1. /**
  2. * Definition for singly-linked list.
  3. * function ListNode(val, next) {
  4. * this.val = (val===undefined ? 0 : val)
  5. * this.next = (next===undefined ? null : next)
  6. * }
  7. */
  8. /**
  9. * @param {ListNode} l1
  10. * @param {ListNode} l2
  11. * @return {ListNode}
  12. */
  13. var addTwoNumbers = function(l1, l2) {
  14. let l3 = new ListNode(0)
  15. let p1 = l1
  16. let p2 = l2
  17. let p3 = l3
  18. let carry = 0
  19. while(p1 || p2) {
  20. const v1 = p1 ? p1.val : 0
  21. const v2 = p2 ? p2.val : 0
  22. const val = v1 + v2 + carry
  23. carry = Math.floor(val / 10)
  24. p3.next = new ListNode(val % 10)
  25. if(p1){
  26. p1 = p1.next
  27. }
  28. if(p2) {
  29. p2 = p2.next
  30. }
  31. p3 = p3.next
  32. }
  33. if(carry) {
  34. p3.next = new ListNode(carry)
  35. }
  36. return l3.next;
  37. };

环形链表(leetcode 141)

  1. // 快慢指针,如果有环形,则肯定会相遇
  2. function hasCicle (head) {
  3. let slow = head
  4. let fast = head
  5. while (slow && fast && fast.next) {
  6. slow = slow.next
  7. fast = fast.next.next
  8. if(slow === fast) {
  9. return true
  10. }
  11. }
  12. return false
  13. }
  14. // 标记法,每个遍历过的节点,增加标记
  15. function hasCicle (head) {
  16. let p = head
  17. while (p) {
  18. if(p.tag){
  19. return true
  20. }
  21. p.tag = true
  22. p = p.next
  23. }
  24. return false
  25. }

原型链

原型链的本质是链表,链表通过next进行连接,对象通过proto进行连接

  • obj: obj -> Object.prototype -> null
  • func: func -> Function.prototype -> Object.prototype -> null
  • arr: arr -> Array.prototype -> Object.prototype -> null

知识点

  • 如果A沿着原型链能招找B.prototype, 那么A instanceof b 为true
  1. const instanceof = (A, B) => {
  2. let p = A
  3. while(p) {
  4. if(p === B.prototype) {
  5. return true
  6. }
  7. p = p.__proto__
  8. }
  9. return false
  10. }
  • 如果在A对象上没有招到x属性,那么会沿着原型链寻找x属性
  1. var foo = {},
  2. F = function () {};
  3. Object.prototype.a = 'value a';
  4. Function.prototype.b = 'value b';
  5. console.log(foo.a) //value a
  6. console.log(foo.b) // undefined
  7. console.log(F.a) //value a
  8. console.log(F.b) // value b

实践

  1. const json = {
  2. a: {b: {c: 1}}
  3. }
  4. const path = ['a','b', 'c']
  5. let p = json
  6. path.forEach(item => {
  7. p = p[item]
  8. })