分享

详细介绍链表原理即应用(Java语言)...

 醉人说梦 2023-02-04 发布于广东

目录

一、简述

二、单链表的模拟实现

(1)打印链表 display 

(2)头插法插入元素addFirst(int data)

(3)尾插法插入元素addLast(int data)

(4)任意位置插入元素addIndex(int index,int data)

(5)查找是否包含关键字key是否在单链表当中contains(int key)

(6)删除第一次出现关键字为key的节点remove(int key)

(7)删除所有值为key的节点removeAllKey(int key)

(8)得到单链表的长度size()

(9)清空链表clear()

以上代码的总合:

三、双向链表的模拟实现

(1)打印链表 display 

(2)头插法插入元素addFirst(int data)

(3)尾插法插入元素addLast(int data)

(4)任意位置插入元素addIndex(int index,int data)

(5)查找是否包含关键字key是否在单链表当中contains(int key)

(6)删除第一次出现关键字为key的节点remove(int key)

(7)删除所有值为key的节点removeAllKey(int key)

(8)得到单链表的长度size()

(9)清空链表clear()

以上代码总合

四、LinkedList的使用

1、LinkedList的构造

2、LinkedList其他方法介绍(将以int类型 举例子)

(1)尾插

(2)插入到给定下标

(3)尾插链表c中的所有元素(c不受有影响)

(4)删除给定下标的元素

(5)获取给定下标的元素

(6)将给定下标的元素改成给定的元素

(7)清空链表

(8)判断给定元素是否在链表中

(9)返回第一个给定元素的下标

(10)返回最后一个给定元素的下标

(11)删除第一次出现的给定的元素

(12)截取一个链表的部分元素给另一个链表


一、简述

顺序表:ArrayList

链表:LinkedList

首先我们要知道为什么要学习LinkedList,在学习链表之前肯定学过ArrayList,因为ArrayList存在缺陷,ArrayList的缺陷刚好是LinkedList的长处。

ArrayList和LinkedList有哪些区别呢?

1.存储方式

ArrayList底层使用数组来存储元素,是一块连续的内存,在物理逻辑上是连续的

LinkedList在物理逻辑上不一定是连续的,在逻辑上是连续的

2.增删查改进行区别

(1)当程序运用于增加,删除元素的场景比较多的时候,建议使用LinkedList.只需要修改指向就可以.而ArrayList需要大量元素位置的变化,时间复杂度为O(n).

(2)当程序运用于查找,修改场景比较多的时候,建议使用ArrayList.

LinkedList虽然也可以给下标,但也需要进行查找.时间复杂度为O(n),ArrayList可以通过下表直接确定到那个位置

注:顺序存储结构会预先分配内存,可能会造成存储空间浪费的问题(先定义一个数组,数组的空间分配大了的话,会造成内存浪费,链表就没有这个问题)

链表分为:

单链表、双链表、带头和不带头节点的链表、循环链表

//图中0x~~表示的是地址,随便写的,不是真正的地址,只是便于理解,也不是真正地址的格式,

//真正地址的格式大概是这样的:0x0012FFB0。

单链表

双链表

带头和不带头节点的单链表、双链表

 

循环单链表

 循环双链表

二、单链表的模拟实现

为了理解链表的运行机制,我们先自己实现一个链表(链表Java底层已经实现好了,可也直接用),作为初学者建议先自己实现单链表。

我实现的这个单链表有一下几个功能:

(1)打印链表 display 

  1. //打印单链表
  2. public void display(){
  3. ListNode cur = head;
  4. while(cur != null){
  5. System.out.print(cur.val+" ");
  6. cur = cur.next;
  7. }
  8. System.out.println();
  9. }

(2)头插法插入元素addFirst(int data)

  1. //头插法
  2. public void addFirst(int data){
  3. ListNode node = new ListNode(data);
  4. if(this.head == null){
  5. node.next = head;
  6. }else{
  7. node.next = head;
  8. this.head = node;
  9. }
  10. }

(3)尾插法插入元素addLast(int data)

  1. //尾插法
  2. public void addLast(int data){
  3. ListNode node = new ListNode(data);
  4. if(head == null){
  5. this.head = node;
  6. }else{
  7. ListNode cur = head;
  8. while(cur.next != null){
  9. cur = cur.next;
  10. }
  11. cur.next = node;
  12. }
  13. }

(4)任意位置插入元素addIndex(int index,int data)

  1. //任意位置插入,第一个数据节点为0号下标
  2. public void addIndex(int index,int data){
  3. if(index > size() || index < 0){
  4. System.out.println("添加的节点不合法");
  5. }
  6. if(index == 0){
  7. addFirst(data);
  8. return;
  9. }
  10. else if(index == size()){
  11. addLast(data);
  12. return;
  13. }
  14. ListNode node = new ListNode(data);
  15. ListNode cur = Findcur(index);
  16. node.next = cur.next;
  17. cur.next = node;
  18. }
  19. //返回下标为index的节点
  20. private ListNode Findcur (int index){
  21. ListNode cur = this.head;
  22. while(index-1 != 0){
  23. cur = cur.next;
  24. index--;
  25. }
  26. return cur;
  27. }

(5)查找是否包含关键字key是否在单链表当中contains(int key)

  1. //查找是否包含关键字key是否在单链表当中
  2. public boolean contains(int key){
  3. ListNode cur = head;
  4. while(cur !=null)
  5. {
  6. if(cur.val == key){
  7. return true;
  8. }else{
  9. cur = cur.next;
  10. }
  11. }
  12. return false;
  13. }

(6)删除第一次出现关键字为key的节点remove(int key)

  1. //删除第一次出现关键字为key的节点
  2. public void remove(int key){
  3. if(key == head.val){
  4. head = head.next;
  5. return;
  6. }
  7. ListNode cur = FindIndexCur(key);
  8. if(cur == null){
  9. System.out.println("无该元素的节点");
  10. }else{
  11. ListNode chear = cur.next;
  12. cur.next = chear.next;
  13. }
  14. }
  15. //返回元素为key的节点
  16. private ListNode FindIndexCur (int key){
  17. ListNode cur = head;
  18. while(cur.next != null){
  19. if(cur.next.val == key){
  20. return cur;
  21. }
  22. cur = cur.next;
  23. }
  24. return cur.next;
  25. }

(7)删除所有值为key的节点removeAllKey(int key)

  1. //删除所有值为key的节点
  2. public void removeAllKey(int key){
  3. if(this.head == null){
  4. return;
  5. }
  6. ListNode cur = head.next;
  7. ListNode prev = head;
  8. while(cur != null) {
  9. if (cur.val == key) {
  10. prev.next = cur.next;
  11. cur = cur.next;
  12. } else {
  13. prev = cur;
  14. cur = cur.next;
  15. }
  16. }
  17. if(head.val == key){
  18. head = head.next;
  19. }
  20. }

(8)得到单链表的长度size()

  1. //得到单链表的长度
  2. public int size(){
  3. int count = 0;
  4. ListNode cur = head;
  5. while(cur != null){
  6. count++;
  7. cur = cur.next;
  8. }
  9. return count;
  10. }

(9)清空链表clear()

  1. //清空链表
  2. public void clear(){
  3. ListNode cur = head;
  4. ListNode curNode = null;
  5. while(cur != null){
  6. curNode = cur.next;
  7. cur.next = null;
  8. cur = curNode;
  9. }
  10. head = null;
  11. }

以上代码的总合:

  1. public class MySingleList {
  2. static class ListNode{//静态内部类
  3. public int val;//数值域
  4. public ListNode next;//存储下一个节点的地址
  5. //构造方法
  6. public ListNode(int val){
  7. this.val = val;
  8. }
  9. }
  10. public ListNode head;//代表链表的头接节点的引用
  11. /**
  12. * 这里只是简单地进行链表的构造
  13. */
  14. public void createList(){
  15. ListNode listNode1 = new ListNode(12);
  16. ListNode listNode2 = new ListNode(23);
  17. ListNode listNode3 = new ListNode(34);
  18. ListNode listNode4 = new ListNode(45);
  19. ListNode listNode5 = new ListNode(56);
  20. listNode1.next = listNode2;
  21. listNode2.next = listNode3;
  22. listNode3.next = listNode4;
  23. listNode4.next = listNode5;
  24. this.head = listNode1;
  25. }
  26. public void display(){
  27. ListNode cur = head;
  28. while(cur != null){
  29. System.out.print(cur.val+" ");
  30. cur = cur.next;
  31. }
  32. System.out.println();
  33. }
  34. //头插法
  35. public void addFirst(int data){
  36. ListNode node = new ListNode(data);
  37. if(this.head == null){
  38. node.next = head;
  39. }else{
  40. node.next = head;
  41. this.head = node;
  42. }
  43. }
  44. //尾插法
  45. public void addLast(int data){
  46. ListNode node = new ListNode(data);
  47. if(head == null){
  48. this.head = node;
  49. }else{
  50. ListNode cur = head;
  51. while(cur.next != null){
  52. cur = cur.next;
  53. }
  54. cur.next = node;
  55. }
  56. }
  57. //任意位置插入,第一个数据节点为0号下标
  58. public void addIndex(int index,int data){
  59. if(index > size() || index < 0){
  60. System.out.println("添加的节点不合法");
  61. }
  62. if(index == 0){
  63. addFirst(data);
  64. return;
  65. }
  66. else if(index == size()){
  67. addLast(data);
  68. return;
  69. }
  70. ListNode node = new ListNode(data);
  71. ListNode cur = Findcur(index);
  72. node.next = cur.next;
  73. cur.next = node;
  74. }
  75. private ListNode Findcur (int index){
  76. ListNode cur = this.head;
  77. while(index-1 != 0){
  78. cur = cur.next;
  79. index--;
  80. }
  81. return cur;
  82. }
  83. //查找是否包含关键字key是否在单链表当中
  84. public boolean contains(int key){
  85. ListNode cur = head;
  86. while(cur !=null)
  87. {
  88. if(cur.val == key){
  89. return true;
  90. }else{
  91. cur = cur.next;
  92. }
  93. }
  94. return false;
  95. }
  96. //删除第一次出现关键字为key的节点
  97. public void remove(int key){
  98. if(key == head.val){
  99. head = head.next;
  100. return;
  101. }
  102. ListNode cur = FindIndexCur(key);
  103. if(cur == null){
  104. System.out.println("无该元素的节点");
  105. }else{
  106. ListNode chear = cur.next;
  107. cur.next = chear.next;
  108. }
  109. }
  110. private ListNode FindIndexCur (int key){
  111. ListNode cur = head;
  112. while(cur.next != null){
  113. if(cur.next.val == key){
  114. return cur;
  115. }
  116. cur = cur.next;
  117. }
  118. return cur.next;
  119. }
  120. //删除所有值为key的节点
  121. public void removeAllKey(int key){
  122. if(this.head == null){
  123. return;
  124. }
  125. ListNode cur = head.next;
  126. ListNode prev = head;
  127. while(cur != null) {
  128. if (cur.val == key) {
  129. prev.next = cur.next;
  130. cur = cur.next;
  131. } else {
  132. prev = cur;
  133. cur = cur.next;
  134. }
  135. }
  136. if(head.val == key){
  137. head = head.next;
  138. }
  139. }
  140. //得到单链表的长度
  141. public int size(){
  142. int count = 0;
  143. ListNode cur = head;
  144. while(cur != null){
  145. count++;
  146. cur = cur.next;
  147. }
  148. return count;
  149. }
  150. //清空链表
  151. public void clear(){
  152. ListNode cur = head;
  153. ListNode curNode = null;
  154. while(cur != null){
  155. curNode = cur.next;
  156. cur.next = null;
  157. cur = curNode;
  158. }
  159. head = null;
  160. }
  161. }

在学习了单链表后,我们对链表也有了一定的认识,LinkedList的底层是一个双向链表,为了深入学习,深入理解,我们学习一下

三、双向链表的模拟实现

我要实现的双链表要有一下几个功能(这里模拟实现的是主要几个功能,LinkedList的功能众多)

(1)打印链表 display 

  1. //打印链表
  2. public void display(){
  3. ListNode l1 = head;
  4. while(l1!=null){
  5. System.out.println(l1.val);
  6. l1 = l1.next;
  7. }
  8. };

(2)头插法插入元素addFirst(int data)

  1. //头插法
  2. public void addFirst(int data){
  3. ListNode note = new ListNode();
  4. if(head == null){
  5. note.val = data;
  6. head = note;
  7. last = note;
  8. }else{
  9. note.val = data;
  10. note.next = head;
  11. head.prev = note;
  12. head = note;
  13. }
  14. };

(3)尾插法插入元素addLast(int data)

  1. //尾插法
  2. public void addLast(int data){
  3. ListNode note = new ListNode();
  4. if(head == null){
  5. note.val = data;
  6. head = note;
  7. last = note;
  8. }else{
  9. note.val = data;
  10. note.prev = last;
  11. last.next = note;
  12. last = note;
  13. }
  14. };

(4)任意位置插入元素addIndex(int index,int data)

  1. //任意位置插入,第一个数据节点为0号下标
  2. public void addIndex(int index,int data){
  3. if(index==0){
  4. addFirst(data);
  5. return;
  6. }
  7. if(index==size()){
  8. addLast(data);
  9. return;
  10. }
  11. if(index<0 || index>(size())){
  12. System.out.println("下标错误");
  13. return;
  14. }
  15. ListNode node = new ListNode(data);
  16. ListNode l1 = findList(index);
  17. node.next = l1;
  18. l1.prev.next = node;
  19. node.prev = l1.prev;
  20. l1.prev = node;
  21. };
  22. //返回给定下标的节点
  23. public ListNode findList(int index){
  24. ListNode l1 = head;
  25. while(index!=0){
  26. l1 = l1.next;
  27. index--;
  28. }
  29. return l1;
  30. }

(5)查找是否包含关键字key是否在单链表当中contains(int key)

  1. //查找是否包含关键字key是否在单链表当中
  2. public boolean contains(int key){
  3. ListNode l1 = head;
  4. while(l1!=null){
  5. if(l1.val == key){
  6. return true;
  7. }
  8. l1 = l1.next;
  9. }
  10. return false;
  11. };

(6)删除第一次出现关键字为key的节点remove(int key)

要注意是不是在第一个和最后一个,这两个需要特殊考虑

  1. //删除第一次出现关键字为key的节点
  2. public void remove(int key){
  3. //首先判断头节点是否为key
  4. if(head.val == key){
  5. head = head.next;
  6. head.prev = null;
  7. return;
  8. }
  9. //要是没有值为key的节点,则退出
  10. if(contains(key) == false){
  11. // System.out.println("无该关键字");
  12. return;
  13. }
  14. ListNode l1 = head;
  15. int jishu = 1;
  16. while(l1.val!=key){
  17. l1 = l1.next;
  18. jishu++;
  19. }
  20. if(jishu==size()){
  21. l1.prev.next = null;
  22. return;
  23. }
  24. l1.prev.next=l1.next;
  25. l1.next.prev = l1.prev;
  26. };

(7)删除所有值为key的节点removeAllKey(int key)

  1. //删除所有值为key的节点
  2. public void removeAllKey(int key){
  3. ListNode l1 = head;
  4. while(l1!=null){
  5. remove(key);
  6. l1 = l1.next;
  7. }
  8. };

(8)得到单链表的长度size()

  1. //得到单链表的长度
  2. public int size(){
  3. ListNode node = head;
  4. int len = 1;
  5. while(node.next!=null){
  6. len++;
  7. node = node.next;
  8. }
  9. return len;
  10. };

(9)清空链表clear()

  1. public void clear(){
  2. ListNode l1 = head.next;
  3. while(head!=null){
  4. head.next=null;
  5. head.prev=null;
  6. head = l1;
  7. if(l1.next!=null){
  8. l1 = l1.next;
  9. }
  10. }
  11. head = null;
  12. last = null;
  13. };

以上代码总合

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

以上是想让我们深入理解链表的工作原理,在我们平时使用链表的时候是不需要写这么多代码的,Java底层已经帮我们写好了,我们直接调用就可以了,那么LinkedList有哪些方法可以直接调用呢?LinkedList怎么使用?LinkedList有多香???

四、LinkedList的使用

1、LinkedList的构造

(1)构造一个存放int类型数据的链表

        LinkedList<Integer> linkedList = new LinkedList<>();

(2)构造一个存放char类型数据的链表

        LinkedList<Character> integerList2 = new LinkedList<>();

其他类型同理

注:list是线性表,linkedlist是链表,arraylist是顺序表,list是linkedlist和arraylist的父接口

写成:

        List<Integer> linkedList = new LinkedList<>();

也没有问题,只是linkedlist里边有的方法,可能list就没有,常见的都是有的。

2、LinkedList其他方法介绍(将以int类型 举例子)

LinkedList<Integer> integerList2 = new LinkedList<>();

(1)尾插

integerList2.add(1);

(2)插入到给定下标

integerList2.add(3,4);//将4插入到3下标

(3)尾插链表c中的所有元素(c不受有影响)

  1. LinkedList<Integer> integerList3 = new LinkedList<>();
  2. integerList3.add(111);
  3. integerList3.add(222);
  4. integerList3.add(333);
  5. integerList2.addAll(integerList3);

这样integerList2就在原来的基础上,尾插了integerList3的所有元素

(4)删除给定下标的元素

integerList2.add(3,4);//将4插入到3下标

(5)获取给定下标的元素

int a = integerList2.get(0);//a的值是链表integerList2的0下标的元素

(6)将给定下标的元素改成给定的元素

  1. integerList2.set(2,888);//将下标为2的元素改成888。

(7)清空链表

void clear();

(8)判断给定元素是否在链表中

boolean panduan = integerList2.contains(2);//判断元素2是否在链表中。

(9)返回第一个给定元素的下标

        System.out.println(integerList2.indexOf(222));

输出integerList2中第一个222的下标,如果没有,则输出-1。

(10)返回最后一个给定元素的下标

        System.out.println(integerList2.lastindexOf(222));

输出integerList2中最后一个222的下标,如果没有,则输出-1。

(11)删除第一次出现的给定的元素

integerList2.remove((Integer)222);

注:这个地方容易和删除给定下标的元素混淆,所删除的元素必须强制类型转化,否则电脑也无法识别要删除的是下标还是元素,Character也是。

(12)截取一个链表的部分元素给另一个链表

  1. List<Character> integerList2 = new LinkedList<>();
  2. integerList2 = integerList1.subList(0,4);

将链表integerList1中的0-4下标的元素给链表integerList2.

注:这个地方新的表必须是线性表而不是链表,integerList2必须是用List定义的

(13)打印链表

  1. LinkedList<Integer> linkedList = new LinkedList<>();
  2. linkedList.add(1);
  3. linkedList.add(2);
  4. linkedList.add(3);
  5. linkedList.add(4);
  6. linkedList.add(5);
  7. linkedList.add(6);

法1:直接打印

        System.out.println(linkedList);

打印效果:

 法2:for循环打印

  1. for (int x:linkedList) {
  2. System.out.println(x);
  3. }

打印效果:

法3:使用迭代器iterator

  1. Iterator<Integer> it = linkedList.iterator();
  2. while (it.hasNext()){
  3. //it.next()不仅会打印下一个,还会让it往后走一步
  4. System.out.print(it.next()+" ");
  5. }

打印效果:

 法4:与法3同理

  1. ListIterator<Integer> listIterator1 = linkedList.listIterator();
  2. while(listIterator1.hasNext()){
  3. System.out.print(listIterator1.next()+" ");
  4. }
  5. System.out.println();

(14)倒序打印

法1:利用迭代器进行倒序打印

  1. System.out.println("倒序打印");
  2. ListIterator<Integer> listIterator2 = linkedList.listIterator(linkedList.size());
  3. //要给他size,要不然他找不到最后一个节点
  4. while(listIterator2.hasPrevious()){
  5. System.out.print(listIterator2.previous()+" ");
  6. }
  7. System.out.println();

法2:利用递归进行打印

  1. //利用递归打印链表
  2. public void display2(ListNode head){
  3. if(head == null){
  4. return;
  5. }
  6. if(head.next == null){
  7. System.out.println(head.val);
  8. return;
  9. }
  10. display2(head.next);
  11. System.out.println(head.val);
  12. }

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多