链表 - 图2

链表有两个结点是比较特殊的,它们分别是第一个结点和最后一个结点。我们习惯性地把第一个结点叫作头结点,把最后一个结点叫作尾结点。其中,头结点用来记录链表的基地址。有了它,我们就可以遍历得到整条链表。而尾结点特殊的地方是:指针不是指向下一个结点,而是指向一个空地址 NULL,表示这是链表上最后一个结点。

链表:真正的动态数据结构

  • 数据存储在“节点”(Node) 中
  • 优点: 真正的动态,不需要处理固定容量的问题
  • 缺点: 丧失了随机访问的能力
    1. class Node {
    2. E е;
    3. Node next;
    4. }
    image.png

数组和链表的对比

数组

  • 数组最好用于索引有语意的情况。scores[2]
  • 最大的优点:支持快速查询

    链表

  • 链表不适合用于索引有语意的情况

  • 最大的优点:动态

image.png
image.png

image.png


添加操作

addLast(e) O(n)
addFirst(e) O(1)
add(index, e) O(n/2) = O(n)

删除操作

removeLast(e) O(n)
removeFirst(e) O(1)
remove(index, e) O(n/2)= O(n)

修改操作

set(index,e) O(n)

查找操作

get(index) O(n)
contains(e) O(n)

  1. package com.hoho.datastruture.LinkedList;
  2. import com.sun.xml.internal.bind.marshaller.NoEscapeHandler;
  3. import sun.tools.tree.ForStatement;
  4. import sun.tools.tree.NewArrayExpression;
  5. import javax.xml.soap.Node;
  6. /**
  7. * 链表
  8. *
  9. * @author tlc
  10. */
  11. public class LinkedList<E> {
  12. private class Node {
  13. public E e;
  14. public Node next;
  15. public Node(E e, Node next) {
  16. this.e = e;
  17. this.next = next;
  18. }
  19. public Node(E e) {
  20. this(e, null);
  21. }
  22. public Node() {
  23. this(null, null);
  24. }
  25. @Override
  26. public String toString() {
  27. return e.toString();
  28. }
  29. }
  30. //虚拟头结点
  31. private Node dummyHead;
  32. private int size;
  33. public LinkedList() {
  34. dummyHead = new Node(null, null);
  35. size = 0;
  36. }
  37. public int getSize() {
  38. return size;
  39. }
  40. public boolean isEmpty() {
  41. return size == 0;
  42. }
  43. //在链表头添加
  44. public void addFirst(E e) {
  45. add(0, e);
  46. }
  47. //在链表中间 添加
  48. public void add(int index, E e) {
  49. if (index < 0 || index > size) {
  50. throw new IllegalArgumentException("Add Failed. Require index > 0 & index <= size");
  51. }
  52. //先要找到待插入节点的前一个节点
  53. Node prev = dummyHead;
  54. for (int i = 0; i < index; i++) {
  55. prev = prev.next;
  56. }
  57. //插入新的节点
  58. prev.next = new Node(e, prev.next);
  59. size++;
  60. }
  61. public void addLast(E e) {
  62. add(size, e);
  63. }
  64. public E get(int index) {
  65. if (index < 0 || index > size) {
  66. throw new IllegalArgumentException("get Failed. Require index > 0 & index <= size");
  67. }
  68. //链表的实际第一个节点
  69. Node current = dummyHead.next;
  70. for (int i = 0; i < index; i++) {
  71. current = current.next;
  72. }
  73. return current.e;
  74. }
  75. public E getFirst() {
  76. return get(0);
  77. }
  78. public E getLast() {
  79. return get(size - 1);
  80. }
  81. public void setDummyHead(int index, E e) {
  82. if (index < 0 || index > size) {
  83. throw new IllegalArgumentException("Set Failed. Require index > 0 & index <= size");
  84. }
  85. Node current = dummyHead.next;
  86. for (int i = 0; i < index; i++) {
  87. current = current.next;
  88. }
  89. current.e = e;
  90. }
  91. public boolean contains(E e) {
  92. Node current = dummyHead.next;
  93. while (current != null) {
  94. if (current.e.equals(e)) {
  95. return true;
  96. }
  97. current = current.next;
  98. }
  99. return false;
  100. }
  101. public E remove(int index) {
  102. if (index < 0 || index > size) {
  103. throw new IllegalArgumentException("Set Failed. Require index > 0 & index <= size");
  104. }
  105. Node prev = dummyHead;
  106. for (int i = 0; i < index; i++) {
  107. prev = prev.next;
  108. }
  109. Node retNode = prev.next;
  110. prev.next = retNode.next;
  111. retNode.next = null;
  112. size--;
  113. return retNode.e;
  114. }
  115. public E removeFirst(){
  116. return remove(0);
  117. }
  118. public E removeLast(){
  119. return remove(size-1);
  120. }
  121. @Override
  122. public String toString() {
  123. StringBuilder res = new StringBuilder();
  124. Node current = dummyHead.next;
  125. while (current != null) {
  126. res.append(current + " -> ");
  127. current = current.next;
  128. }
  129. res.append("NULL");
  130. return res.toString();
  131. }
  132. }