当前位置:   article > 正文

Java数据结构-循环链表的设计与实现(头歌平台,详细注释)_循环链表java

循环链表java

目录

第1关:单循环链表的实现—链表的添加、遍历

任务描述

相关知识

单循环链表

添加操作

遍历循环链表

代码: 

第2关:单循环链表的实现—链表的删除

任务描述

相关知识

单循环链表删除操作

代码:  

第3关:双向循环链表的实现—链表的插入

任务描述

相关知识

双向循环链表

双向链表结点表示

双向循环链表图示

双向循环链表添加操作

代码:  

第4关:双向循环链表的实现—链表的删除

任务描述

相关知识

双向循环链表的删除操作

代码:  


单链表:由左到右指向,左指右,单向的

双链表:由左到右指向,左指右,又有由右到左指向,右指左,双向的

第1关:单循环链表的实现—链表的添加、遍历

任务描述

在操作单链表时,我们有时希望从单链表中的任一结点出发都能遍历整个链表,但对于单链表来说,只有从头结点开始才能扫描表中的全部结点。因此我们需要改动链表,使其首尾相接,这样就能满足我们的需求。

本关任务:完成带头结点的单循环链表的添加功能,遍历链表并输出。

相关知识
单循环链表

循环链表是一种首尾相接的链表。其特点是无需增加存储量,只需对表的链接方式稍作改变,即可使得表操作更加方便灵活。

在单链表中,将末尾结点的指针域null改为指向表头结点或开始结点,就得到单链形式的循环链表,称为单循环链表。

为使空表和非空表的处理方式一致,循环链表中也可以设置一个头结点。这样空循环链表仅有一个自成循环的头结点。 如下图:

添加操作

单循环链表的添加操作与普通的单链表操作类似,只需添加时注意处理尾结点,使其指向头结点。下面是添加操作示意图。

这里采用的是尾插法,即在链表的末尾添加结点。我们使tail指向链表尾结点,因此添加结点node时只需改动tail和新结点node之间的链接关系即可。由于有头结点,所以空表和非空表的处理方式是一样的。

 
 
  1. node.next=tail.next;
  2. tail.next=node;
  3. tail=node;
遍历循环链表

在循环链表中,链表的尾结点不是指向null。因此要输出链表元素时,其终止条件就不再是像非循环链表那样判断p==nullp.next==null,而是判别它们是否等于某一指定结点,如头结点head。如下图所示:

代码: 
  1. package step1;
  2. /**
  3. * Created by sykus on 2018/1/15.
  4. */
  5. public class MyCircleLinkedList {
  6. private Node head;//头结点, 不存数据
  7. private Node tail;//尾结点, 指向链表的最后一个节点
  8. private int size;
  9. public MyCircleLinkedList() {
  10. head = new Node(Integer.MIN_VALUE, null);
  11. head.next = head;
  12. tail = head;
  13. size = 0;
  14. }
  15. /**
  16. * 添加到链表尾部
  17. *
  18. * @param item
  19. */
  20. public void add(int item) {
  21. /********** Begin *********/
  22. //添加到链表尾部
  23. Node newNode = new Node(item, null);//创建新结点,并赋元素
  24. newNode.next=tail.next;//让新结点指向当前尾结点的下一个结点(即头结点)(即循环单链表的最后一个结点需要指向头结点)
  25. tail.next=newNode;//让尾结点指向新结点
  26. tail=newNode;//让新结点变为尾结点
  27. size++;//元素个数+1
  28. /********** End *********/
  29. }
  30. /**
  31. * 遍历链表并输出元素
  32. */
  33. public void output() {
  34. /********** Begin *********/
  35. //遍历链表并输出元素
  36. Node n = head;//保存头结点
  37. while (n.next!= head) {//因为循环单链表尾部指向头,所以不为头则代表还没遍历完元素
  38. n = n.next;//让n变为当前结点
  39. System.out.println(n.item);//输出每个被遍历的元素
  40. }
  41. /********** End *********/
  42. }
  43. public boolean isEmpty() {
  44. return head.next == head;
  45. }
  46. public int size() {
  47. return size;
  48. }
  49. //结点内部类
  50. private static class Node {
  51. int item;
  52. Node next;
  53. Node(int item, Node next) {
  54. this.item = item;
  55. this.next = next;
  56. }
  57. }
  58. }

以下是测试样例:

测试输入:2 0 5 1 1

预期输出:

  1. 2
  2. 0
  3. 5
  4. 1
  5. 1

第2关:单循环链表的实现—链表的删除

任务描述

在上一关,我们已经完成了单循环链表的添加、遍历功能,我们已经可以向表中添加数据并输出,现在我们将要实现其删除功能。

本关任务:删除循环链表中指定位置的结点,并返回其值。

相关知识
单循环链表删除操作

单循环链表的删除操作与普通的单链表操作基本类似,通过遍历找到要删除结点的直接前驱,然后改变前驱的链接情况。如下图:

这里删除的是尾结点,由于我们在构建单循环链表时是用tail指向尾结点的,所以在删除尾结点后需改变tail的指向,如果删除的不是尾结点则不需改变tail指向。

代码:  
  1. package step2;
  2. /**
  3. * Created by sykus on 2018/1/15.
  4. */
  5. public class MyCircleLinkedList {
  6. private Node head;//头结点, 不存数据
  7. private Node tail;//尾结点, 指向链表的最后一个节点
  8. private int size;
  9. public MyCircleLinkedList() {
  10. head = new Node(Integer.MIN_VALUE, null);
  11. head.next = head;
  12. tail = head;
  13. size = 0;
  14. }
  15. /**
  16. * 添加到链表尾部
  17. *
  18. * @param item
  19. */
  20. public void add(int item) {
  21. Node node = new Node(item, tail.next);
  22. tail.next = node;
  23. tail = node;
  24. ++size;
  25. }
  26. /**
  27. * 遍历链表并输出元素
  28. */
  29. public void output() {
  30. Node p = head;
  31. while (p.next != head) {
  32. p = p.next;
  33. System.out.println(p.item);
  34. }
  35. }
  36. /**
  37. * 删除从头结点开始的第index个结点
  38. * index从0开始
  39. *
  40. * @param index
  41. * @return
  42. */
  43. public int remove(int index) {
  44. checkPosIndex(index);
  45. /********** Begin *********/
  46. //删除从头结点开始的第index个结点
  47. //index从0开始
  48. Node n = head;//保存头结点
  49. for(int i=0;i<index;i++){//寻找要删除的结点的前一个结点
  50. n = n.next;//保存当前结点
  51. }
  52. Node del = n.next;//保存要删除的结点
  53. n.next = del.next;//将要删除的结点的前一个结点的指向,指向要删除的结点的下一个结点
  54. --size;//元素个数-1
  55. return del.item;//返回删除元素
  56. /********** End *********/
  57. }
  58. public boolean isEmpty() {
  59. return head.next == head;
  60. }
  61. public int size() {
  62. return size;
  63. }
  64. private void checkPosIndex(int index) {
  65. if (index < 0 || index >= size) {
  66. throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
  67. }
  68. }
  69. //结点内部类
  70. private static class Node {
  71. int item;
  72. Node next;
  73. Node(int item, Node next) {
  74. this.item = item;
  75. this.next = next;
  76. }
  77. }
  78. }

预期输入:2 0 5 1 1 预期输出:

  1. 0
  2. 2
  3. 5
  4. 1
  5. 1

第3关:双向循环链表的实现—链表的插入

任务描述

在单链表中,当我们要访问某一个结点的前驱结点时,要从头结点开始遍历;要删除链表中的一个结点时,仅给出该结点的指针还不行;在指定结点前插入一个新结点,同样需要从头开始遍历。对于这些问题,双向循环链表可以解决。

本关任务:实现双向循环链表的添加功能。

相关知识
双向循环链表

双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。

双向链表结点表示

双向链表中的结点由三个域组成,两个链接域一个数据域,如下图:

prev链接域指向结点的直接前驱,next链接域链接该结点的直接后继。

双向循环链表图示

下图是带头结点head的双向循环链表示意图:

双向循环链表添加操作

双向循环链表的添加操作示意图如下:

代码:  
  1. package step3;
  2. /**
  3. * Created by sykus on 2018/1/15.
  4. */
  5. public class MyDoubleLinkedList {
  6. private Node head;//头结点
  7. private Node tail;//指向链表的尾结点
  8. private int size;
  9. public MyDoubleLinkedList() {
  10. head = new Node(null, Integer.MIN_VALUE, null);
  11. head.next = head.prev = head;
  12. tail = head;
  13. size = 0;
  14. }
  15. /**
  16. * 添加元素到表尾
  17. *
  18. * @param item
  19. */
  20. public void add(int item) {
  21. /********** Begin *********/
  22. //添加元素到表尾
  23. Node newNode = new Node(null, item, null);//创建新结点,并赋元素
  24. tail.next = newNode;//尾结点指向新结点
  25. newNode.prev = tail;//新结点的前一个结点指向此时的尾结点(因为为双向链表)
  26. newNode.next = head;//新结点指向头结点(因为为循环链表)
  27. head.prev = newNode;//头结点的前一个结点指向新结点(同理)
  28. tail = newNode;//将尾结点变为新结点
  29. ++size;//元素+1
  30. /********** End *********/
  31. }
  32. /**
  33. * 打印双向链表
  34. *
  35. * @param flag true从左向右顺序打印, false从右向左顺序打印
  36. */
  37. public void printList(boolean flag) {
  38. Node f = head;
  39. if (flag) {//向右
  40. while (f.next != head) {
  41. f = f.next;
  42. System.out.print(f.item + " ");
  43. }
  44. } else {//向左
  45. while (f.prev != head) {
  46. f = f.prev;
  47. System.out.print(f.item + " ");
  48. }
  49. }
  50. }
  51. public int size() {
  52. return size;
  53. }
  54. //结点内部类
  55. private static class Node {
  56. int item;
  57. Node next;//直接后继引用
  58. Node prev;//直接前驱引用
  59. Node(Node prev, int item, Node next) {
  60. this.prev = prev;
  61. this.item = item;
  62. this.next = next;
  63. }
  64. }
  65. }

测试输入:1 2 3 4 5 预期输出:

  1. 1 2 3 4 5
  2. 5 4 3 2 1

第4关:双向循环链表的实现—链表的删除

任务描述

上一关实现了双向循环链表的添加功能,可以向表中添加元素,现在实现双向循环链表的删除功能。

本关任务:在上一关的基础上,实现双向循环链表的删除功能。

相关知识
双向循环链表的删除操作

双向循环链表的删除操作示意图如下:

代码:  
  1. package step4;
  2. /**
  3. * Created by sykus on 2018/1/15.
  4. */
  5. public class MyDoubleLinkedList {
  6. private Node head;//头结点
  7. private Node tail;//指向链表的尾结点
  8. private int size;
  9. public MyDoubleLinkedList() {
  10. head = new Node(null, Integer.MIN_VALUE, null);
  11. head.next = head.prev = head;
  12. tail = head;
  13. size = 0;
  14. }
  15. /**
  16. * 添加元素到表尾
  17. *
  18. * @param item
  19. */
  20. public void add(int item) {
  21. Node newNode = new Node(null, item, null);
  22. tail.next = newNode;
  23. newNode.prev = tail;
  24. newNode.next = head;
  25. head.prev = newNode;
  26. tail = newNode;
  27. ++size;
  28. }
  29. /**
  30. * 删除指定位置index出的结点,并返回其值
  31. *
  32. * @param index
  33. * @return
  34. */
  35. public int remove(int index) {
  36. checkPosIndex(index);//
  37. /********** Begin *********/
  38. Node n = head;//保存头结点
  39. for(int i=0;i<index;i++){//寻找要删除的结点的前一个结点
  40. n = n.next;//将当前结点保存
  41. }
  42. Node del=n.next;//保存要删除的结点
  43. Node nextnode=del.next;//保存要删除的结点的下一个
  44. n.next=nextnode;//将当前要删除的结点的前一个结点的指向,指向要删除的结点的下一个结点
  45. nextnode.prev=n;//将上述结点的前一个结点指向,n为要删除的结点的前一个结点(即双链表需要连接前面的结点)
  46. return del.item;//返回要删除的结点
  47. /********** End *********/
  48. }
  49. /**
  50. * 打印双向链表
  51. *
  52. * @param flag true从左向右顺序打印, false从右向左顺序打印
  53. */
  54. public void printList(boolean flag) {
  55. Node f = head;
  56. if (flag) {//向右
  57. while (f.next != head) {
  58. f = f.next;
  59. System.out.print(f.item + " ");
  60. }
  61. } else {//向左
  62. while (f.prev != head) {
  63. f = f.prev;
  64. System.out.print(f.item + " ");
  65. }
  66. }
  67. }
  68. public int size() {
  69. return size;
  70. }
  71. private void checkPosIndex(int index) {
  72. if (index < 0 || index >= size) {
  73. throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
  74. }
  75. }
  76. //结点内部类
  77. private static class Node {
  78. int item;
  79. Node next;//直接后继引用
  80. Node prev;//直接前驱引用
  81. Node(Node prev, int item, Node next) {
  82. this.prev = prev;
  83. this.item = item;
  84. this.next = next;
  85. }
  86. }
  87. }

预期输入:1 2 3 4 5 预期输出:

  1. 2 3 4 5
  2. 5 4 3 2

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

闽ICP备14008679号