• LinkedList与链表


    目录

    1.链表

    2.链表的模拟实现

    3.LinkedList的模拟实现

    4.LinkedList的使用

    4.1 什么是LinkedList

    4.2 LinkedList的使用

    5.ArrayList和LinkedList的区别


    我的GitHub:Powerveil · GitHub

    我的Gitee:Powercs12 (powercs12) - Gitee.com

    皮卡丘每天学Java

    1.链表

    链表也是线性表的一种。

    链表是一种物理存储结构上非连续存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的 。

    注意:

    1. 从上图可以看出,链式结构在逻辑上是连续的,但是在物理上不一定连续

    2. 现实中的结点一般都是从堆上申请出来

    3. 从堆上申请的空间,是按照一定的策略来分配的,两次申请的空间可能连续,也可能不连续

    实际中链表的结构非常多样,以下情况组合起来就有8种链表结构:

    1. 单向或者双向

    2. 带头或者不带头

    3. 循环或者非循环 

    虽然有这么多的链表的结构,但是我们重点掌握两种:

    • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
    • 无头双向链表:在Java的集合框架库中LinkedList底层实现就是无头双向循环链表。

    2.链表的模拟实现

    1. public class TestSingleList {
    2. public ListNode head;
    3. static class ListNode {
    4. public int val;
    5. public ListNode next;
    6. public ListNode(int val) {
    7. this.val = val;
    8. }
    9. }
    10. public void display() {
    11. ListNode cur = head;
    12. while (cur != null) {
    13. System.out.printf(cur.val + " ");
    14. cur = cur.next;
    15. }
    16. System.out.println();
    17. }
    18. public void display(ListNode node) {
    19. ListNode cur = node;
    20. while (cur != null) {
    21. System.out.printf(cur.val + " ");
    22. cur = cur.next;
    23. }
    24. System.out.println();
    25. }
    26. //查找是否包含关键字key是否在单链表中
    27. public boolean contains(int key) {
    28. ListNode cur = head;
    29. while (cur != null) {
    30. if (cur.val == key) return true;
    31. cur = cur.next;
    32. }
    33. return false;
    34. }
    35. //得到单链表的长度
    36. public int size() {
    37. ListNode cur = head;
    38. int count = 0;
    39. while (cur != null) {
    40. count++;
    41. cur = cur.next;
    42. }
    43. return count;
    44. }
    45. //头插法:O(1)
    46. public void addFirst(int data) {
    47. ListNode node = new ListNode(data);
    48. node.next = head;
    49. head = node;
    50. }
    51. //尾插法:O(n)
    52. public void addLast(int data) {
    53. ListNode node = new ListNode(data);
    54. if (head == null) {
    55. head = node;
    56. } else {
    57. ListNode cur = head;
    58. while (cur.next != null) {
    59. cur = cur.next;
    60. }
    61. cur.next = node;
    62. }
    63. }
    64. private void checkIndex(int index) {
    65. if (index < 0 || index > size()) {
    66. throw new IndexNotLegalException("index的值不合法");
    67. }
    68. }
    69. //任意位置插入,第一个数据节点0号下标
    70. public void addIndex(int index, int data) {
    71. checkIndex(index);
    72. //下标为0是头插
    73. if (index == 0) {
    74. addFirst(data);
    75. }
    76. //下标为size是尾插
    77. if (index == size()) {
    78. addLast(data);
    79. }
    80. //中间插
    81. ListNode node = new ListNode(data);
    82. ListNode cur = findIndex(index - 1);
    83. node.next = cur.next;
    84. cur.next = node;
    85. }
    86. //找到指定下标节点
    87. public ListNode findIndex(int index) {
    88. ListNode cur = head;
    89. while (index > 0) {
    90. cur = cur.next;
    91. index--;
    92. }
    93. return cur;
    94. }
    95. //删除第一次出现关键字为key的节点
    96. public void remove(int key) {
    97. // if (head == null) {
    98. // throw new RuntimeException("链表没有元素不可以删除");
    99. // }
    100. if (head.val == key) {
    101. head = head.next;
    102. return;
    103. }
    104. ListNode cur = searchPrevOfKey(key);
    105. if (cur == null) return;
    106. ListNode del = cur.next;
    107. cur.next = del.next;
    108. }
    109. //根据值找到前一个节点
    110. private ListNode searchPrevOfKey(int key) {
    111. if (head == null) return null;
    112. ListNode cur = head;
    113. while (cur.next != null) {
    114. if (cur.next.val == key) return cur;
    115. cur = cur.next;
    116. }
    117. return null;
    118. }
    119. //删除所有值为key的节点
    120. public void removeAllKey(int key) {
    121. if (head == null) return;
    122. ListNode cur = head;
    123. while (cur.next != null) {
    124. ListNode curNext = cur.next;
    125. if (curNext.val == key) {
    126. cur.next = curNext.next;
    127. } else {
    128. cur = curNext;
    129. }
    130. }
    131. if (head.val == key) {
    132. head = head.next;
    133. }
    134. }
    135. //清空链表
    136. public void clear() {
    137. ListNode cur = head;
    138. while (cur != null) {
    139. ListNode curNext = cur.next;
    140. cur.next = null;
    141. cur = curNext;
    142. }
    143. head = null;
    144. }
    145. }

    3.LinkedList的模拟实现

    1. public class MyLinkedList {
    2. // 头节点
    3. public ListNode head;
    4. // 节点
    5. static class ListNode {
    6. public int val;
    7. public ListNode next;
    8. public ListNode(int val) {
    9. this.val = val;
    10. }
    11. }
    12. // 打印链表
    13. public void display() {
    14. ListNode cur = head;
    15. while (cur != null) {
    16. System.out.printf(cur.val + " ");
    17. cur = cur.next;
    18. }
    19. System.out.println();
    20. }
    21. // 从某个节点开始打印
    22. public void display(ListNode node) {
    23. ListNode cur = node;
    24. while (cur != null) {
    25. System.out.printf(cur.val + " ");
    26. cur = cur.next;
    27. }
    28. System.out.println();
    29. }
    30. // 查找是否包含关键字key是否在单链表中
    31. public boolean contains(int key) {
    32. ListNode cur = head;
    33. while (cur != null) {
    34. if (cur.val == key) return true;
    35. cur = cur.next;
    36. }
    37. return false;
    38. }
    39. // 得到单链表的长度
    40. public int size() {
    41. ListNode cur = head;
    42. int count = 0;
    43. while (cur != null) {
    44. count++;
    45. cur = cur.next;
    46. }
    47. return count;
    48. }
    49. // 头插法:O(1)
    50. public void addFirst(int data) {
    51. ListNode node = new ListNode(data);
    52. node.next = head;
    53. head = node;
    54. }
    55. // 尾插法:O(n)
    56. public void addLast(int data) {
    57. ListNode node = new ListNode(data);
    58. if (head == null) {
    59. head = node;
    60. } else {
    61. ListNode cur = head;
    62. while (cur.next != null) {
    63. cur = cur.next;
    64. }
    65. cur.next = node;
    66. }
    67. }
    68. // 检查下标是否合法
    69. private void checkIndex(int index) {
    70. if (index < 0 || index > size()) {
    71. throw new IndexNotLegalException("index的值不合法");
    72. }
    73. }
    74. // 任意位置插入,第一个数据节点0号下标
    75. public void addIndex(int index, int data) {
    76. checkIndex(index);
    77. // 下标为0是头插
    78. if (index == 0) {
    79. addFirst(data);
    80. }
    81. // 标为size是尾插
    82. if (index == size()) {
    83. addLast(data);
    84. }
    85. // 中间插
    86. ListNode node = new ListNode(data);
    87. ListNode cur = findIndex(index - 1);
    88. node.next = cur.next;
    89. cur.next = node;
    90. }
    91. // 找到指定下标节点
    92. public ListNode findIndex(int index) {
    93. ListNode cur = head;
    94. while (index > 0) {
    95. cur = cur.next;
    96. index--;
    97. }
    98. return cur;
    99. }
    100. // 删除第一次出现关键字为key的节点
    101. public void remove(int key) {
    102. // if (head == null) {
    103. // throw new RuntimeException("链表没有元素不可以删除");
    104. // }
    105. if (head.val == key) {
    106. head = head.next;
    107. return;
    108. }
    109. ListNode cur = searchPrevOfKey(key);
    110. if (cur == null) return;
    111. ListNode del = cur.next;
    112. cur.next = del.next;
    113. // cur.next = cur.next.next;
    114. }
    115. // 根据值找到前一个节点
    116. private ListNode searchPrevOfKey(int key) {
    117. if (head == null) return null;
    118. ListNode cur = head;
    119. while (cur.next != null) {
    120. if (cur.next.val == key) return cur;
    121. cur = cur.next;
    122. }
    123. return null;
    124. }
    125. // 删除所有值为key的节点
    126. public void removeAllKey(int key) {
    127. if (head == null) return;
    128. ListNode cur = head;
    129. while (cur.next != null) {
    130. ListNode curNext = cur.next;
    131. if (curNext.val == key) {
    132. cur.next = curNext.next;
    133. } else {
    134. cur = curNext;
    135. }
    136. }
    137. if (head.val == key) {
    138. head = head.next;
    139. }
    140. }
    141. // 清空链表
    142. public void clear() {
    143. ListNode cur = head;
    144. while (cur != null) {
    145. ListNode curNext = cur.next;
    146. cur.next = null;
    147. cur = curNext;
    148. }
    149. head = null;
    150. }
    151. }

    4.LinkedList的使用

    4.1 什么是LinkedList

    官方文档

    LinkedList (Java Platform SE 8 )

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

     下面是类图

    1. LinkedList实现了List接口
    2. LinkedList的底层使用了双向链表
    3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问

    注意:插入和删除的时间复杂度都为O(n),因为找到节点需要遍历

    4.2 LinkedList的使用

    LinkedList的构造

    方法解释
    LinkedList()无参构造
    public LinkedList(Collection c)使用其他集合容器中元素构造List
    1. @SuppressWarnings({"all"})
    2. public class Test {
    3. public static void main(String[] args) {
    4. // 构造一个空的LinkedList
    5. LinkedList linkedList1 = new LinkedList<>();
    6. List list = new ArrayList<>();
    7. list.add("Hello world!");
    8. list.add("每天学Java");
    9. list.add("逐渐提升");
    10. list.add("...");
    11. // 使用ArrayList构造LinkedList
    12. LinkedList linkedList2 = new LinkedList<>(list);
    13. }
    14. }

    LinkedList的其他常用方法介绍

    方法解释
    boolean add(E e)尾插 e
    void add(int index, E element)将 e 插入到 index 位置
    boolean addAll(Collection c)尾插 c 中的元素
    E remove(int index)删除 index 位置元素
    boolean remove(Object o)删除遇到的第一个 o
    E get(int index)获取下标 index 位置元素
    E set(int index, E element)将下标 index 位置元素设置为 element
    void clear()清空
    boolean contains(Object o)判断 o 是否在线性表中
    int indexOf(Object o)返回第一个 o 所在下标
    int lastIndexOf(Object o)返回最后一个 o 的下标
    List subList(int fromIndex, int toIndex)截取部分 list
    1. public static void main(String[] args) {
    2. LinkedList list = new LinkedList<>();
    3. list.add(1); // add(elem): 表示尾插
    4. list.add(2);
    5. list.add(3);
    6. list.add(4);
    7. list.add(5);
    8. list.add(6);
    9. list.add(7);
    10. System.out.println(list.size());
    11. System.out.println(list);
    12. // 在起始位置插入0
    13. list.add(0, 0); // add(index, elem): 在index位置插入元素elem
    14. System.out.println(list);
    15. list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
    16. list.removeFirst(); // removeFirst(): 删除第一个元素
    17. list.removeLast(); // removeLast(): 删除最后元素
    18. list.remove(1); // remove(index): 删除index位置的元素
    19. System.out.println(list);
    20. // contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
    21. if (!list.contains(1)) {
    22. list.add(0, 1);
    23. }
    24. list.add(1);
    25. System.out.println(list);
    26. System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
    27. System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
    28. int elem = list.get(0); // get(index): 获取指定位置元素
    29. list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
    30. System.out.println(list);
    31. // subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
    32. List copy = list.subList(0, 3);
    33. System.out.println(list);
    34. System.out.println(copy);
    35. list.clear(); // 将list中元素清空
    36. System.out.println(list.size());
    37. }

    LinkedList的遍历

    1. public static void main(String[] args) {
    2. LinkedList list = new LinkedList<>();
    3. list.add(1); // add(elem): 表示尾插
    4. list.add(2);
    5. list.add(3);
    6. list.add(4);
    7. list.add(5);
    8. list.add(6);
    9. list.add(7);
    10. System.out.println(list.size());
    11. // foreach遍历
    12. for (int e:list) {
    13. System.out.print(e + " ");
    14. }
    15. System.out.println();
    16. // 使用迭代器遍历---正向遍历
    17. ListIterator it = list.listIterator();
    18. while(it.hasNext()){
    19. System.out.print(it.next()+ " ");
    20. }
    21. System.out.println();
    22. // 使用反向迭代器---反向遍历
    23. ListIterator rit = list.listIterator(list.size());
    24. while (rit.hasPrevious()){
    25. System.out.print(rit.previous() +" ");
    26. }
    27. System.out.println();
    28. }

    5.ArrayList和LinkedList的区别

    不同点ArrayListLinkedList
    存储空间上物理上一定连续逻辑上连接,但物理上不一定连接
    随机访问支持O(1)不支持:O(N)
    头插需要搬移元素,效率低O(N)只需修改引用的指向,时间复杂度为O(1)
    插入空间不够时需要扩容没有容量的概念
    应用场景元素搞笑存储+频繁访问任意位置插入和删除频繁

  • 相关阅读:
    eclipse配置tomcat详解(图文版)
    重要功能丨支持1688API接口接入一键跨境铺货及采购,解决跨境卖家货源烦恼!
    【数据结构】顺序表
    微信内置浏览器调试和调试微信内的H5页面汇总(持续更新...)
    whois人员信息python批处理读入与文本输出
    升级ios16后iphone无法识别SIM?一招解决这个问题!
    [附源码]计算机毕业设计Springboot电影推荐网站
    Linux操作系统~系统文件IO,什么是文件描述符fd?什么是vfs虚拟文件系统
    全局变量和局部变量(static,extern,volatile)
    神经网络硕士就业前景,神经网络就业怎么样
  • 原文地址:https://blog.csdn.net/qq_63918780/article/details/127548914