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

数组 vs链表

  • 数组:增删非首尾元素时往往需要移动元素
  • 链表:增删非首位元素时,不需要移动元素、只需要更改next指向就好了

JS 的实现

  • javaScript没有链表,但可以用object来实现

原型链

  • 下面用a -> b表示,a.proto等于b
  • obj -> Object.prototype -> null
  • func -> Function.prototype -> Object.prototype -> null
  • arr -> Array.prototype -> Object.prototype -> nullz
  • 知识点
    • 如果A沿着原型链能找到B.prototype,那么, A instance of B 为true
    • 如果在A上面找不到的方法,会沿着A的原型链,层层向上找

单向链表

图示

JS实现

  1. // 自定义一个类,用于比较
  2. class Comparator {
  3. constructor(compareFunction) {
  4. // 如果有传进来比较函数,就用自身的比较函数
  5. this.compare = compareFunction || Comparator.defaultCompareFunction;
  6. }
  7. /*
  8. * 对比a,b
  9. * a === b 返回0
  10. * a < b 返回-1
  11. * a > b 返回1
  12. */
  13. static defaultCompareFunction(a, b) {
  14. if(a === b) return 0;
  15. return a < b? -1: 1;
  16. }
  17. // 如果a === b 返回true
  18. equal(a, b) {
  19. return this.compare(a, b) === 0;
  20. }
  21. // 如果a < b 返回true
  22. lessThan(a, b) {
  23. return this.compare(a, b) < 0;
  24. }
  25. // 如果a > b 返回true
  26. greaterThan(a, b) {
  27. return this.compare(a, b) > 0;
  28. }
  29. // 如果a <= b 返回true
  30. lessThanOrEqual(a, b) {
  31. return this.lessThan(a, b) || this.equal(a, b);
  32. }
  33. // 如果a >= b 返回true
  34. greaterThanOrEqual(a, b) {
  35. return this.greaterThan(a, b) || this.equal(a, b);
  36. }
  37. // 反转这个类 的 比较顺序
  38. reverse() {
  39. const compareOriginal = this.compare;
  40. this.compare = (a, b) => compareOriginal(b, a);
  41. }
  42. }
  1. // 定义链表节点
  2. class LinkedListNode {
  3. constructor(value, next = null) {
  4. this.value = value;
  5. this.next = next;
  6. }
  7. doSomething(callback) {
  8. console.log(this.value);
  9. callback && callback(this.value);
  10. }
  11. }
  12. // 定义链表
  13. class LinkedList {
  14. constructor() {
  15. this.head = null;
  16. this.tail = null;
  17. this.compare = new Comparator(); // 本来可以接受一个比较函数,传进去,但是这里就不做了,没必要
  18. }
  19. // 在链表头(this.head)放节点
  20. prepend(value) {
  21. // 创建一个节点
  22. const newNode = new LinkedListNode(value, this.head);
  23. if(!this.tail) {
  24. this.tail = newNode;
  25. }
  26. return this;
  27. }
  28. // 在链表尾(this.tail)放节点
  29. append(value) {
  30. const newNode = new LinkedListNode(value);
  31. if (!this.head) {
  32. this.tail = newNode;
  33. this.head = newNode;
  34. } else {
  35. this.tail.next = newNode;
  36. this.tail = newNode;
  37. }
  38. return this;
  39. }
  40. // 删除节点
  41. delete(value) {
  42. if (!this.head) return null;
  43. let deleteNodes = [];
  44. // 从head开始找,如果一直是这个值,head就一直往下指
  45. while(this.head && this.compare.equal(this.head.value === value)) {
  46. deleteNodes.push(this.head);
  47. this.head = this.head.next;
  48. }
  49. // head不是这个值了,就往下找
  50. let currentNode = this.head;
  51. // 有可能经过上面的while循环,已经变成空链表
  52. if (currentNode !== null) {
  53. while(currentNode.next) {
  54. if(this.compare.equal(this.currentNode.next.value, value)) {
  55. deleteNodes.push(this.currentNode.next);
  56. this.currentNode.next = this.currentNode.next.next;
  57. } else {
  58. this.currentNode = this.currentNode.next;
  59. }
  60. }
  61. }
  62. // tail也要判断,万一等于value,上面的操作,都没有把它删掉过
  63. if(this.compare.equal(this.tail.value, value)) {
  64. this.tail = currentNode;
  65. }
  66. // 最后把删除的节点都返回
  67. return deleteNodes;
  68. }
  69. // 链表指向反转
  70. reverse() {
  71. // 需要用三个指针
  72. let currNode = this.head;
  73. let prevNode = null;
  74. let nextNode = null;
  75. while(currNode) {
  76. // 把下一个节点保存起来
  77. nextNode = currNode.next;
  78. // 反转currNode指向
  79. currNode.next = prevNode;
  80. // 移动prevNode和currNode指针,以便下次反转
  81. prevNode = currNode;
  82. currNode = nextNode;
  83. }
  84. // 记得把head和tail指针,交换位置
  85. this.tail = this.head;
  86. this.head = prevNode;
  87. return this;
  88. }
  89. }