单链表链表结构的实现

链表 - 图1

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.next = null;
  5. }
  6. }
  7. class LinkedList {
  8. constructor() {
  9. this.length = 0;
  10. this.head = null;
  11. }
  12. equalsFn(a, b) {
  13. return a === b;
  14. }
  15. }

功能

  1. push(element):向链表尾部添加一个新元素。
  2. insert(element, position):向链表的特定位置插入一个新元素。
  3. getElementAt(index):返回链表中特定位置的元素。如果链表中不存在这样的元素,则返回undefined。
  4. remove(element):从链表中移除一个元素。
  5. indexOf(element):返回元素在链表中的索引。如果链表中没有该元素则返回-1。
  6. removeAt(position):从链表的特定位置移除一个元素。
  7. isEmpty():如果链表中不包含任何元素,返回true,如果链表长度大于0 则返回false。
  8. size():返回链表包含的元素个数,与数组的length 属性类似。
  9. toString():返回表示整个链表的字符串。由于列表项使用了Node 类,就需要重写继承自JavaScript 对象默认的toString 方法,让其只输出元素的值。

push

  1. push(val) {
  2. let newNode = new Node(val);
  3. let curr;
  4. if (!this.head) { // 刚开始链表为空,就把这个设置为head
  5. this.head = newNode;
  6. } else {
  7. curr = this.head;
  8. while (curr.next) { // 一直找到链表末尾
  9. curr = curr.next;
  10. }
  11. curr.next = newNode;
  12. }
  13. this.length++;
  14. }

removeAt

链表 - 图2

  1. removeAt(index) {
  2. if (index < this.length && index >= 0) {
  3. let curr = this.head;
  4. if (index === 0) {
  5. this.head = curr.next;
  6. } else {
  7. // 找到index前一个后停止,下一个就是要删除的结点
  8. for (let i = 1; i < index; i++) {
  9. curr = curr.next;
  10. }
  11. let beRemove = curr.next;
  12. // 改变要被删除结点的上一个结点的指向
  13. curr.next = beRemove.next;
  14. this.length--;
  15. return beRemove;
  16. }
  17. }
  18. }

getElementAt

  1. getElementAt(index) {
  2. if (index < this.length && index >= 0) {
  3. let curr = this.head;
  4. if (index === 0) return curr;
  5. else {
  6. for (let i = 1; i <= index; i++) {
  7. curr = curr.next;
  8. }
  9. return curr;
  10. }
  11. }
  12. }

重写removeAt

  1. removeAt(index) {
  2. let curr = this.getElementAt(index - 1);
  3. curr.next = curr.next.next;

insert

链表 - 图3

  1. insert(index, value) {
  2. let newNode = new Node(value);
  3. if (index < this.length && index >= 0) {
  4. let curr = this.head;
  5. if (index === 0) { // index 为0,改变头结点指向就行了
  6. this.head = newNode;
  7. this.head.next = curr;
  8. } else {
  9. // 找到插入位置的前一个,
  10. for (let i = 1; i < index; i++) {
  11. curr = curr.next;
  12. }
  13. let nextNode = curr.next;
  14. curr.next = newNode;
  15. newNode.next = nextNode;
  16. }
  17. this.length++;
  18. }
  19. }

indexOf

  1. indexOf(value) {
  2. let curr = this.head; // 存入head
  3. if (this.equalsFn(curr.value, value)) {
  4. return 0;
  5. }
  6. for (let i = 1; i < this.length; i++) {
  7. curr = curr.next;
  8. if (this.equalsFn(curr.value, value)) {
  9. return i;
  10. }
  11. }
  12. return -1;
  13. }

removeValue

  1. removeValue(value) {
  2. let curr = this.head;
  3. let prev = null;
  4. if (this.equalsFn(curr.value, value)) {
  5. this.head = curr.next;
  6. this.length--;
  7. }
  8. for (let i = 1; i < this.length; i++) {
  9. prev = curr;
  10. curr = curr.next;
  11. if (this.equalsFn(curr.value, value)) {
  12. prev.next = curr.next;
  13. this.length--;
  14. }
  15. }
  16. return undefined;
  17. }

toString

  1. toString() {
  2. let objString = "";
  3. let curr = this.head;
  4. if (!curr) {
  5. return objString;
  6. } else {
  7. objString = `${curr.value}`;
  8. for (let i = 1; i < this.length; i++) {
  9. curr = curr.next;
  10. objString = `${objString}, ${curr.value}`;
  11. }
  12. return objString;
  13. }
  14. }

完整代码

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.next = null;
  5. }
  6. }
  7. class LinkedList {
  8. constructor() {
  9. this.length = 0;
  10. this.head = null;
  11. }
  12. equalsFn(a, b) {
  13. return a === b;
  14. }
  15. push(val) {
  16. let newNode = new Node(val);
  17. let curr;
  18. if (!this.head) { // 刚开始链表为空,就把这个设置为head
  19. this.head = newNode;
  20. } else {
  21. curr = this.head;
  22. while (curr.next) { // 一直找到链表末尾
  23. curr = curr.next;
  24. }
  25. curr.next = newNode;
  26. }
  27. this.length++;
  28. }
  29. removeAt(index) {
  30. let curr = this.getElementAt(index - 1);
  31. curr.next = curr.next.next;
  32. }
  33. getElementAt(index) {
  34. if (index < this.length && index >= 0) {
  35. let curr = this.head;
  36. if (index === 0) return curr;
  37. else {
  38. for (let i = 1; i <= index; i++) {
  39. curr = curr.next;
  40. }
  41. return curr;
  42. }
  43. }
  44. }
  45. insert(index, value) {
  46. let newNode = new Node(value);
  47. if (index < this.length && index >= 0) {
  48. let curr = this.head;
  49. if (index === 0) { // index 为0,改变头结点指向就行了
  50. this.head = newNode;
  51. this.head.next = curr;
  52. } else {
  53. // 找到插入位置的前一个,
  54. for (let i = 1; i < index; i++) {
  55. curr = curr.next;
  56. }
  57. let nextNode = curr.next;
  58. curr.next = newNode;
  59. newNode.next = nextNode;
  60. }
  61. this.length++;
  62. } else {
  63. throw "索引值错误";
  64. }
  65. }
  66. indexOf(value) {
  67. let curr = this.head; // 存入head
  68. if (this.equalsFn(curr.value, value)) {
  69. return 0;
  70. }
  71. for (let i = 1; i < this.length; i++) {
  72. curr = curr.next;
  73. if (this.equalsFn(curr.value, value)) {
  74. return i;
  75. }
  76. }
  77. return -1;
  78. }
  79. removeValue(value) {
  80. let curr = this.head;
  81. let prev = null;
  82. if (this.equalsFn(curr.value, value)) {
  83. this.head = curr.next;
  84. this.length--;
  85. }
  86. for (let i = 1; i < this.length; i++) {
  87. prev = curr;
  88. curr = curr.next;
  89. if (this.equalsFn(curr.value, value)) {
  90. prev.next = curr.next;
  91. this.length--;
  92. }
  93. }
  94. return undefined;
  95. }
  96. toString() {
  97. let objString = "";
  98. let curr = this.head;
  99. if (!curr) {
  100. return objString;
  101. } else {
  102. objString = `${curr.value}`;
  103. for (let i = 1; i < this.length; i++) {
  104. curr = curr.next;
  105. objString = `${objString}, ${curr.value}`;
  106. }
  107. return objString;
  108. }
  109. }
  110. getHead() {
  111. return this.head;
  112. }
  113. isEmpty() {
  114. return this.length === 0 ? true : false;
  115. }
  116. size() {
  117. return this.length;
  118. }
  119. }
  120. let linkedList = new LinkedList();
  121. linkedList.push("a");
  122. linkedList.push("b");
  123. linkedList.push("c");
  124. // linkedList.removeAt(1);
  125. linkedList.insert(1, "aa");
  126. console.log(linkedList.getElementAt(2));
  127. console.log(linkedList.indexOf("a"));
  128. console.log(linkedList.removeValue("c"));
  129. console.log(linkedList.size());
  130. console.log(linkedList.isEmpty());
  131. console.log(linkedList);
  132. console.log(linkedList.toString());

双向链表的实现

链表 - 图4

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.next = null;
  5. this.prev = null;
  6. }
  7. }
  8. class DBLinkedList {
  9. constructor() {
  10. this.length = 0;
  11. this.head = null;
  12. }
  13. equalsFn(a, b) {
  14. return a === b;
  15. }
  16. }

双向链表需要讨论的情况

  1. 当结点为头结点时
  2. 当结点为最后一个结点时
  3. 当结点有且只有一个结点时

重构链表方法

  1. class Node {
  2. constructor(value) {
  3. this.value = value;
  4. this.next = null;
  5. this.prev = null;
  6. }
  7. }
  8. class DBLinkedList {
  9. constructor() {
  10. this.length = 0;
  11. this.head = null;
  12. }
  13. equalsFn(a, b) {
  14. return a === b;
  15. }
  16. push(val) {
  17. let newNode = new Node(val);
  18. let prev = this.head;
  19. let curr;
  20. if (!this.head) { // 刚开始链表为空,就把这个设置为head
  21. this.head = newNode;
  22. } else {
  23. curr = this.head;
  24. while (curr.next) { // 一直找到链表末尾
  25. curr = curr.next;
  26. prev = curr;
  27. }
  28. curr.next = newNode;
  29. newNode.prev = prev;
  30. }
  31. this.length++;
  32. }
  33. removeAt(index) {
  34. if (index < this.length && index >= 0) {
  35. let curr = this.head;
  36. if (index === 0) {
  37. this.head = curr.next;
  38. if (this.length !== 1) {
  39. this.head.prev = null;
  40. }
  41. this.length--;
  42. } else {
  43. // 找到index前一个后停止,下一个就是要删除的结点
  44. for (let i = 1; i < index; i++) {
  45. curr = curr.next;
  46. }
  47. let beRemove = curr.next;
  48. // 改变要被删除结点的上一个结点的指向
  49. curr.next = beRemove.next;
  50. if (index !== this.length - 1) {
  51. beRemove.next.prev = curr;
  52. }
  53. this.length--;
  54. return beRemove;
  55. }
  56. }
  57. }
  58. getElementAt(index) {
  59. if (index < this.length && index >= 0) {
  60. let curr = this.head;
  61. if (index === 0) return curr;
  62. else {
  63. for (let i = 1; i <= index; i++) {
  64. curr = curr.next;
  65. }
  66. return curr;
  67. }
  68. }
  69. }
  70. insert(index, value) {
  71. let newNode = new Node(value);
  72. if (index < this.length && index >= 0) {
  73. let curr = this.head;
  74. if (index === 0) { // index 为0,改变头结点指向就行了
  75. this.head = newNode;
  76. this.head.next = curr;
  77. if (this.length > 1) {
  78. this.head.prev = null;
  79. }
  80. } else {
  81. // 找到插入位置的前一个,
  82. for (let i = 1; i < index; i++) {
  83. curr = curr.next;
  84. }
  85. let nextNode = curr.next;
  86. curr.next = newNode;
  87. newNode.prev = curr;
  88. newNode.next = nextNode;
  89. if (index !== this.length - 1) {
  90. nextNode.prev = newNode;
  91. }
  92. }
  93. this.length++;
  94. } else {
  95. throw "索引值错误";
  96. }
  97. }
  98. indexOf(value) {
  99. let curr = this.head; // 存入head
  100. if (this.equalsFn(curr.value, value)) {
  101. return 0;
  102. }
  103. for (let i = 1; i < this.length; i++) {
  104. curr = curr.next;
  105. if (this.equalsFn(curr.value, value)) {
  106. return i;
  107. }
  108. }
  109. return -1;
  110. }
  111. removeValue(value) {
  112. let curr = this.head;
  113. let prev = null;
  114. if (this.equalsFn(curr.value, value)) {
  115. this.head = curr.next;
  116. if (this.head) {
  117. this.head.prev = null;
  118. }
  119. this.length--;
  120. }
  121. for (let i = 1; i < this.length; i++) {
  122. prev = curr;
  123. curr = curr.next;
  124. if (this.equalsFn(curr.value, value)) {
  125. prev.next = curr.next;
  126. if (curr.next) {
  127. curr.next.prev = prev;
  128. }
  129. this.length--;
  130. }
  131. }
  132. return undefined;
  133. }
  134. toString() {
  135. let objString = "";
  136. let curr = this.head;
  137. if (!curr) {
  138. return objString;
  139. } else {
  140. objString = `${curr.value}`;
  141. for (let i = 1; i < this.length; i++) {
  142. curr = curr.next;
  143. objString = `${objString}, ${curr.value}`;
  144. }
  145. return objString;
  146. }
  147. }
  148. getHead() {
  149. return this.head;
  150. }
  151. isEmpty() {
  152. return this.length === 0 ? true : false;
  153. }
  154. size() {
  155. return this.length;
  156. }
  157. }

循环链表的概念

链表 - 图5

  • 循环链表可以像链表一样只有单向引用,也可以像双向链表一样有双向引用。
  • 循环链表和链表之间唯一的区别在于,最后一个元素指向下一个元素的指针不是引用undefined,而是指向第一个元素(head)