当前位置:   article > 正文

【数据结构】链表与LinkedList

【数据结构】链表与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<Integer> list = new LinkedList<>();

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

public LinkedList(Collection<? extends E> c)

栗子:

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

LinkedList的基本方法:

  1. public static void main(String[] args) {
  2. LinkedList<Integer> 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<Integer> 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<Integer> 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<Integer> it = list.listIterator();
  2. while(it.hasNext()) {
  3. System.out.println(it.next() + " ");
  4. }
  5. }

ArrayList与LinkedList的区别


声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号