当前位置:   article > 正文

【数据结构】Java实现双向链表

java实现双向链表
LinkedList 的底层是双向链表结构 ,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来了,因此在在任意位置插入或者删除元素时,不需要搬移元素,效率比较高。

1. 接口的实现

(这个接口在前面的单链表,动态数组中也可以用到)

  1. public interface SeqList<E> {
  2. // 尾插
  3. void add(E element);
  4. // 将 element 插入到 index 位置
  5. void add(int index,E element);
  6. // 删除 index 位置元素<返回删除的值
  7. E removeByIndex(int index);
  8. // 删除第一个值element的元素
  9. void removeByValue(E element);
  10. // 删除所有值element的元素
  11. void removeAllValue(E element);
  12. // 将下标 index 位置元素设置为 element,返回替换前的值
  13. E set(int index,E element);
  14. E get(int index);
  15. // 判断 o 是否在其中
  16. boolean contains(E element);
  17. int indexOf(E element);
  18. int size();
  19. void clear();
  20. }

2. 动手实现双链表

2.1 重写SeqList接口方法

定义双向链表类,实现SeqList方法,重写同String方法。

(Alt + insert 快速实现方法重写)

  1. package seqlist.link;
  2. import seqlist.SeqList;
  3. public class DoubleLinkedList<E> implements SeqList<E> {
  4. private DoubleNode head;//头节点
  5. private DoubleNode tail;//尾节点
  6. private int size; // 车厢节点个数,保存的元素个数
  7. //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
  8. private class DoubleNode {
  9. E val;//保存的元素
  10. DoubleNode prev;
  11. DoubleNode next;
  12. DoubleNode(E val) {
  13. this.val = val;
  14. }
  15. public DoubleNode(E val, DoubleNode prev, DoubleNode next) {
  16. this.val = val;
  17. this.prev = prev;
  18. this.next = next;
  19. }
  20. }
  21. public void addFrist(E val){ }
  22. @Override
  23. public void add(E element) { }
  24. @Override
  25. public void add(int index, E element) { }
  26. @Override
  27. public E removeByIndex(int index) { }
  28. @Override
  29. public void removeByValue(E element) { }
  30. @Override
  31. public void removeAllValue(E element) { }
  32. @Override
  33. public E set(int index, E element) { }
  34. public boolean rangeCheck(int index){ }
  35. @Override
  36. public E get(int index) { }
  37. @Override
  38. public boolean contains(E element) { }
  39. @Override
  40. public int indexOf(E element) { }
  41. @Override
  42. public int size() { }
  43. @Override
  44. public void clear() { }
  45. @Override
  46. public String toString() { }
  47. }

2.2 在当前链表尾部添加节点(尾插)

(1)size++

(2)如果链表为空,这个新插入的节点就是头节点

(3)链表不为空,将当前链表的尾节点的next指向新节点,新节点的前驱prev指向尾节点

(4)更新当新节点为尾节点

  1. public void add(E element) {
  2. DoubleNode node = new DoubleNode(element);
  3. size ++;
  4. if (head == null){
  5. head = node;
  6. }else {
  7. node.prev = tail;
  8. tail.next = node;
  9. }
  10. tail = node;
  11. }

2.3 在当前链表头部添加节点(头插)

这里头插不是重写方法!(因为前面的动态数组没又头插这一说法,所以这个方法就不放在接口里了)

(1)size++

(2)如果链表尾空,那么新节点就是头节点和尾节点

(3)链表不为空,将新节点的next指向head,head的前驱prev指向新节点

(4)更新新节点为头节点head

  1. public void addFirst(E element) {
  2. DoubleNode node = new DoubleNode(element);
  3. size ++;
  4. if(head == null){
  5. tail = node;
  6. }else {
  7. node.next = head;
  8. head.prev = node;
  9. }
  10. head = node;
  11. }

2.4 检验index是否合法

  1. private boolean rangeCheck(int index) {
  2. if (index < 0 ||index >= size) {
  3. return false;
  4. }
  5. return true;
  6. }

2.5 在 第index位置添加节点(任意位置)

(1)判断index是否合法,不合法退出
(2)判断是不是头插或者尾插,调用相应的方法添加后退出
(3)调用node方法index位置节点的找到前驱prev,将prev.next作为后继节点node方法判断index是比较靠近head就从前往后遍历,比较靠近tail就从后往前遍历,使代码效率更高
(4)节点链接,先连左边区域,再连右边区域
(5)size++ 
DoubleNode node = new DoubleNode(element,prev,next);
看前面的构造函数,这时候已经将node.prev = prev,node.next = next
  1. public void add(int index, E element) {
  2. if (index < 0 || index > size){
  3. throw new IllegalArgumentException("add index illegal");
  4. }
  5. if(head == null){
  6. addFirst(element);
  7. return;
  8. }
  9. if(index == size){
  10. add(element);
  11. return;
  12. }
  13. DoubleNode prev = node(index - 1);
  14. DoubleNode next = prev.next;
  15. DoubleNode node = new DoubleNode(element,prev,next);
  16. // 先处理左边区域
  17. prev.next = node;
  18. // 再处理右半区域
  19. next.prev = node;
  20. size ++;
  21. }
  22. // 根据传入索引与中间位置的关系,决定到底从前向后寻找节点还是从后向前寻找节点
  23. // 内部使用的工具方法
  24. private DoubleNode node(int index){
  25. if (index < (size>>1)){
  26. DoubleNode ret = head;
  27. for (int i = 0; i < index; i++) {
  28. ret = ret.next;
  29. }
  30. return ret;
  31. }else {
  32. DoubleNode ret = tail;
  33. for (int i = size -1; i > index; i--) {
  34. ret = ret.prev;
  35. }
  36. return ret;
  37. }
  38. }

2.6  删除第index个节点

(1)判断index是否合法,不合法退出

(2)调用node方法找到待删除节点

(3)调用unlink(node)方法进行删除,将node的前驱prev和后继next连接,将node的prev和next置空null,再size--。(前驱prev为空时,node是头节点,将新的头节点设为node的下一个节点next;后继next为空时node为尾节点,将node的前驱prev设尾节点)

(4)返回被删除节点的值

  1. public E removeByIndex(int index) {
  2. if (!rangeCheck(index)){
  3. throw new IllegalArgumentException("removeByIndex index illegal");
  4. }
  5. DoubleNode node = node(index);
  6. unlink(node);
  7. return node.val;
  8. }
  9. private void unlink(DoubleNode node){
  10. DoubleNode prev =node.prev;
  11. DoubleNode next = node.next;
  12. // 先处理左半区域
  13. if(prev == null){
  14. this.head = next;
  15. }else {
  16. node.next = null;
  17. prev.next = next;
  18. }
  19. // 在处理右半区域
  20. if(next == null){
  21. this.tail = prev;
  22. }else {
  23. node.next = null;
  24. next.prev = prev;
  25. }
  26. size--;
  27. }

2.7 删除第一个值element的节点

遍历链表,在链表中找到与element值相等的节点,调用unlink(node)方法进行删除
这里的图和2.6 中的图一致
  1. public void removeByValue(E element) {
  2. DoubleNode node = head;
  3. for (int i = 0; i < size; i++) {
  4. if (node.val.equals(element)){
  5. unlink(node);
  6. return;
  7. }
  8. node = node.next;
  9. }
  10. }

2.8 删除所有值element的节点

(1)遍历链表,在链表中找到与element值相等的节点,调用unlink(node)方法进行删除

(2)链表有多长就要遍历几次,以防有的节点没有被遍历(此时,每当进行一次删除,size就会减一,直接用size遍历可能导致某些节点漏掉了,因此用length保存初始的size值)

  1. public void removeAllValue(E element) {
  2. DoubleNode node = head;
  3. // 因为每次unlink之后都会修改size的值,但是删除所有元素,
  4. // 要把所有链表节点全部遍历一遍
  5. int length = this.size;
  6. for (int i = 0; i < length; i++) {
  7. DoubleNode next = node.next;
  8. if (node.val.equals(element)) {
  9. unlink(node);
  10. }
  11. node = next;
  12. }
  13. }

2.9 修改第index个节点的值为element

(1)判断index是否合法,不合法退出

(2)调用node方法找到该节点

(3)保存原来节点的值

(4)修改该节点的值

(5)返回原来节点的值

  1. public E set(int index, E element) {
  2. if(!rangeCheck(index)){
  3. throw new IllegalArgumentException("set index illeagal");
  4. }
  5. DoubleNode node = node(index);
  6. E oldVal = node.val;
  7. node.val = element;
  8. return oldVal;
  9. }

2.10 获取第index个节点的值

(1)判断index是否合法,不合法退出

(2)调用node方法找到该节点并返回

  1. public E get(int index) {
  2. if (!rangeCheck(index)) {
  3. throw new IllegalArgumentException("get index illegal!");
  4. }
  5. return node(index).val;
  6. }

2.11 判断链表中是否存在element

  1. public boolean contains(E element) {
  2. DoubleNode node = head;
  3. while (node.next != null){
  4. if (node.val.equals(element)){
  5. return true;
  6. }
  7. node = node.next;
  8. }
  9. return false;
  10. }

2.12  获取element在链表中的位置

  1. public int indexOf(E element) {
  2. DoubleNode node = head;
  3. int i = 0;
  4. while (node.next != null){
  5. if (node.val.equals(element)){
  6. return i;
  7. }
  8. i ++;
  9. node = node.next;
  10. }
  11. return -1;
  12. }

2.13 打印链表

  1. public String toString() {
  2. StringBuilder sb = new StringBuilder();
  3. for(DoubleNode x = head; x != null; x = x.next){
  4. sb.append(x.val);
  5. sb.append("->");
  6. if(x.next == null){
  7. // 此时temp走到了尾结点
  8. sb.append("NULL");
  9. }
  10. }
  11. return sb.toString();
  12. }

2.14  获取链表长度以及清空链表

  1. public int size() {
  2. return size;
  3. }
  4. @Override
  5. public void clear() {
  6. while (head.next != null){
  7. DoubleNode node = head.next;
  8. head.next =null;
  9. head.prev = null;
  10. head = node;
  11. }
  12. head = null;
  13. size = 0;
  14. }

3. DoubleLinkedList整体实现

3.1 DoubleLinkedList类

  1. public class DoubleLinkedList<E> implements SeqList<E> {
  2. private DoubleNode head;//头节点
  3. private DoubleNode tail;//尾节点
  4. private int size; // 车厢节点个数,保存的元素个数
  5. //车厢类的定义,车厢作为火车的内部类,对外部完全隐藏
  6. private class DoubleNode {
  7. E val;//保存的元素
  8. DoubleNode prev;
  9. DoubleNode next;
  10. DoubleNode(E val) {
  11. this.val = val;
  12. }
  13. public DoubleNode(E val, DoubleNode prev, DoubleNode next) {
  14. this.val = val;
  15. this.prev = prev;
  16. this.next = next;
  17. }
  18. }
  19. // w尾插
  20. @Override
  21. public void add(E element) {
  22. DoubleNode node = new DoubleNode(element);
  23. size ++;
  24. if (head == null){
  25. head = node;
  26. }else {
  27. node.prev = tail;
  28. tail.next = node;
  29. }
  30. tail = node;
  31. }
  32. public void addFirst(E element) {
  33. DoubleNode node = new DoubleNode(element);
  34. size ++;
  35. if(head == null){
  36. tail = node;
  37. }else {
  38. node.next = head;
  39. head.prev = node;
  40. }
  41. head = node;
  42. }
  43. @Override
  44. public void add(int index, E element) {
  45. if (index < 0 || index > size){
  46. throw new IllegalArgumentException("add index illegal");
  47. }
  48. if(head == null){
  49. addFirst(element);
  50. return;
  51. }
  52. if(index == size){
  53. add(element);
  54. return;
  55. }
  56. DoubleNode prev = node(index - 1);
  57. DoubleNode next = prev.next;
  58. DoubleNode node = new DoubleNode(element,prev,next);
  59. // 先处理左边区域
  60. prev.next = node;
  61. // 再处理右半区域
  62. next.prev = node;
  63. size ++;
  64. }
  65. // 根据传入索引与中间位置的关系,决定到底从前向后寻找节点还是从后向前寻找节点
  66. // 内部使用的工具方法
  67. private DoubleNode node(int index){
  68. if (index < (size>>1)){
  69. DoubleNode ret = head;
  70. for (int i = 0; i < index; i++) {
  71. ret = ret.next;
  72. }
  73. return ret;
  74. }else {
  75. DoubleNode ret = tail;
  76. for (int i = size -1; i > index; i--) {
  77. ret = ret.prev;
  78. }
  79. return ret;
  80. }
  81. }
  82. @Override
  83. public E removeByIndex(int index) {
  84. if (!rangeCheck(index)){
  85. throw new IllegalArgumentException("removeByIndex index illegal");
  86. }
  87. DoubleNode node = node(index);
  88. unlink(node);
  89. return node.val;
  90. }
  91. @Override
  92. public void removeByValue(E element) {
  93. DoubleNode node = head;
  94. for (int i = 0; i < size; i++) {
  95. if (node.val.equals(element)){
  96. unlink(node);
  97. return;
  98. }
  99. node = node.next;
  100. }
  101. }
  102. @Override
  103. public void removeAllValue(E element) {
  104. DoubleNode node = head;
  105. // 因为每次unlink之后都会修改size的值,但是删除所有元素,
  106. // 要把所有链表节点全部遍历一遍
  107. int length = this.size;
  108. for (int i = 0; i < length; i++) {
  109. DoubleNode next = node.next;
  110. if (node.val.equals(element)) {
  111. unlink(node);
  112. }
  113. node = next;
  114. }
  115. }
  116. private void unlink(DoubleNode node){
  117. DoubleNode prev =node.prev;
  118. DoubleNode next = node.next;
  119. // 先处理左半区域
  120. if(prev == null){
  121. this.head = next;
  122. }else {
  123. node.next = null;
  124. prev.next = next;
  125. }
  126. // 在处理右半区域
  127. if(next == null){
  128. this.tail = prev;
  129. }else {
  130. node.next = null;
  131. next.prev = prev;
  132. }
  133. size--;
  134. }
  135. private boolean rangeCheck(int index) {
  136. if (index < 0 ||index >= size) {
  137. return false;
  138. }
  139. return true;
  140. }
  141. @Override
  142. public E set(int index, E element) {
  143. if(!rangeCheck(index)){
  144. throw new IllegalArgumentException("set index illeagal");
  145. }
  146. DoubleNode node = node(index);
  147. E oldVal = node.val;
  148. node.val = element;
  149. return oldVal;
  150. }
  151. @Override
  152. public E get(int index) {
  153. if (!rangeCheck(index)) {
  154. throw new IllegalArgumentException("get index illegal!");
  155. }
  156. return node(index).val;
  157. }
  158. @Override
  159. public boolean contains(E element) {
  160. DoubleNode node = head;
  161. while (node.next != null){
  162. if (node.val.equals(element)){
  163. return true;
  164. }
  165. node = node.next;
  166. }
  167. return false;
  168. }
  169. @Override
  170. public int indexOf(E element) {
  171. DoubleNode node = head;
  172. int i = 0;
  173. while (node.next != null){
  174. if (node.val.equals(element)){
  175. return i;
  176. }
  177. i ++;
  178. node = node.next;
  179. }
  180. return -1;
  181. }
  182. @Override
  183. public int size() {
  184. return size;
  185. }
  186. @Override
  187. public void clear() {
  188. while (head.next != null){
  189. DoubleNode node = head.next;
  190. head.next =null;
  191. head.prev = null;
  192. head = node;
  193. }
  194. head = null;
  195. size = 0;
  196. }
  197. @Override
  198. public String toString() {
  199. StringBuilder sb = new StringBuilder();
  200. for(DoubleNode x = head; x != null; x = x.next){
  201. sb.append(x.val);
  202. sb.append("->");
  203. if(x.next == null){
  204. // 此时temp走到了尾结点
  205. sb.append("NULL");
  206. }
  207. }
  208. return sb.toString();
  209. }
  210. }

3.2 Test类

  1. public class DoubleNodeTest {
  2. public static void main(String[] args) {
  3. DoubleLinkedList<Integer> list = new DoubleLinkedList<>();
  4. list.add(1);
  5. list.add(3);
  6. list.add(5);
  7. list.add(9);
  8. list.add(3);
  9. list.add(3);
  10. System.out.println(list);
  11. System.out.println("清空链表");
  12. list.clear();
  13. System.out.println(list);
  14. list.add(6);
  15. list.add(10);
  16. list.add(5);
  17. list.add(7);
  18. list.add(10);
  19. list.add(10);
  20. System.out.println(list);
  21. System.out.println("------------添加测试-----------");
  22. System.out.println("从链表尾部添加99,头部添加99999");
  23. list.add(99);
  24. list.addFirst(99999);
  25. System.out.println(list.size());
  26. System.out.println("添加index为2,element为88");
  27. list.add(2,88);
  28. System.out.println(list);
  29. System.out.println(list.size());
  30. System.out.println("-----------删除测试------------");
  31. System.out.println("删除index为0");
  32. list.removeByIndex(0);
  33. System.out.println("删除元素为6");
  34. list.removeByValue(6);
  35. System.out.println("删除所有10");
  36. list.removeAllValue(10);
  37. System.out.println(list);
  38. System.out.println("-----------其他------------");
  39. System.out.println("查看是否包含10这个元素");
  40. System.out.println(list.contains(10));
  41. System.out.println("修改index为3,element为19");
  42. list.set(3,19);
  43. System.out.println("获取index为3的元素");
  44. System.out.println(list.get(3));
  45. System.out.println(list);
  46. System.out.println("获取element为88的index");
  47. System.out.println(list.indexOf(88));
  48. System.out.println("获取链表长度");
  49. System.out.println(list.size());
  50. System.out.println("清空链表");
  51. list.clear();
  52. System.out.println(list + "...");
  53. }
  54. }

3.3 测试结果

趁热打铁:

【leetcode】链表(1)

【leetcode】链表(2)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/喵喵爱编程/article/detail/966342
推荐阅读
相关标签
  

闽ICP备14008679号