当前位置:   article > 正文

Java实现单向链表——精简_tail->next = newnode; tail = newnode;

tail->next = newnode; tail = newnode;

文章目录

引言

实现思路

 添加元素

 插入元素

 删除元素

查找元素 

更新元素 

显示链表 

实现代码(完整)

总结

留言 


引言

       链表是一种重要的数据结构。它的存储空间是不连续的,单向链表是最简单的一种链表。本文主要介绍单向链表,及其“增删改查”等功能的实现。我们希望设计出来的链表不仅仅只能存储一种类型的数据。而是可以像ArrayList集合那样,存储任意类型的数据,因此设计的时候要结合泛型。

实现思路

       链表是由一个个的节点链接起来的,这里介绍的是单向链表,每个节点都要有一个数据域“value”用来存储数据,和指向下一个节点的引用“next”,它就像是一根链子,把每一个节点链接起来。除了最后一个节点之外,其余的每个节点的“next”都指向它的下一个节点,这样就形成了一个链表。

为了方便之后进行“增删改查”操作,链表要有一个头结点“head”,尾结点“tail”及链表的长度“size”。

       首先要有一个私有的静态内部类,这个类是“节点类”,它是为整个链表服务的,我们要存入链表中的数据就要被封装成一个个节点,然后链接在链表中。显然,每个节点都是根据这个节点类创建出来的。

  1. private static class ListNode<T> {// 定义私有节点内部类
  2. T value;// 用来存储数据(数据域)
  3. ListNode<T> next;// 用来存储下一节点的引用(地址域)
  4. public ListNode(T val) {// 构造方法,
  5. this.value = val;
  6. }

 添加元素

       添加元素时,我们只需要把要添加的元素封装成一个节点,然后链接在链表的尾部即可。如果链表为空,那么链表的头结点和尾结点都是这个节点。否则就把这个节点直接链接到“尾结点”后面。同时它就是新的“尾结点”

  1. public void append(T val) {// 添加元素,直接添加在链表的末尾
  2. ListNode<T> newNode = new ListNode<T>(val);
  3. // 如果尾结点为空,那么这个链表一个节点都没有,即第一次添加节点
  4. if (tail == null) {
  5. head = tail = newNode;
  6. } else {// 否则直接把节点链接在链表尾部
  7. tail.next = newNode;
  8. tail = newNode;
  9. }
  10. size++;
  11. }

 插入元素

       插入元素时,如果是在链表头部插入新节点newNode,那么直接让newNode的next指向头结点,然后newNode就是新的头结点。如果是在链表尾部插入,则直接添加元素方法append()即可。如果是在中间某个位置插入新节点newNode,那么通过for循环,从头结点开始,找到要插入位置的前一个节点prev,和prev的下一个节点after;然后让prev的next指向newNode,newNode的next指向after,链表长度加1即可。如图:

  1. public boolean insert(int position, T val) {// 插入节点,可以在链表的任意位置插入节点
  2. if (position > size || position < 0) {//如果插入的位置不合法,做出提示
  3. System.out.println("请输入正确的下标:0-" + (size - 1));
  4. return false;
  5. }
  6. ListNode<T> newNode = new ListNode<T>(val);
  7. if (position == 0) {// 在链表头结点中插入节点
  8. newNode.next = head;
  9. head = newNode;
  10. if (tail == null) {
  11. tail = newNode;// 如果第一次添加元素,此时尾结点也是此节点
  12. }
  13. size++;
  14. return true;
  15. } else if (position == size) {// 在链表尾结点中插入节点
  16. this.append(val);
  17. return true;
  18. } else {
  19. ListNode<T> prev = head;
  20. //找到要插入位置的前一个节点
  21. for (int i = 0; i < position - 1; i++) {
  22. prev = prev.next;
  23. }
  24. //此时prev的下一个节点就就是要插入位置的后一个节点
  25. ListNode<T> after = prev.next;
  26. //更改指向,即完成了添加元素
  27. prev.next = newNode;
  28. newNode.next = after;
  29. size++;
  30. return true;
  31. }
  32. }

 删除元素

       删除元素时,如果删除的节点是“头结点”,那么新的“头结点”就是原头结点的下一个节点,然后再判断链表的长度是否为0,如果为0,那么尾结点为null,删除的节点是其他的节点的话,就要从“头结点”开始遍历链表,找到要删除的节点“cur”和它前一个节点“prev”。然后让“prev”的next指向 “cur”的“next”即可。

  1. public boolean delete(T val) {
  2. if (head != null && head.value.equals(val)) {
  3. head = head.next;
  4. size--;
  5. // 删除后如果长度为0,头结点和尾结点都为空
  6. if (size == 0) {
  7. tail = null;
  8. }
  9. return true;
  10. } else {
  11. ListNode<T> prev = head;//标记要删除的节点的前一个节点
  12. ListNode<T> cur = head;//标记要删除的节点
  13. //从头结点开始遍历链表,找到了要删除的节点
  14. while (prev != null && cur != null) {
  15. if (cur.value.equals(val)) {//此条件如果成立,则找到了要删除的节点
  16. if (cur == tail) {//如果它是尾结点,那么新的尾结点就是前一个节点
  17. tail = prev;
  18. }
  19. prev.next = cur.next;//让前一个节点指向要删除节点的下一个节点
  20. size--;
  21. return true;
  22. }
  23. prev = cur;
  24. cur = cur.next;
  25. }
  26. }
  27. return false;
  28. }

查找元素 

       查找元素,通过使用for循环,从头结点开始查找。找到目标节点后,将index返回。如果没有找到 ,就返回-1。

  1. public int search(T val) {// 查找元素,返回该元素在链表中的下标,如果没有则返回-1
  2. ListNode<T> cur = head;
  3. //从头结点遍历链表,寻找目标节点
  4. for (int index = 0; cur != null; index++) {
  5. //如果某个节点的value内容和val一样,即找到了目标节点
  6. if (cur.value.equals(val)) {
  7. return index;
  8. }
  9. cur = cur.next;
  10. }
  11. //遍历链表没找到目标节点,返回-1
  12. return -1;
  13. }

更新元素 

       更新元素时,通过while循环查找要更新的节点。找到目标节点之后,把它的“value”修改成要修改的数据“newVal”即可。

  1. public boolean update(T oldVal, T newVal) {// 更新链表中的某个节点中的值
  2. ListNode<T> cur = head;
  3. // 从头结点开始遍历链表,寻找要更新的节点
  4. while (cur != null) {
  5. //找到目标节点后,更新它的值
  6. if (cur.value.equals(oldVal)) {
  7. cur.value = newVal;
  8. return true;
  9. }
  10. cur = cur.next;
  11. }
  12. return false;
  13. }

显示链表 

       显示链表可以先创建一个ArrayList集合对象“NodeList”。通过while循环,把链表的每个节点都添加进去,然后把“”NodeList打印输出即可。也可以创建一个StringJoiner对象,通过循环把每个节点的value 添加进去,然后打印输出。

  1. public void display() {// 用于显示链表
  2. ArrayList<ListNode<T>> NodeList = new ArrayList<LinkedList.ListNode<T>>();
  3. StringJoiner sj = new StringJoiner(",", "[", "]");
  4. ListNode<T> cur = head;
  5. // 通过while循环遍历链表所有节点
  6. while (cur != null) {
  7. // 把链表节点添加到ArrayList集合中
  8. NodeList.add(cur);
  9. cur = cur.next;
  10. }
  11. System.out.println(NodeList);// 打印
  12. // while (cur != null) {
  13. // sj.add(cur.value.toString());
  14. // cur = cur.next;
  15. // }
  16. // System.out.println(sj.toString());
  17. }

实现代码(完整)

LinkedList.java

  1. import java.util.ArrayList;
  2. import java.util.StringJoiner;
  3. public class LinkedList<T> {
  4. ListNode<T> head;// 链表的头结点
  5. ListNode<T> tail;// 链表的尾结点
  6. int size;// 链表的长度
  7. private static class ListNode<T> {// 定义私有节点内部类
  8. T value;// 用来存储数据(数据域)
  9. ListNode<T> next;// 用来存储下一节点的引用(地址域)
  10. public ListNode(T val) {// 构造方法,
  11. this.value = val;
  12. }
  13. @Override
  14. // 重写toString()方法,便于查看链表内容,如果链表中放入自己定义的类的对象,那么在该类中一定要重写toString()方法
  15. public String toString() {
  16. // TODO Auto-generated method stub
  17. return this.value.toString();
  18. }
  19. }
  20. public LinkedList() {// 链表的构造方法,
  21. head = null;
  22. tail = null;
  23. size = 0;
  24. }
  25. public void append(T val) {// 添加元素,直接添加在链表的末尾
  26. ListNode<T> newNode = new ListNode<T>(val);
  27. // 如果尾结点为空,那么这个链表一个节点都没有,即第一次添加节点
  28. if (tail == null) {
  29. head = tail = newNode;
  30. } else {// 否则直接把节点链接在链表尾部
  31. tail.next = newNode;
  32. tail = newNode;
  33. }
  34. size++;
  35. }
  36. public boolean insert(int position, T val) {// 插入节点,可以在链表的任意位置插入节点
  37. if (position > size || position < 0) {
  38. System.out.println("请输入正确的下标:0-" + (size - 1));
  39. return false;
  40. }
  41. ListNode<T> newNode = new ListNode<T>(val);
  42. if (position == 0) {// 在链表头结点中插入节点
  43. newNode.next = head;
  44. head = newNode;
  45. if (tail == null) {
  46. tail = newNode;// 如果第一次添加元素,此时尾结点也是此节点
  47. }
  48. size++;
  49. return true;
  50. } else if (position == size) {// 在链表尾结点中插入节点
  51. this.append(val);
  52. return true;
  53. } else {
  54. ListNode<T> prev = head;
  55. // 找到要插入位置的前一个节点
  56. for (int i = 0; i < position - 1; i++) {
  57. prev = prev.next;
  58. }
  59. // 此时prev的下一个节点就就是要插入位置的后一个节点
  60. ListNode<T> after = prev.next;
  61. // 更改指向,即完成了添加元素
  62. prev.next = newNode;
  63. newNode.next = after;
  64. size++;
  65. return true;
  66. }
  67. }
  68. public void display() {// 用于显示链表
  69. ArrayList<ListNode<T>> NodeList = new ArrayList<LinkedList.ListNode<T>>();
  70. StringJoiner sj = new StringJoiner(",", "[", "]");
  71. ListNode<T> cur = head;
  72. // 通过while循环遍历链表所有节点
  73. while (cur != null) {
  74. // 把链表节点添加到ArrayList集合中
  75. NodeList.add(cur);
  76. cur = cur.next;
  77. }
  78. System.out.println(NodeList);// 打印
  79. // while (cur != null) {
  80. // sj.add(cur.value.toString());
  81. // cur = cur.next;
  82. // }
  83. // System.out.println(sj.toString());
  84. }
  85. // 用于删除链表的元素
  86. public boolean delete(T val) {
  87. if (head != null && head.value.equals(val)) {
  88. head = head.next;
  89. size--;
  90. // 删除后如果长度为0,头结点和尾结点都为空
  91. if (size == 0) {
  92. tail = null;
  93. }
  94. return true;
  95. } else {
  96. ListNode<T> prev = head;// 标记要删除的节点的前一个节点
  97. ListNode<T> cur = head;// 标记要删除的节点
  98. // 从头结点开始遍历链表,找到了要删除的节点
  99. while (prev != null && cur != null) {
  100. if (cur.value.equals(val)) {// 此条件如果成立,则找到了要删除的节点
  101. if (cur == tail) {// 如果它是尾结点,那么新的尾结点就是前一个节点
  102. tail = prev;
  103. }
  104. prev.next = cur.next;// 让前一个节点指向要删除节点的下一个节点
  105. size--;
  106. return true;
  107. }
  108. prev = cur;
  109. cur = cur.next;
  110. }
  111. }
  112. return false;
  113. }
  114. public int search(T val) {// 查找元素,返回该元素在链表中的下标,如果没有则返回-1
  115. ListNode<T> cur = head;
  116. // 从头结点遍历链表,寻找目标节点
  117. for (int index = 0; cur != null; index++) {
  118. // 如果某个节点的value内容和val一样,即找到了目标节点
  119. if (cur.value.equals(val)) {
  120. return index;
  121. }
  122. cur = cur.next;
  123. }
  124. // 遍历链表没找到目标节点,返回-1
  125. return -1;
  126. }
  127. public boolean update(T oldVal, T newVal) {// 更新链表中的某个节点中的值
  128. ListNode<T> cur = head;
  129. // 从头结点开始遍历链表,寻找要更新的节点
  130. while (cur != null) {
  131. //找到目标节点后,更新它的值
  132. if (cur.value.equals(oldVal)) {
  133. cur.value = newVal;
  134. return true;
  135. }
  136. cur = cur.next;
  137. }
  138. return false;
  139. }
  140. }

Main.java 

  1. public class Main {
  2. public static void main(String[] args) {
  3. LinkedList<String> list = new LinkedList<String>();
  4. list.append("关羽");
  5. list.append("张飞");
  6. list.append("赵云");
  7. list.append("马超");
  8. list.append("黄忠");
  9. list.display();
  10. System.out.println("---------------------------------");
  11. int index1 = list.search("赵云");
  12. System.out.println("赵云的位置:"+index1);
  13. int index2 = list.search("刘备");
  14. System.out.println("刘备的位置:"+index2);
  15. System.out.println("---------------------------------");
  16. list.insert(2, "刘备");
  17. list.delete("马超");
  18. list.update("张飞", "孙权");
  19. list.display();
  20. }
  21. }

测试结果 :

总结

       单向链表只有指向下一个节点的引用“next”,因此,遍历只能从头结点开始从前往后遍历,直到尾结点。

       要实现链表的“插入”,“删除”,“更新”等操作,关键是要通过对链表的遍历找到“目标节点”,然后更改其引用“next”的指向即可。

留言 

由于水平有限,一些细节可能没有考虑到位,还请理解!本文仅供参考使用,如有帮助,不甚荣幸!

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

闽ICP备14008679号