当前位置:   article > 正文

【Java数据结构】初始线性表之一:链表

【Java数据结构】初始线性表之一:链表

为什么要有链表

上一节我们描述了顺序表:【Java数据结构】初识线性表之一:顺序表-CSDN博客

并且进行了简单模拟实现。通过源码知道,ArrayList底层使用数组来存储元素。

由于其底层是一段连续空间,当在ArrayList任意位置插入或者删除元素时,就需要将后序元素整体往前或者往后搬移,时间复杂度为O(n),效率比较低,因此ArrayList不适合做任意位置插入和删除比较多的场景。因此:java集合中又引入了LinkedList,即链表结构。

链表

 链表的概念及结构

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

注意:

  • 链表的结构在逻辑上是连续的,但是在物理位置上不一定连续。
  • 节点一般都是从堆上申请出来的。
  • 从堆上申请空间是按一定策略来分配的,两次申请的空间可能会连续,也可能不连续。

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

  • 单向或者双向
  • 带头或者不带头
  • 循环或者非循环

本章节我们来描述其中两种:

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

模拟实现无头单向非循环链表

模拟实现无头单向非循环链表主要有以下的方法:

public class SingleLinkedList {
   //头插法
   public void addFirst(int data){
  }
   //尾插法
   public void addLast(int data){
  }
   //任意位置插入,第一个数据节点为0号下标
   public void addIndex(int index,int data){
  }
   //查找是否包含关键字key是否在单链表当中
   public boolean contains(int key){
       return false;
  }
   //删除第一次出现关键字为key的节点
   public void remove(int key){
  }

  //删除所有值为key的节点
   public void removeAllKey(int key){
  }
   //得到单链表的长度
   public int size(){
       return -1;
  }
   public void clear() {
  }
   
   public void display() {}
}

链表的插入:

 

链表的删除:

 模拟链表的代码实现:

  1. public class MySingLeList {
  2. static class ListNode{
  3. public int val;
  4. public ListNode next;
  5. public ListNode(int val) {
  6. this.val = val;
  7. }
  8. }
  9. public ListNode head;
  10. public void createList(){
  11. ListNode node1 = new ListNode(12);
  12. ListNode node2 = new ListNode(34);
  13. ListNode node3 = new ListNode(45);
  14. ListNode node4 = new ListNode(56);
  15. ListNode node5 = new ListNode(67);
  16. node1.next = node2;
  17. node2.next = node3;
  18. node3.next = node4;
  19. node4.next = node5;
  20. this.head = node1;
  21. }
  22. public void display(){
  23. ListNode tmp = this.head;
  24. while(!(tmp == null)){
  25. System.out.print(tmp.val+" ");
  26. tmp = tmp.next;
  27. }
  28. }
  29. public int size(){
  30. ListNode tmp = this.head;
  31. int count = 0;
  32. while(!(tmp == null)){
  33. count++;
  34. tmp = tmp.next;
  35. }
  36. return count;
  37. }
  38. public boolean contains(int findData){
  39. ListNode tmp = this.head;
  40. while(!(tmp == null)){
  41. if(tmp.val == findData){
  42. return true;
  43. }
  44. }
  45. return false;
  46. }
  47. public void addFirst(int data){
  48. ListNode node = new ListNode(data);
  49. node.next = this.head;
  50. this.head = node;
  51. }
  52. public void addLast(int data){
  53. ListNode node = new ListNode(data);
  54. ListNode tmp = this.head;
  55. if(tmp == null){
  56. this.head = node;
  57. return;
  58. }
  59. while(!(tmp.next == null)){
  60. tmp = tmp.next;
  61. }
  62. tmp.next = node;
  63. }
  64. public void addIndex(int pos,int data){
  65. ListNode node = new ListNode(data);
  66. ListNode tmp = this.head;
  67. if(pos == 0){
  68. node.next = tmp;
  69. this.head = node;
  70. return;
  71. } else if (pos == this.size()) {
  72. this.addLast(data);
  73. } else if (pos > this.size()) {
  74. System.out.println("输入的位置错误!");
  75. }else{
  76. for (int i = 0; i < pos -1; i++) {
  77. tmp = tmp.next;
  78. }
  79. node.next = tmp.next;
  80. tmp.next = node;
  81. }
  82. }
  83. public void remove(int data){
  84. int flag = 1;
  85. ListNode tmp = this.head;
  86. if(this.head.val == data){
  87. this.head = this.head.next;
  88. return;
  89. }
  90. while(tmp.next != null){
  91. if(tmp.next.val== data){
  92. flag = 0;
  93. break;
  94. }
  95. tmp = tmp.next;
  96. }
  97. if(flag == 0){
  98. tmp.next = tmp.next.next;
  99. }else{
  100. System.out.println("链表中没有该元素!");
  101. }
  102. }
  103. public void removeAll(int data){
  104. ListNode prv = this.head;
  105. ListNode tmp = this.head.next;
  106. while(tmp != null){
  107. if(tmp.val == data){
  108. prv.next = tmp.next;
  109. tmp = tmp.next;
  110. }else{
  111. prv = tmp;
  112. tmp = tmp.next;
  113. }
  114. }
  115. if(this.head.val == data){
  116. this.head = this.head.next;
  117. }
  118. }
  119. public void clear(){
  120. this.head = null;
  121. }
  122. }

模拟实现无头双向链表

无头双向链表主要有以下的方法:

public class MyLinkedList {
  //头插法
   public void addFirst(int data){ }
   //尾插法
   public void addLast(int data){}
   //任意位置插入,第一个数据节点为0号下标
   public void addIndex(int index,int data){}
   //查找是否包含关键字key是否在单链表当中
   public boolean contains(int key){}
   //删除第一次出现关键字为key的节点
   public void remove(int key){}
   //删除所有值为key的节点
   public void removeAllKey(int key){}
   //得到单链表的长度
   public int size(){}
   public void display(){}
   public void clear(){}
}

双向链表的插入:

 

双向链表的删除:

 

 模拟代码实现:

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

LinkedList 的使用

什么是LinkedList

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

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

注意 :

  • 1. LinkedList实现了List接口
  • 2. LinkedList的底层使用了双向链表
  • 3. LinkedList没有实现RandomAccess接口,因此LinkedList不支持随机访问 
  • 4. LinkedList的任意位置插入和删除元素时效率比较高,时间复杂度为O(1)
  • 5. LinkedList比较适合任意位置插入的场景

LinkedList 的构造方法

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list1 = new LinkedList<>();//无参构造
  4. List<Integer> list2 = new LinkedList<>();
  5. list2.add(1);
  6. list2.add(2);
  7. list2.add(3);
  8. List<Integer> list3 = new LinkedList<>(list2);//使用其他集合容器中元素构造List
  9. list3.add(4);
  10. System.out.println(list3);
  11. }
  12. }

LinkedList 常用方法介绍

插入节点

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list1 = new LinkedList<>();
  4. list1.add(5);
  5. list1.add(6);
  6. List<Integer> list2 = new LinkedList<>();
  7. list2.add(1);//尾插
  8. list2.add(2);
  9. list2.add(3);
  10. list2.add(1,4);//在指定位置插入节点
  11. list2.addAll(list1);//尾插其他容器中的所有节点
  12. System.out.println(list2);
  13. }
  14. }

删除节点:

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list2 = new LinkedList<>();
  4. list2.add(1);
  5. list2.add(2);
  6. list2.add(3);
  7. list2.remove(1);//删除指定位置的节点
  8. list2.remove(new Integer(3));//删除指定元素的节点
  9. System.out.println(list2);
  10. }
  11. }

获取指定位置的元素:

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list2 = new LinkedList<>();
  4. list2.add(1);
  5. list2.add(2);
  6. list2.add(3);
  7. System.out.println(list2.get(1));
  8. }
  9. }

更新指定位置的元素:

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list2 = new LinkedList<>();
  4. list2.add(1);
  5. list2.add(2);
  6. list2.add(3);
  7. list2.set(1,4);
  8. System.out.println(list2);
  9. }
  10. }

判断指定元素是否在链表中:

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list2 = new LinkedList<>();
  4. list2.add(1);
  5. list2.add(2);
  6. list2.add(3);
  7. System.out.println(list2.contains(new Integer(2)));
  8. }
  9. }

截取部分 list

  1. public class Main {
  2. public static void main(String[] args) {
  3. List<Integer> list2 = new LinkedList<>();
  4. list2.add(1);
  5. list2.add(2);
  6. list2.add(3);
  7. System.out.println(list2.subList(0,2));
  8. }
  9. }

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

闽ICP备14008679号