• 【数据结构】链表与LinkedList


    作者主页:paper jie 的博客

    本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

    本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。

    其他专栏:《算法详解》《C语言》《javaSE》等

    内容分享:本期将会分享数据结构中的链表知识

    目录

    链表

    链表的概念与结构

    单向链表的模拟实现

    具体实现代码

    MyLinkedList

     indexillgality

    LinkedList

    LinkedList的模拟实现

    MyLinkedList

    Indexexception

    java中的LinkedList

    LinkedList的使用

    LinkedList的多种遍历

    ArrayList与LinkedList的区别


    链表

    链表的概念与结构

    链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。大家可以把它理解为现实中的绿皮火车

    这里要注意:

    链式在逻辑上是连续的,但是在物理上不一定是连续的

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

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

    链表中的结构是多样的,根据情况来使用,一般使用一下结构:

    单向或双向

    带头和不带头

    循环和非循环

    这些结构中,我们需要重点掌握两种:

    无头单向非循环链表:结构简单,一般不会单独来存数据,实际上更多的是作为其他数据结构的子结构,如哈希桶,图的邻接表等。

    无头双向链表:在我们java的集合框架中LinkedList低层实现的就是无头双向循环链表。

    单向链表的模拟实现

    下面是单向链表需要实现的一些基本功能:

    1. // 1、无头单向非循环链表实现
    2. public class SingleLinkedList {
    3. //头插法
    4. public void addFirst(int data){
    5. }
    6. //尾插法
    7. public void addLast(int data){
    8. }
    9. //任意位置插入,第一个数据节点为0号下标
    10. public void addIndex(int index,int data){
    11. }
    12. //查找是否包含关键字key是否在单链表当中
    13. public boolean contains(int key){
    14. return false;
    15. }
    16. //删除第一次出现关键字为key的节点
    17. public void remove(int key){
    18. }
    19. //删除所有值为key的节点
    20. public void removeAllKey(int key){
    21. }
    22. //得到单链表的长度
    23. public int size(){
    24. return -1;
    25. }
    26. public void clear() {
    27. }
    28. public void display() {}
    29. }

    具体实现代码

    MyLinkedList
    1. package myLinkedList;
    2. import sun.awt.image.ImageWatched;
    3. import java.util.List;
    4. /**
    5. * Created with IntelliJ IDEA.
    6. * Description:
    7. * User: sun杰
    8. * Date: 2023-09-14
    9. * Time: 10:38
    10. */
    11. public class MyLinkedList implements IList{
    12. static class LinkNode {
    13. public int value;
    14. public LinkNode next;
    15. public LinkNode(int data) {
    16. this.value = data;
    17. }
    18. }
    19. LinkNode head;
    20. public void createNode() {
    21. LinkNode linkNode1 = new LinkNode(12);
    22. LinkNode linkNode2 = new LinkNode(23);
    23. LinkNode linkNode3 = new LinkNode(34);
    24. LinkNode linkNode4 = new LinkNode(56);
    25. LinkNode linkNode5 = new LinkNode(78);
    26. linkNode1.next = linkNode2;
    27. linkNode2.next = linkNode3;
    28. linkNode3.next = linkNode4;
    29. linkNode4.next = linkNode5;
    30. this.head = linkNode1;
    31. }
    32. @Override
    33. public void addFirst(int data) {
    34. //实例化一个节点
    35. LinkNode firstNode = new LinkNode(data);
    36. if(this.head == null) {
    37. this.head = firstNode;
    38. return;
    39. }
    40. //将原第一个对象的地址给新节点的next,也就是将head给新next
    41. firstNode.next = this.head;
    42. //将新的对象的地址给head头
    43. this.head = firstNode;
    44. }
    45. @Override
    46. public void addLast(int data) {
    47. //实例化一个节点
    48. LinkNode lastNode = new LinkNode(data);
    49. //找到最后一个节点
    50. LinkNode cur = this.head;
    51. while(cur.next!= null) {
    52. cur = cur.next;
    53. }
    54. cur.next = lastNode;
    55. //将最后一个节点的next记录插入节点的地址
    56. }
    57. @Override
    58. public void addIndex(int index, int data) throws indexillgality {
    59. if(index < 0 || index > size()) {
    60. throw new indexillgality("index不合法");
    61. }
    62. LinkNode linkNode = new LinkNode(data);
    63. if(this.head == null) {
    64. addFirst(data);
    65. return;
    66. }
    67. if(size() == index ) {
    68. addLast(data);
    69. return;
    70. }
    71. LinkNode cur = this.head;
    72. int count = 0;
    73. while(count != index - 1) {
    74. cur = cur.next;
    75. count++;
    76. }
    77. linkNode.next = cur.next;
    78. cur.next = linkNode;
    79. }
    80. @Override
    81. public boolean contains(int key) {
    82. LinkNode cur = this.head;
    83. while(cur != null) {
    84. if(cur.value == key) {
    85. return true;
    86. }
    87. cur = cur.next;
    88. }
    89. return false;
    90. }
    91. @Override
    92. public void remove(int key) {
    93. if(this.head.value == key) {
    94. this.head = this.head.next;
    95. return ;
    96. }
    97. //找前驱
    98. LinkNode cur = findprev(key);
    99. //判断返回值
    100. if(cur != null) {
    101. //删除
    102. LinkNode del = cur.next;
    103. cur.next = del.next;
    104. //cur.next = cur.next.next;
    105. }
    106. }
    107. //找删除的前驱
    108. private LinkNode findprev(int key) {
    109. LinkNode cur = head;
    110. while(cur.next != null) {
    111. if(cur.next.value == key) {
    112. return cur;
    113. }
    114. cur = cur.next;
    115. }
    116. return null;
    117. }
    118. @Override
    119. public void removeAllKey(int key) {
    120. if(size() == 0) {
    121. return ;
    122. }
    123. if(head.value == key) {
    124. head = head.next;
    125. }
    126. LinkNode cur = head.next;
    127. LinkNode prev = head;
    128. while(cur != null) {
    129. if(cur.value == key) {
    130. prev.next = cur.next;
    131. }
    132. prev = cur;
    133. cur = cur.next;
    134. }
    135. }
    136. @Override
    137. public int size() {
    138. LinkNode cur = head;
    139. int count = 0;
    140. while(cur != null) {
    141. count++;
    142. cur = cur.next;
    143. }
    144. return count;
    145. }
    146. @Override
    147. public void display() {
    148. LinkNode x = head;
    149. while(x != null) {
    150. System.out.print(x.value + " ");
    151. x = x.next;
    152. }
    153. System.out.println();
    154. }
    155. @Override
    156. public void clear() {
    157. LinkNode cur = head;
    158. while(cur != null) {
    159. LinkNode curNext = cur.next;
    160. cur.next = null;
    161. cur = curNext;
    162. }
    163. head = null;
    164. }
    165. }
     indexillgality

    这时一个自定义异常

    1. package myLinkedList;
    2. /**
    3. * Created with IntelliJ IDEA.
    4. * Description:
    5. * User: sun杰
    6. * Date: 2023-09-14
    7. * Time: 12:55
    8. */
    9. public class indexillgality extends RuntimeException {
    10. public indexillgality(String message) {
    11. super(message);
    12. }
    13. }

    LinkedList

    LinkedList的模拟实现

    这相当于无头双向链表的实现,下面是它需要的基本功能:

    1. // 2、无头双向链表实现
    2. public class MyLinkedList {
    3. //头插法
    4. public void addFirst(int data){ }
    5. //尾插法
    6. public void addLast(int data){}
    7. //任意位置插入,第一个数据节点为0号下标
    8. public void addIndex(int index,int data){}
    9. //查找是否包含关键字key是否在单链表当中
    10. public boolean contains(int key){}
    11. //删除第一次出现关键字为key的节点
    12. public void remove(int key){}
    13. //删除所有值为key的节点
    14. public void removeAllKey(int key){}
    15. //得到单链表的长度
    16. public int size(){}
    17. public void display(){}
    18. public void clear(){}
    19. }

    MyLinkedList

    1. package myLinkedList;
    2. import java.util.List;
    3. /**
    4. * Created with IntelliJ IDEA.
    5. * Description:
    6. * User: sun杰
    7. * Date: 2023-09-20
    8. * Time: 18:49
    9. */
    10. public class MyLinkedList implements IList {
    11. //单个节点
    12. public static class ListNode {
    13. private int val;
    14. private ListNode prev;
    15. private ListNode next;
    16. public ListNode(int val) {
    17. this.val = val;
    18. }
    19. }
    20. ListNode head;
    21. ListNode last;
    22. @Override
    23. public void addFirst(int data) {
    24. ListNode cur = new ListNode(data);
    25. if(head == null) {
    26. cur.next = head;
    27. head = cur;
    28. last = cur;
    29. }else {
    30. cur.next = head;
    31. head.prev = cur;
    32. head = cur;
    33. }
    34. }
    35. @Override
    36. public void addLast(int data) {
    37. ListNode cur = new ListNode(data);
    38. if(head == null) {
    39. head = cur;
    40. last = cur;
    41. } else {
    42. last.next = cur;
    43. cur.prev = last;
    44. last = cur;
    45. }
    46. }
    47. @Override
    48. public void addIndex(int index, int data) throws Indexexception {
    49. ListNode cur = new ListNode(data);
    50. if(index < 0 || index > size()) {
    51. throw new Indexexception("下标越界");
    52. }
    53. //数组为空时
    54. if(head == null) {
    55. head = cur;
    56. last = cur;
    57. return ;
    58. }
    59. //数组只有一个节点的时候
    60. if(head.next == null || index == 0) {
    61. head.prev = cur;
    62. cur.next = head;
    63. head = cur;
    64. return;
    65. }
    66. if(index == size()) {
    67. last.next = cur;
    68. cur.prev = last;
    69. return ;
    70. }
    71. //找到对应下标的节点
    72. ListNode x = head;
    73. while(index != 0) {
    74. x = x.next;
    75. index--;
    76. }
    77. //头插法
    78. cur.next = x;
    79. cur.prev = x.prev;
    80. x.prev.next = cur;
    81. x.prev = cur;
    82. }
    83. @Override
    84. public boolean contains(int key) {
    85. ListNode cur = head;
    86. while(cur != null) {
    87. if(cur.val == key) {
    88. return true;
    89. }
    90. cur = cur.next;
    91. }
    92. return false;
    93. }
    94. @Override
    95. public void remove(int key) {
    96. if(head == null) {
    97. return;
    98. }
    99. ListNode cur = head;
    100. while(cur != null) {
    101. if(cur.val == key) {
    102. if(cur.next == null && cur.prev == null) {
    103. head = null;
    104. last = null;
    105. return;
    106. }else if(cur.next == null){
    107. cur.prev.next = null;
    108. last = cur.prev;
    109. return;
    110. }else if(cur.prev == null) {
    111. head = cur.next;
    112. cur.next.prev = null;
    113. return ;
    114. }else {
    115. ListNode frone = cur.prev;
    116. ListNode curnext = cur.next;
    117. frone.next = curnext;
    118. curnext.prev = frone;
    119. return ;
    120. }
    121. }
    122. cur = cur.next;
    123. }
    124. }
    125. @Override
    126. public void removeAllKey(int key) {
    127. if(head == null) {
    128. return;
    129. }
    130. ListNode cur = head;
    131. while(cur != null) {
    132. if(cur.val == key) {
    133. if(cur.next == null && cur.prev == null) {
    134. head = null;
    135. last = null;
    136. } else if(cur.next == null){
    137. cur.prev.next = null;
    138. last = cur.prev;
    139. }else if(cur.prev == null) {
    140. head = cur.next;
    141. cur.next.prev = null;
    142. }else {
    143. ListNode frone = cur.prev;
    144. ListNode curnext = cur.next;
    145. frone.next = curnext;
    146. curnext.prev = frone;
    147. }
    148. }
    149. cur = cur.next;
    150. }
    151. }
    152. @Override
    153. public int size() {
    154. int count = 0;
    155. ListNode cur = head;
    156. while(cur != null) {
    157. count++;
    158. cur = cur.next;
    159. }
    160. return count;
    161. }
    162. @Override
    163. public void display() {
    164. ListNode cur = head;
    165. while(cur != null) {
    166. System.out.print(cur.val + " ");
    167. cur = cur.next;
    168. }
    169. System.out.println();
    170. }
    171. @Override
    172. public void clear() {
    173. if(head == null) {
    174. return;
    175. }
    176. ListNode cur = head.next;
    177. while(cur != null) {
    178. head = null;
    179. head = cur;
    180. cur = cur.next;
    181. }
    182. head = null;
    183. }
    184. }

    Indexexception

    这也是一个自定义异常

    1. package myLinkedList;
    2. /**
    3. * Created with IntelliJ IDEA.
    4. * Description:
    5. * User: sun杰
    6. * Date: 2023-09-21
    7. * Time: 9:47
    8. */
    9. public class Indexexception extends RuntimeException{
    10. public Indexexception(String message) {
    11. super(message);
    12. }
    13. }

    java中的LinkedList

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

    在集合框架中,LinkedList也实现了List接口:

    注意:

    LinkedList实现了List接口

    LinkedList的底层使用的是双向链表

    Linked没有实现RandomAccess接口,因此LinkedList不支持随机访问

    LinkedList的随机位置插入和删除元素时效率较高,复杂度为O(1)

    LinkedList比较适合任意位置插入的场景

    LinkedList的使用

    LinkedList的构造:

    一般来说有两种方法:

    无参构造:

    List list = new LinkedList<>();

    使用其他集合容器中的元素构造List:

    public LinkedList(Collection c)

    栗子:

    1. public static void main(String[] args) {
    2. // 构造一个空的LinkedList
    3. List list1 = new LinkedList<>();
    4. List list2 = new java.util.ArrayList<>();
    5. list2.add("JavaSE");
    6. list2.add("JavaWeb");
    7. list2.add("JavaEE");
    8. // 使用ArrayList构造LinkedList
    9. List list3 = new LinkedList<>(list2);
    10. }

    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. 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的多种遍历

    foreach:

    1. public static void main(String[] args) {
    2. List list = new LinkedList<>();
    3. list.add(1);
    4. list.add(3);
    5. list.add(5);
    6. list.add(2);
    7. list.remove(1);
    8. for (int x:list) {
    9. System.out.print(x + " ");
    10. }
    11. }

    使用迭代器遍历:

    1. ListIterator it = list.listIterator();
    2. while(it.hasNext()) {
    3. System.out.println(it.next() + " ");
    4. }
    5. }

    ArrayList与LinkedList的区别


  • 相关阅读:
    19. 删除链表的倒数第 N 个结点 C++(快慢指针)
    (D卷,100分)- 最富裕的小家庭(Java & JS & Python & C)
    【行业动态】福建服装品牌如何完成差异化战略?
    13、文本框和单选框
    记录:移动设备软件开发(Android项目组织结构)
    【iOS 16升级必备】如何备份iPhone数据?
    动态分配的内存位置在哪里?
    Spring Boot-2.3.7.RELEASE整合activiti-6.0示例步骤
    Grafana离线安装部署以及插件安装
    MFC Windows 程序设计[152]之耍酷滑动滚动条
  • 原文地址:https://blog.csdn.net/paperjie/article/details/133240159