什么是链表

链表图示

1-1 什么是链表【一手微信itit11223344】.mp4_20210921_142625634.jpg

1-1 什么是链表【一手微信itit11223344】.mp4_20210921_142845504.jpg

使用链表增删改查

图示

1632365802278.jpg

代码实现

  1. public class LinkedList<E> {
  2. private class Node{
  3. public E e;
  4. public Node next;
  5. public Node(E e,Node next){
  6. this.e = e;
  7. this.next = next;
  8. }
  9. public Node(E e){
  10. this(e,null);
  11. }
  12. public Node(){
  13. this(null,null);
  14. }
  15. @Override
  16. public String toString() {
  17. return e.toString();
  18. }
  19. }
  20. private Node dummyHead;
  21. private int size;
  22. public LinkedList(){
  23. dummyHead = new Node(null,null);
  24. size = 0;
  25. }
  26. public boolean isEmpty(){
  27. return size == 0;
  28. }
  29. //在链表的index位置添加新的元素e
  30. //在链表中不是一个常用的操作,仅用于学习使用
  31. public void add(int index,E e){
  32. if (index < 0 || index>size){
  33. throw new IllegalArgumentException("Add Failed. Illegal index");
  34. }
  35. //使用dummyHead头节点之后就不用再就行判断是否插入位置是0的顾虑了
  36. Node prev = dummyHead;
  37. for (int i = 0; i < index; i++) {
  38. prev = prev.next;
  39. }
  40. //Node node = new Node(e);
  41. //node.next = prev.next;
  42. //prev.next = node;
  43. prev.next = new Node(e,prev.next);
  44. size++;
  45. }
  46. //在链表头添加新的元素
  47. public void addFirst(E e){
  48. //Node node = new Node(e);
  49. //node.next = head;
  50. //head = node;
  51. add(0,e);
  52. }
  53. //在链表末尾添加新的元素
  54. public void addLast(E e){
  55. add(size,e);
  56. }
  57. //获得链表的第index个位置的元素
  58. //在链表中不是一个常用的操作,仅是练习使用
  59. public E get(int index){
  60. if (index < 0 || index >= size){
  61. throw new IllegalArgumentException("Get Failed . Illegal index");
  62. }
  63. Node cur = dummyHead.next; //这次得到的是头节点
  64. for (int i = 0; i < index; i++) {
  65. cur = cur.next;
  66. }
  67. return cur.e;
  68. }
  69. //获得链表的第一个元素
  70. public E getFirst(){
  71. return get(0);
  72. }
  73. //获得链表的最后一个元素
  74. public E getLast(){
  75. return get(size);
  76. }
  77. //修改链表中的第index个元素
  78. public void set(int index,E e){
  79. if (index < 0 || index >= size){
  80. throw new IllegalArgumentException("Set Failed. Illegal index");
  81. }
  82. Node cur = dummyHead.next;
  83. for (int i = 0; i < index; i++) {
  84. cur = cur.next;
  85. }
  86. cur.e = e;
  87. }
  88. //查找链表是否含有元素e
  89. public boolean contains(E e){
  90. Node cur = dummyHead.next;
  91. while (cur != null){
  92. if (cur.e.equals(e)){
  93. return true;
  94. }
  95. cur = cur.next;
  96. }
  97. return false;
  98. }
  99. //从列表中删除index位置的元素,返回删除的元素
  100. public E remove(int index){
  101. if (index < 0 || index >= size){
  102. throw new IllegalArgumentException("Remove Failed . Illegal index");
  103. }
  104. Node prev = dummyHead;
  105. for (int i = 0; i < index; i++) {
  106. prev = prev.next;
  107. }
  108. Node retNode = prev.next;
  109. prev.next = retNode.next;
  110. retNode.next = null;
  111. size--;
  112. return retNode.e;
  113. }
  114. //从链表中删除第一个元素,返回删除第一个元素
  115. public E removeFirst(){
  116. return remove(0);
  117. }
  118. //从链表中删除最后一个元素
  119. public E removeLast(){
  120. return remove(size-1);
  121. }
  122. public int getSize(){
  123. return size;
  124. }
  125. @Override
  126. public String toString() {
  127. StringBuffer res = new StringBuffer();
  128. Node cur = dummyHead.next;
  129. while (cur!=null){
  130. res.append(cur+"->");
  131. cur = cur.next;
  132. }
  133. res.append("NULL");
  134. return res.toString();
  135. }
  136. }

使用链表的虚拟头节点(dummyHead)

1-3 使用链表的虚拟头结点【一手微信itit11223344】.mp4_20210921_150606789.jpg

为什么要设立dummyHead?

  • 当实行插入操作时,使用dummyHead头节点就不需要再判断插入位置是0的顾虑了
  • 遍历操作index次时,就遍历index次,而不需要遍历index-1

image.png

链表实现栈

  • 链表头作为栈顶

    1. public class LinkedListForStack<E> implements Stack<E> {
    2. LinkedList<E> stack;
    3. public LinkedListForStack() {
    4. stack = new LinkedList<>();
    5. }
    6. @Override
    7. public int getSize() {
    8. return stack.getSize();
    9. }
    10. @Override
    11. public boolean isEmpty() {
    12. return stack.isEmpty();
    13. }
    14. @Override
    15. public void push(E e) {
    16. stack.addFirst(e);
    17. }
    18. @Override
    19. public E pop() {
    20. return stack.removeFirst();
    21. }
    22. @Override
    23. public E peek() {
    24. return stack.getFirst();
    25. }
    26. @Override
    27. public String toString() {
    28. final StringBuffer sb = new StringBuffer("stack top");
    29. sb.append(stack);
    30. return sb.toString();
    31. }
    32. }

带有尾指针的链表

  • 可以用来实现链表队列 ```java public class LinkedListForQueue implements Queue {
  1. private class Node{
  2. public E e;
  3. public Node next;
  4. public Node(E e, Node next){
  5. this.e = e;
  6. this.next = next;
  7. }
  8. public Node(E e){
  9. this(e,null);
  10. }
  11. public Node(){
  12. this(null,null);
  13. }
  14. @Override
  15. public String toString() {
  16. return e.toString();
  17. }
  18. }
  19. private Node head,tail;
  20. private int size;
  21. @Override
  22. public int getSize() {
  23. return size;
  24. }
  25. @Override
  26. public boolean isEmpty() {
  27. return size==0;
  28. }
  29. @Override
  30. public void enqueue(E e) {
  31. if (tail == null){
  32. tail = new Node(e);
  33. head =tail;
  34. }else {
  35. tail.next = new Node(e);
  36. tail = tail.next;
  37. }
  38. size++;
  39. }
  40. @Override
  41. public E dequeue(E e) {
  42. if (isEmpty()){
  43. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  44. }
  45. Node retNode = head;
  46. head = head.next;
  47. retNode.next = null;
  48. if (head == null){
  49. tail = null;
  50. }
  51. size--;
  52. return retNode.e;
  53. }
  54. @Override
  55. public E getFront() {
  56. if (isEmpty()){
  57. throw new IllegalArgumentException("Cannot getFront from an empty queue");
  58. }
  59. return head.e;
  60. }
  61. @Override
  62. public String toString() {
  63. final StringBuffer sb = new StringBuffer("Queue front{");
  64. Node cur = head;
  65. while (cur != null){
  66. sb.append(cur+"->");
  67. cur = cur.next;
  68. }
  69. sb.append("null} tail" );
  70. return sb.toString();
  71. }

}

  1. <a name="TlfkY"></a>
  2. # 链表的复杂度分析
  3. - 增 : O(n) , 如果是在表头添加,则是O(1)
  4. - 删 : O(n) , 如果是表头也是O(1)
  5. - 改 : O(n)
  6. - 查 : O(n)
  7. <a name="HEaRL"></a>
  8. # 链表相关问题
  9. <a name="Y6tMN"></a>
  10. ## 第一个很好的用到了dummyHead好处的例子:移除链表元素
  11. - LeetCode203
  12. <a name="AwIof"></a>
  13. ## 反转链表
  14. - 图示:
  15. ![3-1 链表最经典的问题:翻转链表【一手微信itit11223344】.mp4_20210924_093737283.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/21552184/1632737317821-beeff95d-aade-4de8-ad21-ebee57ca832d.jpeg#clientId=u7cb5e946-f6c7-4&from=ui&height=410&id=uba0be041&margin=%5Bobject%20Object%5D&name=3-1%20%E9%93%BE%E8%A1%A8%E6%9C%80%E7%BB%8F%E5%85%B8%E7%9A%84%E9%97%AE%E9%A2%98%EF%BC%9A%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8%E3%80%90%E4%B8%80%E6%89%8B%E5%BE%AE%E4%BF%A1itit11223344%E3%80%91.mp4_20210924_093737283.jpg&originHeight=1080&originWidth=1728&originalType=binary&ratio=1&size=76871&status=done&style=none&taskId=u31fdf69a-3162-49f9-95e1-8c7ca0f9b9e&width=656)
  16. - 当使用递归时:
  17. ![3-3 翻转链表的递归实现【一手微信itit11223344】.mp4_20210924_100755474.jpg](https://cdn.nlark.com/yuque/0/2021/jpeg/21552184/1632737379074-f1fd010a-164a-4316-938f-b3fda20936c9.jpeg#clientId=u7cb5e946-f6c7-4&from=ui&height=411&id=u53281aa1&margin=%5Bobject%20Object%5D&name=3-3%20%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8%E7%9A%84%E9%80%92%E5%BD%92%E5%AE%9E%E7%8E%B0%E3%80%90%E4%B8%80%E6%89%8B%E5%BE%AE%E4%BF%A1itit11223344%E3%80%91.mp4_20210924_100755474.jpg&originHeight=1080&originWidth=1728&originalType=binary&ratio=1&size=85947&status=done&style=none&taskId=udad9a41b-bc36-4f0c-8f1d-5fd4cdfab52&width=657)
  18. <a name="hIbfB"></a>
  19. # 递归问题
  20. <a name="pqIxu"></a>
  21. ## 使用递归实现了链表
  22. ```java
  23. public class RecursionForLinkedList<E> {
  24. private class Node{
  25. public E e;
  26. public Node next;
  27. public Node(E e, Node next){
  28. this.e = e;
  29. this.next = next;
  30. }
  31. public Node(E e){
  32. this(e,null);
  33. }
  34. public Node(){
  35. this(null,null);
  36. }
  37. @Override
  38. public String toString() {
  39. return e.toString();
  40. }
  41. }
  42. private Node head;
  43. private int size;
  44. public RecursionForLinkedList() {
  45. head = null;
  46. size = 0;
  47. }
  48. public boolean isEmpty(){
  49. return size == 0;
  50. }
  51. public int getSize(){
  52. return size;
  53. }
  54. public void add(int index , E e){
  55. if (index < 0 || index>size){
  56. throw new IllegalArgumentException("Add Failed. Illegal index");
  57. }
  58. head = add(head,index,e);
  59. size++;
  60. }
  61. private Node add(Node node,int index,E e){
  62. if (index == 0){
  63. return new Node(e,node);
  64. }
  65. node.next = add(node.next,index-1,e);
  66. return node;
  67. }
  68. /**
  69. * 在链表头添加新的元素
  70. */
  71. private void addFirst(E e){
  72. add(0,e);
  73. }
  74. /**
  75. * 在链表末尾添加新的元素
  76. */
  77. private void addLast(E e){
  78. add(size,e);
  79. }
  80. /**
  81. * 获得链表第index个位置的值
  82. */
  83. public E get(int index){
  84. if (index < 0 || index >= size){
  85. throw new IllegalArgumentException("Get Failed . Illegal index");
  86. }
  87. return get(head,index);
  88. }
  89. private E get(Node node,int index){
  90. if (index < 0 || index >= size){
  91. throw new IllegalArgumentException("Get Failed . Illegal index");
  92. }
  93. if (index == 0){
  94. return node.e;
  95. }
  96. return get(node.next,index-1);
  97. }
  98. /**
  99. * 获得链表的第一个元素
  100. */
  101. private E getFirst(){
  102. return get(0);
  103. }
  104. /**
  105. * 获得最后一个元素
  106. */
  107. private E getLast(){
  108. return get(size);
  109. }
  110. /**
  111. * 修改第index个位置的元素
  112. */
  113. public void set(int index,E e){
  114. set(head,index,e);
  115. }
  116. private void set(Node node,int index,E e){
  117. if (index < 0 || index >= size){
  118. throw new IllegalArgumentException("Set Failed. Illegal index");
  119. }
  120. if (index == 0){
  121. node.e = e;
  122. }
  123. set(node.next,index-1,e);
  124. }
  125. /**
  126. * 查找链表是否存在某个元素
  127. */
  128. public boolean contains(E e){
  129. return contains(head,e);
  130. }
  131. private boolean contains(Node node,E e){
  132. if (node == null){
  133. return false;
  134. }
  135. if (node.e.equals(e)){
  136. return true;
  137. }
  138. return contains(node.next,e);
  139. }
  140. /**
  141. * 在链表中删除某个元素,并返回删除的元素
  142. */
  143. public E remove(int index){
  144. if (index < 0 || index >= size){
  145. throw new IllegalArgumentException("Remove Failed. Illegal index");
  146. }
  147. Pair<Node,E> res = remove(head,index);
  148. size--;
  149. head = res.getKey();
  150. return res.getValue();
  151. }
  152. private Pair<Node,E> remove(Node node, int index){
  153. if (index == 0){
  154. return new Pair<>(node.next,node.e);
  155. }
  156. Pair<Node, E> res = remove(node.next, index - 1);
  157. node.next = res.getKey();
  158. return new Pair<>(node,res.getValue());
  159. }
  160. // 从链表中删除第一个元素, 返回删除的元素
  161. public E removeFirst(){ return remove(0); }
  162. // 从链表中删除最后一个元素, 返回删除的元素
  163. public E removeLast(){ return remove(size - 1); }
  164. //从链表中删除元素e
  165. public void removeElement(E e){
  166. removeElement(head,e);
  167. }
  168. private void removeElement(Node node,E e){
  169. if (node == null){
  170. return;
  171. }
  172. if (node.next.e.equals(e)){
  173. node.next = node.next.next;
  174. }else {
  175. removeElement(node.next,e);
  176. }
  177. }
  178. @Override
  179. public String toString() {
  180. StringBuilder res = new StringBuilder();
  181. Node cur = head;
  182. while (cur != null) {
  183. res.append(cur + "->");
  184. cur = cur.next;
  185. }
  186. res.append("NULL");
  187. return res.toString();
  188. }
  189. public static void main(String[] args) {
  190. RecursionForLinkedList<Integer> list = new RecursionForLinkedList<>();
  191. for (int i = 0; i < 10; i++) list.addFirst(i);
  192. System.out.println(list);
  193. list.removeElement(3);
  194. System.out.println(list);
  195. }
  196. }