链表(第一周)

链表的基础知识

  • 节点
  • 数据域
    • 指针域
  • 链表结构
    • 通过指针域的值形成的线性结构
  • 访问链表的时间复杂度
    • 查找结点 O(n)
    • 插入节点 O(1)
    • 删除节点 O(1)
  • 几种链表的经典实现方式
    • 传统方法(节点+指针)
    • 使用数组模拟
      • 指针域和数据域分离
      • 利用数组存放下标进行搜索
      • 。。。

链表和数组的区别

类型 访问 添加 删除
链表 慢 O(n) 快 O(1) 快 O(1)
数组 快 O(1) 慢 O(n) 慢 O(n)

链表的应用场景

  • 操作系统内的动态内存分配
  • LRU 淘汰缓存算法
  • 缓存算法是一种高速的数据结构
  • 设备间存在速度差异,可以通过较多的数据存放在高速区域,而将较少的内存区域放在相对低速的区域方式,来对系统进行优化

单向链表的实现

  1. // 单向链表
  2. let LinkedList = function () {
  3. // 链表元素
  4. let Node = function (element) {
  5. this.element = element;
  6. this.next = null;
  7. };
  8. let length = 0; // 链表长度
  9. let head = null; // 链表头节点
  10. // 链表末尾添加元素
  11. this.append = function (element) {
  12. let node = new Node(element);
  13. let cur = undefined;
  14. if (head === null) {
  15. head = node;
  16. } else {
  17. cur = head;
  18. while (cur.next) {
  19. cur = cur.next;
  20. }
  21. cur.next = node;
  22. }
  23. length += 1;
  24. };
  25. // 链表中插入元素
  26. this.insert = function (element, index) {
  27. let cur = head;
  28. let node = new Node(element);
  29. // 向链表头部添加
  30. if (index <= 0) {
  31. node.next = cur;
  32. head = node;
  33. }
  34. // 链表尾部添加
  35. if (index >= length) {
  36. this.append(element);
  37. }
  38. // 链表中间添加
  39. while (index-- > 1) {
  40. cur = cur.next;
  41. }
  42. node.next = cur.next;
  43. cur.next = node;
  44. length += 1;
  45. };
  46. // 移出链表中指定的项
  47. this.removeAt = function (index) {
  48. let cur = head;
  49. if (index >= 0 && index <= length) {
  50. if (index === 0) head = head.next;
  51. while (index-- > 1) {
  52. cur = cur.next;
  53. }
  54. cur.next = cur.next.next;
  55. length -= 1;
  56. } else {
  57. return false;
  58. }
  59. };
  60. // 移出值为element的项
  61. this.remove = function (element) {
  62. this.removeAt(this.indexof(element));
  63. };
  64. // 返回 元素在链表中的索引,没有则返回-1
  65. this.indexof = function (element) {
  66. let cur = head;
  67. let len = 0;
  68. while (cur) {
  69. if (cur.element == element) {
  70. return len;
  71. }
  72. cur = cur.next;
  73. len += 1;
  74. }
  75. return -1;
  76. };
  77. // 判断链表是否为空
  78. this.isEmpty = function () {
  79. return len === 0;
  80. };
  81. // 链表的长度
  82. this.size = function () {
  83. let len = 0;
  84. let cur = head;
  85. while (cur) {
  86. cur = cur.next;
  87. len += 1;
  88. }
  89. return length || len;
  90. };
  91. // 清空链表
  92. this.clear = function () {
  93. let cur = head;
  94. for (let i = 0; i < length; i++) {
  95. this.removeAt(i);
  96. }
  97. head = null;
  98. length = 0;
  99. };
  100. // 输出整个链表
  101. this.print = function () {
  102. return head;
  103. };
  104. };
  105. let linkedList = new LinkedList();
  106. linkedList.append(0);
  107. linkedList.append(1);
  108. linkedList.append(2);
  109. linkedList.append(4);
  110. linkedList.insert(3, 3);
  111. linkedList.remove(1);
  112. // linkedList.clear()
  113. console.log(linkedList.print(), "print");
  114. console.log(linkedList.size(), "size");
  115. console.log(linkedList.indexof(3), "indexof");

双向链表的实现

  • 暂无

经典面试题

  • 141 环形链表
  • 142 环形链表 ||
  • 202 快乐数
  • 206 反转链表
  • 92 反转链表 II
  • 25 K 个一组翻转链表 *
  • 61 旋转链表
  • 24 两两交换链表中的节点
  • 19 删除链表的倒数第 N 个结点
  • 83 删除排序链表中的重复元素
  • 82 删除排序链表中的重复元素 II
  • 707 设计链表