当前位置:   article > 正文

Java集合类——LinkedList(单链表及双链表)_java有哪些类用了链表

java有哪些类用了链表

一,ArrayList的缺陷

1.空间浪费

在之前的博客中,我利用源码详细的讲解了ArrayList这个集合类(尤其是扩容机制),可以知道ArrayList的底层主要是一个动态的可变数组,容量满的时候需要进行1.5倍扩容。但是我们现在考虑这样一个问题,假设ArrayList底层数组的容量是100,我们需要存放101个元素时,当存放第101个元素时需要1.5倍扩容(扩容之后的容量是150),此时势必会造成49个存储空间的浪费!

2.时间开销大

ArrayList底层是一个数组,当我们对顺序表进行插入和删除时,需要移动大量的元素,大大提高了时间复杂度,所以可以看出ArrayList并不适用于进行大量插入和删除的操作。

针对上述ArrayList的两大缺陷,Java是否提供了其他的集合类或者数据结构来解决呢?

1.进行元素扩容时能否根据需求来进行扩容,需要多少空间就去申请多少空间;

2.在进行插入和删除的时候,可不可以不需要移动大量元素。

今天所要介绍的LinkedList集合类和链表的数据结构将很好的解决这两个问题!!!

二,链表

2.1 链表的概念

相对于顺序表,链表是一种在逻辑上连续,在存储上不一定连续的数据结构。其中链表每个元素之间的逻辑关系是通过引用来实现的。

链表中的每一个元素称为一个节点,每一个节点至少包含两类数据:

1.数据域(存放该节点的数据信息)

2.节点域(实现链表节点与节点之间的逻辑关系,对于单链表来说只需要存储一个next即可(存储下一个节点的地址),对于双链表来说则需要存储next和prev(分别存储下一个节点和前一个节点的地址))

2.2 链表的分类

1.单向或者双向

2.带头或者不带头

3.循环或者非循环

我们只需要重点掌握两种即可:

1.无头单向非循环链表(因为笔试的OJ题中经常会考);

2.无头双向链表(因为LinkedList的底层就是不带头的双链表)。 

三,顺序表和链表的区别

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

四,无头单向非循环链表的实现

定义一个MySingleList类来实现单链表的方法,用TestMySingleList类来测试MySingleList类的方法

  1. import java.util.List;
  2. public class MySingleList {
  3. static class ListNode {
  4. public int val;
  5. public ListNode next;
  6. public ListNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. ListNode head;//第一节点的引用,默认为null
  11. //打印链表
  12. public void display() {
  13. ListNode cur = this.head;
  14. while (cur != null) {
  15. System.out.print(cur.val + " ");
  16. cur = cur.next;
  17. }
  18. System.out.println();
  19. }
  20. //获取链表长度
  21. public int size() {
  22. ListNode cur = this.head;
  23. int count = 0;
  24. while (cur != null) {
  25. count++;
  26. cur = cur.next;
  27. }
  28. return count;
  29. }
  30. //头插法
  31. public void addFirst(int data) {
  32. ListNode node = new ListNode(data);
  33. node.next = this.head;
  34. this.head = node;
  35. }
  36. //尾插法
  37. public void addLast(int data) {
  38. ListNode node = new ListNode(data);
  39. ListNode cur = this.head;
  40. if (this.head == null) {
  41. this.head = node;
  42. } else {
  43. while (cur.next != null) {
  44. cur = cur.next;
  45. }
  46. //此时已经处于尾节点
  47. cur.next = node;
  48. }
  49. }
  50. //任意index位置插入(假设第一个节点的下表为0)
  51. public void add(int index, int data) {
  52. //判断index位置的合法性
  53. if (index < 0 || index > size()) {
  54. System.out.println("index位置不合法!");//也可以抛异常
  55. } else if (index == 0) {
  56. addFirst(data);
  57. } else if (index == size()) {
  58. addLast(data);
  59. } else {
  60. //1.找到所需插入index位置的前驱
  61. ListNode cur = findIndexPrev(index);
  62. //2.修改引用的指向
  63. ListNode node = new ListNode(data);
  64. node.next = cur.next;
  65. cur.next = node;
  66. }
  67. }
  68. private ListNode findIndexPrev(int index) {
  69. ListNode cur = this.head;
  70. while (index - 1 != 0) {
  71. cur = cur.next;
  72. index--;
  73. }
  74. return cur;
  75. }
  76. //删除第一次出现的关键字key
  77. public void remove(int key) {
  78. if (this.head == null) {
  79. System.out.println("链表为空!");
  80. return;
  81. }
  82. if (this.head.val == key) {
  83. this.head = this.head.next;
  84. return;
  85. }
  86. ListNode cur = findPrev(key);//找到所需删除关键字key的前驱
  87. if (cur == null) {
  88. System.out.println("没有关键字key!");
  89. } else {
  90. cur.next = cur.next.next;
  91. }
  92. }
  93. private ListNode findPrev(int key) {
  94. ListNode cur = this.head;
  95. while (cur.next != null) {
  96. if (cur.val == key) {
  97. return cur;
  98. }
  99. cur = cur.next;
  100. }
  101. return null;
  102. }
  103. //删除所有出现的关键字key
  104. public void removeAllKey(int key) {
  105. if (this.head == null) {
  106. System.out.println("链表为空!");
  107. return;
  108. }
  109. ListNode prev = this.head;
  110. ListNode cur = this.head.next;
  111. while (cur != null) {
  112. if (cur.val == key) {
  113. prev.next = cur.next;
  114. cur = cur.next;
  115. } else {
  116. prev = cur;
  117. cur = cur.next;
  118. }
  119. }
  120. if (this.head.val == key) {
  121. this.head = this.head.next;
  122. }
  123. }
  124. //清空链表
  125. public void clear() {
  126. this.head = null;//单链表只需要将指向第一个节点的引用指向null即可
  127. }
  128. }
  1. public class TestMySingleList {
  2. public static void main(String[] args) {
  3. MySingleList mySingleList = new MySingleList();
  4. mySingleList.addLast(1);
  5. mySingleList.addLast(2);
  6. mySingleList.addLast(3);
  7. mySingleList.addLast(4);
  8. mySingleList.addLast(5);
  9. mySingleList.display();
  10. mySingleList.addFirst(0);
  11. mySingleList.display();
  12. mySingleList.remove(0);
  13. mySingleList.addLast(1);
  14. mySingleList.addLast(1);
  15. mySingleList.addLast(1);
  16. mySingleList.removeAllKey(1);
  17. mySingleList.display();
  18. }
  19. }

 

五,无头双向非循环链表的实现

定义一个MyLinkedList类来实现单链表的方法,用TestMyLinkedList类来测试MyLinkedList类的方法

  1. public class MyLinkedList {
  2. static class ListNode {
  3. public int val;
  4. public ListNode prev;
  5. public ListNode next;
  6. public ListNode(int val) {
  7. this.val = val;
  8. }
  9. }
  10. ListNode head;//定义一个头引用,默认为null
  11. ListNode tail;//定义一个尾引用,默认为null
  12. //打印链表
  13. public void display() {
  14. ListNode cur = this.head;
  15. while (cur != null) {
  16. System.out.print(cur.val + " ");
  17. cur = cur.next;
  18. }
  19. System.out.println();
  20. }
  21. //获取链表长度
  22. public int size() {
  23. ListNode cur = this.head;
  24. int count = 0;
  25. while (cur != null) {
  26. count++;
  27. cur = cur.next;
  28. }
  29. return count;
  30. }
  31. //判断链表是否包含某个元素key
  32. public boolean contains(int key) {
  33. ListNode cur = this.head;
  34. while (cur != null) {
  35. if (cur.val == key) {
  36. return true;
  37. }
  38. cur = cur.next;
  39. }
  40. return false;
  41. }
  42. //头插法
  43. public void addFirst(int data) {
  44. ListNode node = new ListNode(data);
  45. if (this.head == null) {
  46. this.head = node;
  47. this.tail = node;
  48. } else {
  49. node.next = this.head;
  50. this.head.prev = node;
  51. this.head = node;
  52. }
  53. }
  54. //尾插法
  55. public void addLast(int data) {
  56. ListNode node = new ListNode(data);
  57. if (this.head == null) {
  58. this.head = node;
  59. this.tail = node;
  60. } else {
  61. this.tail.next = node;
  62. node.prev = this.tail;
  63. this.tail = node;
  64. }
  65. }
  66. //任意index位置插入(假设第一个节点的下标为0)
  67. public void add(int index, int data) {
  68. //判断index位置的合法性
  69. if (index < 0 || index > size()) {
  70. System.out.println("index位置不合法!");//也可以抛异常
  71. } else if (index == 0) {
  72. addFirst(data);
  73. } else if (index == size()) {
  74. addLast(data);
  75. } else {
  76. //1.找到index位置的节点
  77. ListNode cur = findIndexPrev(index);
  78. //2.修改指向的引用
  79. ListNode node = new ListNode(data);
  80. node.next = cur;
  81. node.prev = cur.prev;
  82. cur.prev.next = node;
  83. cur.prev = node;
  84. }
  85. }
  86. private ListNode findIndexPrev(int index) {
  87. ListNode cur = this.head;
  88. while (index != 0) {
  89. cur = cur.next;
  90. index--;
  91. }
  92. return cur;
  93. }
  94. //删除第一次出现的关键字key
  95. public void remove(int key) {
  96. if (this.head == null) {
  97. System.out.println("链表为空!");
  98. return;
  99. }
  100. ListNode cur = this.head;
  101. while (cur != null) {
  102. if (cur.val == key) {
  103. if (cur == this.head) {
  104. this.head = this.head.next;
  105. if (this.head != null) {
  106. this.head.prev = null;
  107. } else {
  108. this.tail = null;
  109. }
  110. } else {
  111. cur.prev.next = cur.next;
  112. if (cur.next != null) {
  113. cur.next.prev = cur.prev;
  114. } else {
  115. this.tail = cur.prev;
  116. this.tail.next = null;
  117. }
  118. }
  119. return;
  120. }
  121. cur = cur.next;
  122. }
  123. }
  124. //删除所有出现的关键字key
  125. public void removeAllKey(int key) {
  126. if (this.head == null) {
  127. System.out.println("链表为空!");
  128. return;
  129. }
  130. ListNode cur = this.head;
  131. while (cur != null) {
  132. if (cur.val == key) {
  133. if (cur == this.head) {
  134. this.head = this.head.next;
  135. if (this.head != null) {
  136. this.head.prev = null;
  137. } else {
  138. this.tail = null;
  139. }
  140. } else {
  141. cur.prev.next = cur.next;
  142. if (cur.next != null) {
  143. cur.next.prev = cur.prev;
  144. } else {
  145. this.tail = cur.prev;
  146. this.tail.next = null;
  147. }
  148. }
  149. }
  150. cur = cur.next;
  151. }
  152. }
  153. //清空链表
  154. public void clear() {
  155. ListNode cur = this.head;
  156. while (cur != null) {
  157. ListNode curNext = cur.next;
  158. cur.prev = null;
  159. cur.next = null;
  160. cur = curNext;
  161. }
  162. this.head = null;
  163. this.tail = null;
  164. }
  165. }
  1. public class TestMyLinkedList {
  2. public static void main(String[] args) {
  3. MyLinkedList myLinkedList = new MyLinkedList();
  4. myLinkedList.addLast(1);
  5. myLinkedList.addLast(2);
  6. myLinkedList.addLast(3);
  7. myLinkedList.addLast(4);
  8. myLinkedList.addLast(5);
  9. myLinkedList.addFirst(0);
  10. myLinkedList.display();
  11. myLinkedList.remove(0);
  12. myLinkedList.display();
  13. myLinkedList.add(2,10);
  14. myLinkedList.display();
  15. myLinkedList.addLast(1);
  16. myLinkedList.addLast(1);
  17. myLinkedList.removeAllKey(1);
  18. myLinkedList.display();
  19. }
  20. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/905228
推荐阅读
相关标签
  

闽ICP备14008679号