赞
踩
目录
单链表:由左到右指向,左指右,单向的
双链表:由左到右指向,左指右,又有由右到左指向,右指左,双向的
任务描述
在操作单链表时,我们有时希望从单链表中的任一结点出发都能遍历整个链表,但对于单链表来说,只有从头结点开始才能扫描表中的全部结点。因此我们需要改动链表,使其首尾相接,这样就能满足我们的需求。
本关任务:完成带头结点的单循环链表的添加功能,遍历链表并输出。
相关知识
单循环链表
循环链表是一种首尾相接的链表。其特点是无需增加存储量,只需对表的链接方式稍作改变,即可使得表操作更加方便灵活。
在单链表中,将末尾结点的指针域
null
改为指向表头结点或开始结点,就得到单链形式的循环链表,称为单循环链表。为使空表和非空表的处理方式一致,循环链表中也可以设置一个头结点。这样空循环链表仅有一个自成循环的头结点。 如下图:
添加操作
单循环链表的添加操作与普通的单链表操作类似,只需添加时注意处理尾结点,使其指向头结点。下面是添加操作示意图。
这里采用的是尾插法,即在链表的末尾添加结点。我们使
tail
指向链表尾结点,因此添加结点node
时只需改动tail
和新结点node
之间的链接关系即可。由于有头结点,所以空表和非空表的处理方式是一样的。
node.next=tail.next;
tail.next=node;
tail=node;
遍历循环链表
在循环链表中,链表的尾结点不是指向
null
。因此要输出链表元素时,其终止条件就不再是像非循环链表那样判断p==null
或p.next==null
,而是判别它们是否等于某一指定结点,如头结点head
。如下图所示:
- package step1;
-
- /**
- * Created by sykus on 2018/1/15.
- */
- public class MyCircleLinkedList {
- private Node head;//头结点, 不存数据
- private Node tail;//尾结点, 指向链表的最后一个节点
- private int size;
-
- public MyCircleLinkedList() {
- head = new Node(Integer.MIN_VALUE, null);
- head.next = head;
- tail = head;
- size = 0;
- }
-
- /**
- * 添加到链表尾部
- *
- * @param item
- */
- public void add(int item) {
- /********** Begin *********/
- //添加到链表尾部
- Node newNode = new Node(item, null);//创建新结点,并赋元素
- newNode.next=tail.next;//让新结点指向当前尾结点的下一个结点(即头结点)(即循环单链表的最后一个结点需要指向头结点)
- tail.next=newNode;//让尾结点指向新结点
- tail=newNode;//让新结点变为尾结点
- size++;//元素个数+1
-
- /********** End *********/
- }
-
- /**
- * 遍历链表并输出元素
- */
- public void output() {
- /********** Begin *********/
- //遍历链表并输出元素
- Node n = head;//保存头结点
- while (n.next!= head) {//因为循环单链表尾部指向头,所以不为头则代表还没遍历完元素
- n = n.next;//让n变为当前结点
- System.out.println(n.item);//输出每个被遍历的元素
- }
- /********** End *********/
- }
-
- public boolean isEmpty() {
- return head.next == head;
- }
-
- public int size() {
- return size;
- }
-
- //结点内部类
- private static class Node {
- int item;
- Node next;
-
- Node(int item, Node next) {
- this.item = item;
- this.next = next;
- }
- }
- }

以下是测试样例:
测试输入:
2 0 5 1 1
预期输出:
2
0
5
1
1
任务描述
在上一关,我们已经完成了单循环链表的添加、遍历功能,我们已经可以向表中添加数据并输出,现在我们将要实现其删除功能。
本关任务:删除循环链表中指定位置的结点,并返回其值。
相关知识
单循环链表删除操作
单循环链表的删除操作与普通的单链表操作基本类似,通过遍历找到要删除结点的直接前驱,然后改变前驱的链接情况。如下图:
这里删除的是尾结点,由于我们在构建单循环链表时是用
tail
指向尾结点的,所以在删除尾结点后需改变tail
的指向,如果删除的不是尾结点则不需改变tail
指向。
- package step2;
-
- /**
- * Created by sykus on 2018/1/15.
- */
- public class MyCircleLinkedList {
- private Node head;//头结点, 不存数据
- private Node tail;//尾结点, 指向链表的最后一个节点
- private int size;
-
- public MyCircleLinkedList() {
- head = new Node(Integer.MIN_VALUE, null);
- head.next = head;
- tail = head;
- size = 0;
- }
-
- /**
- * 添加到链表尾部
- *
- * @param item
- */
- public void add(int item) {
- Node node = new Node(item, tail.next);
- tail.next = node;
- tail = node;
- ++size;
-
- }
-
- /**
- * 遍历链表并输出元素
- */
- public void output() {
- Node p = head;
- while (p.next != head) {
- p = p.next;
- System.out.println(p.item);
- }
- }
-
-
- /**
- * 删除从头结点开始的第index个结点
- * index从0开始
- *
- * @param index
- * @return
- */
- public int remove(int index) {
- checkPosIndex(index);
-
- /********** Begin *********/
- //删除从头结点开始的第index个结点
- //index从0开始
- Node n = head;//保存头结点
- for(int i=0;i<index;i++){//寻找要删除的结点的前一个结点
- n = n.next;//保存当前结点
- }
- Node del = n.next;//保存要删除的结点
- n.next = del.next;//将要删除的结点的前一个结点的指向,指向要删除的结点的下一个结点
- --size;//元素个数-1
- return del.item;//返回删除元素
- /********** End *********/
- }
-
- public boolean isEmpty() {
- return head.next == head;
- }
-
- public int size() {
- return size;
- }
-
- private void checkPosIndex(int index) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
- }
- }
-
- //结点内部类
- private static class Node {
- int item;
- Node next;
-
- Node(int item, Node next) {
- this.item = item;
- this.next = next;
- }
- }
- }
-

预期输入:
2 0 5 1 1
预期输出:
0
2
5
1
1
任务描述
在单链表中,当我们要访问某一个结点的前驱结点时,要从头结点开始遍历;要删除链表中的一个结点时,仅给出该结点的指针还不行;在指定结点前插入一个新结点,同样需要从头开始遍历。对于这些问题,双向循环链表可以解决。
本关任务:实现双向循环链表的添加功能。
相关知识
双向循环链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
双向链表结点表示
双向链表中的结点由三个域组成,两个链接域一个数据域,如下图:
prev
链接域指向结点的直接前驱,next
链接域链接该结点的直接后继。双向循环链表图示
下图是带头结点
head
的双向循环链表示意图:
双向循环链表添加操作
双向循环链表的添加操作示意图如下:
- package step3;
-
- /**
- * Created by sykus on 2018/1/15.
- */
- public class MyDoubleLinkedList {
-
- private Node head;//头结点
- private Node tail;//指向链表的尾结点
- private int size;
-
- public MyDoubleLinkedList() {
- head = new Node(null, Integer.MIN_VALUE, null);
- head.next = head.prev = head;
- tail = head;
- size = 0;
- }
-
- /**
- * 添加元素到表尾
- *
- * @param item
- */
- public void add(int item) {
-
- /********** Begin *********/
- //添加元素到表尾
- Node newNode = new Node(null, item, null);//创建新结点,并赋元素
- tail.next = newNode;//尾结点指向新结点
- newNode.prev = tail;//新结点的前一个结点指向此时的尾结点(因为为双向链表)
- newNode.next = head;//新结点指向头结点(因为为循环链表)
- head.prev = newNode;//头结点的前一个结点指向新结点(同理)
- tail = newNode;//将尾结点变为新结点
- ++size;//元素+1
- /********** End *********/
-
- }
-
- /**
- * 打印双向链表
- *
- * @param flag true从左向右顺序打印, false从右向左顺序打印
- */
- public void printList(boolean flag) {
- Node f = head;
- if (flag) {//向右
- while (f.next != head) {
- f = f.next;
- System.out.print(f.item + " ");
- }
- } else {//向左
- while (f.prev != head) {
- f = f.prev;
- System.out.print(f.item + " ");
- }
- }
- }
-
- public int size() {
- return size;
- }
-
- //结点内部类
- private static class Node {
- int item;
- Node next;//直接后继引用
- Node prev;//直接前驱引用
-
- Node(Node prev, int item, Node next) {
- this.prev = prev;
- this.item = item;
- this.next = next;
- }
- }
- }

测试输入:
1 2 3 4 5
预期输出:
1 2 3 4 5
5 4 3 2 1
任务描述
上一关实现了双向循环链表的添加功能,可以向表中添加元素,现在实现双向循环链表的删除功能。
本关任务:在上一关的基础上,实现双向循环链表的删除功能。
相关知识
双向循环链表的删除操作
双向循环链表的删除操作示意图如下:
- package step4;
-
- /**
- * Created by sykus on 2018/1/15.
- */
- public class MyDoubleLinkedList {
-
- private Node head;//头结点
- private Node tail;//指向链表的尾结点
- private int size;
-
- public MyDoubleLinkedList() {
- head = new Node(null, Integer.MIN_VALUE, null);
- head.next = head.prev = head;
- tail = head;
- size = 0;
- }
-
- /**
- * 添加元素到表尾
- *
- * @param item
- */
- public void add(int item) {
- Node newNode = new Node(null, item, null);
- tail.next = newNode;
- newNode.prev = tail;
- newNode.next = head;
- head.prev = newNode;
- tail = newNode;
- ++size;
- }
-
- /**
- * 删除指定位置index出的结点,并返回其值
- *
- * @param index
- * @return
- */
- public int remove(int index) {
- checkPosIndex(index);//
-
- /********** Begin *********/
- Node n = head;//保存头结点
- for(int i=0;i<index;i++){//寻找要删除的结点的前一个结点
- n = n.next;//将当前结点保存
- }
- Node del=n.next;//保存要删除的结点
- Node nextnode=del.next;//保存要删除的结点的下一个
- n.next=nextnode;//将当前要删除的结点的前一个结点的指向,指向要删除的结点的下一个结点
- nextnode.prev=n;//将上述结点的前一个结点指向,n为要删除的结点的前一个结点(即双链表需要连接前面的结点)
- return del.item;//返回要删除的结点
- /********** End *********/
- }
-
- /**
- * 打印双向链表
- *
- * @param flag true从左向右顺序打印, false从右向左顺序打印
- */
- public void printList(boolean flag) {
- Node f = head;
- if (flag) {//向右
- while (f.next != head) {
- f = f.next;
- System.out.print(f.item + " ");
- }
- } else {//向左
- while (f.prev != head) {
- f = f.prev;
- System.out.print(f.item + " ");
- }
- }
- }
-
- public int size() {
- return size;
- }
-
- private void checkPosIndex(int index) {
- if (index < 0 || index >= size) {
- throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
- }
- }
-
- //结点内部类
- private static class Node {
- int item;
- Node next;//直接后继引用
- Node prev;//直接前驱引用
-
- Node(Node prev, int item, Node next) {
- this.prev = prev;
- this.item = item;
- this.next = next;
- }
- }
- }

预期输入:
1 2 3 4 5
预期输出:
2 3 4 5
5 4 3 2
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。