链表LinkedList(Java)

心已赠人 2023-07-01 07:27 62阅读 0赞

链表LinkedList

定义
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。
优点
真正的动态,不需要处理固定容量问题
缺点
丧失了随机访问的能力
LinkedList.java
设置了虚拟头节点,让在链表头添加元素不在特殊,使得链表添加元素都是使用同一种方式

  1. public class LinkedList<E> {
  2. private class Node {
  3. // 内部类将节点设为私有 隐藏实现细节
  4. public E e;// 元素
  5. public Node next;// next指针
  6. public Node(E e, Node next) {
  7. this.e = e;
  8. this.next = next;
  9. }
  10. public Node(E e) {
  11. this(e, null);
  12. }
  13. public Node() {
  14. this(null, null);
  15. }
  16. @Override
  17. public String toString() {
  18. return e.toString();
  19. }
  20. }
  21. private Node dummyhead; // 设置虚拟头节点
  22. int size;// 链表中元素个数
  23. public LinkedList() {
  24. // TODO Auto-generated constructor stub
  25. dummyhead = new Node(null, null);
  26. size = 0;
  27. }
  28. // 获取链表中的元素个数
  29. public int getSize() {
  30. return size;
  31. }
  32. // 返回链表是否为空
  33. public boolean isEmpty() {
  34. // TODO Auto-generated method stub
  35. return size == 0;
  36. }
  37. // 在链表的index位置添加一个元素
  38. public void add(int index, E e) {
  39. if (index < 0 || index > size) {
  40. try {
  41. throw new Exception("index越界");
  42. } catch (Exception e1) {
  43. // TODO Auto-generated catch block
  44. e1.printStackTrace();
  45. }
  46. }
  47. Node prev = dummyhead;// 创建一个索引与dummyhead指向一致
  48. for (int i = 0; i < index; i++) // 需要找到索引的前一个位置
  49. prev = prev.next;// prev在链表中移动
  50. // Node node = new Node(e);//创建一个node
  51. // node.next = prev.next;//node.next指向prev.next
  52. // prev.next = node;//prev.next指向node
  53. prev.next = new Node(e, prev.next);// 等于 上面3行代码
  54. size++;
  55. }
  56. // 在链表尾部添加元素
  57. public void addLast(E e) {
  58. add(size, e);
  59. }
  60. // 在链表头添加元素
  61. public void addFirst(E e) {
  62. // TODO Auto-generated method stub
  63. add(0, e);
  64. }
  65. // 获得链表的第index个元素
  66. public E get(int index) {
  67. if (index < 0 || index > size) {
  68. try {
  69. throw new Exception("index越界");
  70. } catch (Exception e1) {
  71. // TODO Auto-generated catch block
  72. e1.printStackTrace();
  73. }
  74. }
  75. Node cur = dummyhead.next;// 寻找第index个元素
  76. for (int i = 0; i < index; i++)
  77. cur = cur.next;
  78. return cur.e;// 返回cur.e就是第index个元素
  79. }
  80. // 获得第一个元素
  81. public E getFirst() {
  82. return get(0);
  83. }
  84. // 获得最后一个元素
  85. public E getLast() {
  86. return get(size - 1);
  87. }
  88. // 修改链表的第index个元素
  89. public void set(int index, E e) {
  90. if (index < 0 || index >= size) {
  91. try {
  92. throw new Exception("index越界");
  93. } catch (Exception e1) {
  94. // TODO Auto-generated catch block
  95. e1.printStackTrace();
  96. }
  97. }
  98. Node cur = dummyhead.next;// 寻找第index个元素
  99. for (int i = 0; i < index; i++)
  100. cur.e = e;
  101. }
  102. // 查找链表中是否有元素e
  103. public boolean contains(E e) {
  104. Node cur = dummyhead.next;
  105. while (cur != null) {
  106. if (cur.e.equals(e)) // 判断cur.e是否等于用户传来的e
  107. return true;
  108. cur = cur.next;// 依次遍历
  109. }
  110. return false;
  111. }
  112. public String toString() {
  113. StringBuilder res = new StringBuilder();
  114. // Node cur = dummyhead.next;
  115. // while (cur != null) {
  116. // res.append(cur + "->");
  117. // cur = cur.next;
  118. // }
  119. for (Node cur = dummyhead.next; cur != null; cur = cur.next)
  120. res.append(cur + "->");
  121. res.append("NULL");
  122. return res.toString();
  123. }
  124. // 删除链表中第index个元素,并返回删除的元素
  125. public E remove(int index) {
  126. if (index < 0 || index > size) {
  127. try {
  128. throw new Exception("index越界");
  129. } catch (Exception e) {
  130. // TODO Auto-generated catch block
  131. e.printStackTrace();
  132. }
  133. }
  134. Node prev = dummyhead;
  135. for (int i = 0; i < index; i++)
  136. prev = prev.next;
  137. Node retNode = prev.next;// prev.next所指的元素就是要删除的节点retNode
  138. prev.next = retNode.next;// prev.next跳过retNode直接指向retNode.next
  139. retNode.next = null;// 垃圾回收retNode.next
  140. size--;
  141. return retNode.e;
  142. }
  143. // 删除链表中第一个元素,返回删除的元素
  144. public E removeFirst() {
  145. return remove(0);
  146. }
  147. // 删除链表中最后一个元素,返回删除的元素
  148. public E removeLast() {
  149. return remove(size - 1);
  150. }
  151. }

测试

  1. public class LinkedListText {
  2. public static void main(String[] args) {
  3. LinkedList<Integer> linkedlist = new LinkedList<>();
  4. for (int i = 0; i < 5; i++) {
  5. linkedlist.addFirst(i);//这里使用的是头插法,使用尾插法则使用linkedlist.addLast(i);
  6. System.out.println(linkedlist);
  7. }
  8. linkedlist.add(2, 24);
  9. System.out.println(linkedlist);
  10. linkedlist.remove(2);
  11. System.out.println(linkedlist);
  12. linkedlist.removeFirst();
  13. System.out.println(linkedlist);
  14. linkedlist.removeLast();
  15. System.out.println(linkedlist);
  16. }
  17. }

测试结果
在这里插入图片描述
在这里插入图片描述
- 头插法相对简便,但插入的数据与插入的顺序相反;
- 尾插法操作相对复杂,但插入的数据与插入顺序相同。

链表的时间复杂度

增加:O(n)
删除:O(n)
修改:O(n)
查询:O(n)

如果只对链表头进行增加、删除操作:O(1);
如果只查询链表头的元素:O(1)

发表评论

表情:
评论列表 (有 0 条评论,62人围观)

还没有评论,来说两句吧...

相关阅读