一、链表介绍
链表内存中的状态以及小结
二、单链表的应用实例
完整代码:
SingleLinkedListDemo.java
创建节点以及管理链表类
说明:需要创建两个类,一个是节点类,另一个是管理链表的类
① 节点类中需要id、name、sickname以及节点类(用于指向下一个节点)
② 管理链表中的类 首先创建一个头节点(里面没有数据,并且保持不变),包含方法有:添加方法(插入到链表尾部),遍历链表。
如下:
//创建管理链表的类
class SingleLinkedList{
//先初始化一个头节点, 头节点不要动, 不存放具体的数据
HeroNode head = new HeroNode(0, "", "");
...其他等方法
}
//创建节点
class HeroNode{
public int id;
public String name;
public String nickname;
public HeroNode next;
//创建一个构造器,存放id、name、nickname
public HeroNode(int id, String name, String nickname) {
super();
this.id = id;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode [id=" + id + ", name=" + name + ", nickname=" + nickname + "]";
}
}
第一种添加方式:添加到链表尾部(包含遍历)
说明:下面是添加节点到链表尾部的方法以及遍历链表的方法。
package LinkedList;
//创建管理链表的类
class SingleLinkedList{
//先初始化一个头节点, 头节点不要动, 不存放具体的数据
HeroNode head = new HeroNode(0, "", "");
//进行添加节点到末尾
public void addNode(HeroNode node) {
//首先使用一个临时变量代替head
HeroNode temp = head;
//寻找到链表的最底部
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
//插入node节点到最底部
temp.next = node;
}
//遍历链表
public void showList() {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空,无法进行遍历数据!");
return;
}
//使用一个临时变量获取到链表下一个节点
HeroNode temp = head.next;
while(true) {
if(temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
}
进行测试:
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作
list.addNode(node3);
list.addNode(node1);
list.addNode(node2);
//不能进行重复添加相同的节点,可能会发生死循环
//list.addNode(node3);
list.addNode(node4);
//进行遍历链表
list.showList();
}
}
运行结果:
第二种添加方式:根据id属性排序添加
说明:addNodeById()是根据id号链式添加的方法,根据id号从小到大进行添加。
① 首先需要一个辅助节点temp也就是进行插入节点位置的前一个节点。
② 每次插入相当于遍历链表,从中找出与id位置相匹配的位置插入,在进行遍历判断过程中,会有三种情况,
第一种是没有找到插入中间位置,插入到尾部,使用temp.next==null判断。
第二种是插入id与原有链表中的id相同,提示无法插入,使用temp.next.id==node.id。
第三种是找到在链表中插入的位置,使用temp.next.id>node.id来判断
插入过程说明:
插入只有两种情况也就是插入到尾部,另一种是插入到链表中,temp代表的插入位置之前的一个节点,那么首先得先将temp的next值赋给node的next也就是node.next = temp.next,
(注意不能直接将node作为temp的next值,这样的话原先temp的next值就会被覆盖从而丢失)
接着将node值赋给temp的next位置,temp.next = node。
flag为true实际代表着链表无法插入id相同的情况,为false代表可以插入,如插入到尾部或者链表中间。
//创建管理链表的类
class SingleLinkedList{
...
public void addNodeById(HeroNode node) {
//temp就代表待插入节点的前一个节点
HeroNode temp = head;
//这个flag代表是否有相同id值,true表示不能插入,false表示能插入
boolean flag = false;
while(true) {
//当temp节点的下一个节点为null,说明在尾部了,跳出循坏在尾部添加
if(temp.next == null) {
break;
}
//当temp节点下一个节点的id与待插入节点的id相同,flag设置为true,表明不能插入
if(temp.next.id == node.id) {
flag = true;
break;
}
//当temp节点下一个节点的id大于待插入节点id,跳出循环,将node节点插入到temp之后
if(temp.next.id > node.id) {
break;
}
//方便进行遍历
temp = temp.next;
}
if(flag) {
System.out.println("插入的节点id已存在,无法插入");
}else {
//首先插入节点的next先获取到temp的next节点
node.next = temp.next;
//再将node节点插入到temp节点之前
temp.next = node;
}
}
测试过程:
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
list.addNodeById(node1);
//进行遍历链表
System.out.println("遍历结果:");
list.showList();
}
}
运行结果:
修改节点方法(根据id)
说明:**依旧使用一个辅助节点temp,并且进行遍历修改
① 首先判断是否为空链表,是的话直接退出。
② **在遍历过程中,有两种情况,一种是没有遍历到结尾没有找到修改位置,第二种情况,找到对应id位置进行修改,这里使用flag(true表示能够修改,false表示不能修改)
//创建管理链表的类
class SingleLinkedList{
...
// 修改节点,根据id编号进行修改
public void updateNode(HeroNode newnode) {
// 创建一个辅助节点,令temp为head的next
HeroNode temp = head.next;
// 判断是否为空链表情况
if (temp == null) {
System.out.println("链表为空,无法进行修改!");
return;
}
// true表示能够修改, false表示不能修改
boolean flag = false;
while (true) {
// 表示到达链表末尾,无法进行插入
if (temp == null) {
break;
}
// 找到对应id相同的英雄
if (temp.id == newnode.id) {
flag = true;// 表示能够进行修改
break;
}
// 进行遍历使用
temp = temp.next;
}
if (flag) {
temp.name = newnode.name;
temp.nickname = newnode.nickname;
System.out.printf("修改id【%d】成功!\n",newnode.id);
} else {
System.out.println("链表中无法找到对应id英雄,修改失败!");
}
}
}
测试过程:**
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
//进行遍历链表
System.out.println("修改前遍历结果:");
list.showList();
//进行修改操作
list.updateNode(new HeroNode(1, "小丁", "青龙偃月刀"));
//list.updateNode(new HeroNode(6, "小丁", "青龙偃月刀"));
System.out.printf("\n修改后遍历结果:\n");
list.showList();
}
}
运行结果:
删除节点方法
说明:想要删除节点,需要找到删除节点的前一个节点,再进行删除即可。
class SingleLinkedList{
...
//根据id来删除节点
public void delNode(int id) {
if(head.next == null) {
System.out.println("链表为空,无法删除!");
return;
}
HeroNode temp = head;
//true用来表示找到删除的节点,false表示未找到删除的节点
boolean flag = false;
while(true) {
//到链表末尾没有找到对应节点
if(temp.next == null) {
break;
}
//找到temp的下一节点为对应删除的节点
if(temp.next.id == id) {
flag = true;
break;
}
//进行遍历
temp = temp.next;
}
if(flag) {
temp.next = temp.next.next;
System.out.printf("成功删除id【%d】",id);
System.out.println();
}else {
System.out.printf("链表中未找到对应id【%d】节点,删除失败!",id);
System.out.println();
}
}
}
测试运行:
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
//进行遍历链表
System.out.println("修改前遍历结果:");
list.showList();
//进行删除操作
list.delNode(5);//删除链表中不存在的情况
list.delNode(1);//删除链表中的节点情况
list.delNode(2);
list.delNode(3);
list.delNode(4);
list.delNode(2);//空链表时删除情况
System.out.printf("\n修改后遍历结果:\n");
list.showList();
}
}
运行结果:
三、单链表的常见面试题
① 求单链表中有效节点的个数
public class SingleLinkedListDemo {
//获取链表中的个数
public static int getLinedListSize(HeroNode head) {
//判断链表为空的情况
if(head.next == null) {
return 0;
}
//使用辅助节点
int length = 0;
HeroNode temp = head.next;
while(temp != null) {
length++;
temp = temp.next;
}
return length;
}
}
测试代码:
说明:配合之前单链表的应用来进行测试
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
//获取链表中的个数
int size = SingleLinkedListDemo.getLinedListSize(list.head);
System.out.printf("链表中的节点有%d个。\n",size);
}
}
运行结果:
② 查找单链表中的倒数第 k 个结点 【新浪面试题】
public class SingleLinkedListDemo {
//获取链表的倒数第k个节点
public static HeroNode getLastIndexNode(HeroNode head,int k) {
//判断链表为空的情况
if(head.next == null) {
return null;
}
//获取链表中的个数
int size = SingleLinkedListDemo.getLinedListSize(head);
//对于k的取值进行判断,若位置不合理返回null
if(k<=0 || k>size) {
return null;
}
//合理情况下求链表中的第size-k个节点
//使用一个辅助节点
HeroNode temp = head.next;
for(int i=1;i<=size-k;i++) {
temp = temp.next;
}
return temp;
}
}
测试代码:
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作 这里是按照id进行添加的
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
//获取链表中的个数
int size = SingleLinkedListDemo.getLinedListSize(list.head);
System.out.printf("链表中的节点有%d个。\n",size);
//进行遍历链表
System.out.println("进行遍历链表:");
list.showList();
System.out.println();
//获取链表的倒数第4个节点
HeroNode node = SingleLinkedListDemo.getLastIndexNode(list.head, 4);
System.out.println("倒数第一个节点为:"+node);
}
}
运行结果:
③ 单链表的反转【腾讯面试题,有点难度】
思路说明:
public class SingleLinkedListDemo {
//反转链表
public static void reverseList(HeroNode head) {
//当链表为空或1个节点时不进行反转
if(head.next == null || head.next.next == null) {
System.out.println("当前链表为空或有一个元素,无法反转!");
return;
}
//设置其为当前指向节点
HeroNode curNode = head.next;
//设置其为当前指向节点的后一节点
HeroNode nextNode = null;
//设置一个反转的链表,用于存放之后遍历的链表
HeroNode reversehead = new HeroNode(0, "", "");
while(curNode!=null) {
nextNode = curNode.next;//用于获取到当前节点cur后一个节点
curNode.next = reversehead.next;//这段代码相当于将每次获取的节点从后往前存放
reversehead.next = curNode;//用于保存存入到reverse后的节点
curNode = nextNode;//让curNode获取到nextNode的值,就像向后遍历一样
}
head.next = reversehead.next;//将反转过后的链表接连到初始链表head上
System.out.println("链表反转成功");
System.out.println();
}
}
测试代码:
package LinkedList;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作 这里是按照id进行添加的
list.addNodeById(node3);
list.addNodeById(node2);
list.addNodeById(node1);
list.addNodeById(node4);
//进行遍历链表
System.out.println("进行遍历链表:");
list.showList();
System.out.println();
//链表进行反转操作
reverseList(list.head);
System.out.println("链表反转之后:");
list.showList();
}
}
运行结果:
④ 从尾到头打印单链表 【百度,要求方式 1:反向遍历 。 方式 2:Stack 栈】
思路分析:
这里演示方式2:使用栈进行存储再打印
package LinkedList;
import java.util.Stack;
public class SingleLinkedListDemo {
//将链表从后往前打印
//使用到栈
public static void reversedPrint(HeroNode head) {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空,无法打印");
return;
}
//设置一个辅助节点
HeroNode cur = head.next;
//使用栈的api
Stack<HeroNode> stack = new Stack<HeroNode>();
while(cur != null) {
stack.add(cur);
//链表不断向后遍历,并压入栈
cur = cur.next;
}
//进行遍历栈
while(stack.size()>0) {
System.out.println(stack.pop());
}
}
}
测试过程:
说明:在遍历链表过程中不断存入栈中,最后再遍历栈中的数据,这样能够使链表的结构不变化**
package LinkedList;
import java.util.Stack;
public class SingleLinkedListDemo {
public static void main(String[] args) {
SingleLinkedList list = new SingleLinkedList();
//创建四个节点
HeroNode node1 = new HeroNode(1, "丁明", "青龙偃妈刀");
HeroNode node2 = new HeroNode(2, "小殷", "优雅男神");
HeroNode node3 = new HeroNode(3, "单泽鹏", "马尾小萝莉");
HeroNode node4 = new HeroNode(4, "顾天乐", "金刚小铁拳");
//进行添加操作 这里是按照id进行添加的
list.addNode(node3);
list.addNode(node2);
list.addNode(node1);
list.addNode(node4);
//进行遍历链表
System.out.println("进行遍历链表:");
list.showList();
System.out.println();
//将链表从后往前打印输出
System.out.println("链表从后往前打印:");
SingleLinkedListDemo.reversedPrint(list.head);
}
}
运行结果:
四、双向链表的应用实例
源代码:DoubleLinkedListDemo.java
**
思路分析(双向链表与单链表的不同)
代码实现
说明:此次代码实现了增加节点(顺序)、增加节点(按照id小到大)、删除节点、更改节点、遍历链表、获取头节点。
有无变化说明
对于遍历节点、修改节点对于单链表都没有变化。
有变化的说明:
① 增加节点(顺序):需要给新增节点的pre赋值
② 增加节点(按照id):由单链表给两个next赋值改变为两个pre以及两个next,并且对于temp.next.pre这个节点的pre在链表最后位置添加时要注意空指针的情况,所以要加上判断。
③ 删除节点:单链表需要查找删除节点的前一个节点,而双链表可以直接查找到对应节点利用pre进行删除节点,对于获取到temp.next.pre也要进行null值判断进行赋值。
package LinkedList;
public class DoubleLinkedListDemo {
public static void main(String[] args) {
DoubleLinkedList list = new DoubleLinkedList();
//创建四个节点
HeroNode2 node1 = new HeroNode2(1, "丁明", "青龙偃妈刀");
HeroNode2 node2 = new HeroNode2(2, "小殷", "优雅男神");
HeroNode2 node3 = new HeroNode2(3, "单泽鹏", "马尾小萝莉");
HeroNode2 node4 = new HeroNode2(4, "顾天乐", "金刚小铁拳");
//进行添加操作 这里是按照id进行添加的
list.addNode(node1);
list.addNode(node2);
list.addNode(node3);
list.addNode(node4);
System.out.println("进行修改操作:");
HeroNode2 node5 = new HeroNode2(3, "长路", "无敌拳霸");
list.updateNode(node5);
//进行遍历操作
list.showList();
System.out.println();
System.out.println("进行删除操作:");
list.delNode(4);
list.showList();
System.out.println();
System.out.println("进行插入(根据id)操作:");
list.addNodeById(new HeroNode2(0, "林儿", "长路对象"));
list.showList();
}
}
//创建管理链表的类
class DoubleLinkedList{
//先初始化一个头节点, 头节点不要动, 不存放具体的数据
public HeroNode2 head = new HeroNode2(0, "", "");
//返回头节点
public HeroNode2 getHeadNode() {
return head;
}
//进行添加节点到末尾
public void addNode(HeroNode2 node) {
//首先使用一个临时变量代替head
HeroNode2 temp = head;
//寻找到链表的最底部
while(true) {
if(temp.next == null) {
break;
}
temp = temp.next;
}
//插入node节点到最底部
temp.next = node;
//增加点:将最后添加的节点的pre得到temp节点
node.pre = temp;
}
//插入节点按照id编号排序进行插入
public void addNodeById(HeroNode2 node) {
//temp就代表待插入节点的前一个节点
HeroNode2 temp = head;
//这个flag代表是否有相同id值,true表示不能插入,false表示能插入
boolean flag = false;
while(true) {
//当temp节点的下一个节点为null,说明在尾部了,跳出循坏在尾部添加
if(temp.next == null) {
break;
}
//当temp节点下一个节点的id与待插入节点的id相同,flag设置为true,表明不能插入
if(temp.next.id == node.id) {
flag = true;
break;
}
//当temp节点下一个节点的id大于待插入节点id,跳出循环,将node节点插入到temp之后
if(temp.next.id > node.id) {
break;
}
//方便进行遍历
temp = temp.next;
}
if(flag) {
System.out.printf("插入的节点id【%d】已存在,无法插入\n",node.id);
}else {
if(temp.next != null) {
//若不是最后一个节点,那么就可以获取到temp.next并赋值
temp.next.pre = node;
}
//首先插入节点的next先获取到temp的next节点
node.next = temp.next;
//插入的节点的前一个节点为temp
node.pre = temp;
//再将node节点插入到temp节点之前
temp.next = node;
}
}
//遍历链表
public void showList() {
//判断链表是否为空
if(head.next == null) {
System.out.println("链表为空,无法进行遍历数据!");
return;
}
//使用一个临时变量获取到链表下一个节点
HeroNode2 temp = head.next;
while(true) {
if(temp == null) {
break;
}
System.out.println(temp);
temp = temp.next;
}
}
// 修改节点,根据id编号进行修改
public void updateNode(HeroNode2 newnode) {
// 创建一个辅助节点,令temp为head的next
HeroNode2 temp = head.next;
// 判断是否为空链表情况
if (temp == null) {
System.out.println("链表为空,无法进行修改!");
return;
}
// true表示能够修改, false表示不能修改
boolean flag = false;
while (true) {
// 表示到达链表末尾,无法进行插入
if (temp == null) {
break;
}
// 找到对应id相同的英雄
if (temp.id == newnode.id) {
flag = true;// 表示能够进行修改
break;
}
// 进行遍历使用
temp = temp.next;
}
if (flag) {
temp.name = newnode.name;
temp.nickname = newnode.nickname;
System.out.printf("修改id【%d】成功!\n",newnode.id);
} else {
System.out.println("链表中无法找到对应id英雄,修改失败!");
}
}
//根据id来删除节点 由于是双向链表就可以查找对应要删除的节点即可进行删除
public void delNode(int id) {
if(head.next == null) {
System.out.println("链表为空,无法删除!");
return;
}
//直接获取到头节点的下一个节点
HeroNode2 temp = head.next;
//true用来表示找到删除的节点,false表示未找到删除的节点
boolean flag = false;
while(true) {
//到链表末尾没有找到对应节点
if(temp == null) {
break;
}
//找到temp的下一节点为对应删除的节点
if(temp.id == id) {
flag = true;
break;
}
//进行遍历
temp = temp.next;
}
if(flag) {
//删除节点的前一个节点的next为下一个节点
temp.pre.next = temp.next;
//为删除节点的后一个节点的pre赋值
//避免删除项是最后一个节点的情况
if(temp.next != null) {
temp.next.pre = temp.pre;
}
System.out.printf("成功删除id【%d】",id);
System.out.println();
}else {
System.out.printf("链表中未找到对应id【%d】节点,删除失败!",id);
System.out.println();
}
}
}
//创建节点
class HeroNode2{
public int id;
public String name;
public String nickname;
public HeroNode2 next;//指向后面一个节点
public HeroNode2 pre;//指向前面一个节点
//创建一个构造器,存放id、name、nickname
public HeroNode2(int id, String name, String nickname) {
super();
this.id = id;
this.name = name;
this.nickname = nickname;
}
@Override
public String toString() {
return "HeroNode2 [id=" + id + ", name=" + name + ", nickname=" + nickname + "]";
}
}
运行结果:
五、单向环形链表
1. 单向环形链表应用场景
2. 单向环形链表介绍
源代码:Josepfo.java
示意图:
① Josephu 问题
问题描述:
② 构建约瑟夫环以及遍历约瑟夫环(代码实现)
创建约瑟夫环的节点以及约瑟夫环对象
//表示约瑟夫环
class CircleSingleLinkedList{
//需要创建第一个节点
private Boy first = null;
}
class Boy{
private Integer id;
private Boy next;
public Boy(Integer id) {
super();
this.id = id;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}
创建约瑟夫环
说明:对于插入有两种情况,第一种是插入第一个节点时,第二种就是插入第二个节点之后所有节点。
需要的节点:**每次插入时创建的节点以及辅助节点(用于指向新创建的节点位置,不对first节点进行更改)。
//表示约瑟夫环
class CircleSingleLinkedList{
//需要创建第一个节点
private Boy first = null;
public void createJosepho(int num) {
//对创建个数进行规范
if(num<1) {
System.out.println("创建人数过少,请重新创建!");
return;
}
//创建一个辅助节点
Boy curBoy = null;
//进行循环插入
for(int i=1;i<=num;i++) {
Boy boy = new Boy(i);
//如果是第一个创建的
if(i == 1) {
first = boy;
first.setNext(first);
//辅助节点获取到新插入的节点
curBoy = first;
}else {
//不是第一次插入时根据辅助节点插入
curBoy.setNext(boy);
boy.setNext(first);
curBoy = boy;
}
}
System.out.printf("%d人的约瑟夫环创建成功\n",num);
}
}
遍历约瑟夫环
说明:**需要判断链表是否为空以及使用辅助节点来进行遍历,遍历时的条件是当前节点的下一个节点为first第一个节点时遍历结束。
//进行遍历约瑟夫环
public void showJosepho() {
//判断是否为空
if(first == null) {
System.out.println("约瑟夫环为空,无法遍历!");
return;
}
//使用辅助节点来进行遍历
Boy curBoy = first;
while(true) {
System.out.println("id:"+ curBoy.getId());
if(curBoy.getNext() == first) {
break;
}
curBoy = curBoy.getNext();
}
}
测试创建与遍历
package LinkedList;
public class Josepfo {
public static void main(String[] args) {
//创建数量为5的约瑟夫环
CircleSingleLinkedList list = new CircleSingleLinkedList();
list.createJosepho(5);
list.showJosepho();
}
}
运行结果:
③ 小孩出圈(传入初始位置、数数数量、总人数)
说明:接连在上面方法的类中,由于是单向链表,所以我们在出圈过程中还应该知晓出圈节点的前一个节点
① **初步状态辅助节点在整个环形链表最后。
② 根据初始位置来调整helper、first节点位置,始终一前一后状态。
③ 来根据数数进行出圈报出id名。**
public void countBoy(int startNo,int countNum,int nums) {
//如果链表为空,初始位置小于1,初始位置大于总数
if(startNo<1 || startNo>nums || first == null) {
System.out.println("您输入的参数有误,请重新输入!");
}
//定义一个辅助节点
Boy helper = first;
//将辅助节点变为最后一个节点来紧跟first头节点
while(true) {
if(helper.getNext() == first) {
break;
}
helper = helper.getNext();
}
//根据初始位置进行调位
for(int i = 0;i<startNo-1;i++) {
//两个节点一起移动
first = first.getNext();
helper = helper.getNext();
}
while(true) {
//如果环中只有一个节点了,那么退出循环
if(helper == first) {
break;
}
//进行数数
for(int i=0;i<countNum-1;i++) {
first = first.getNext();
helper = helper.getNext();
}
//将first指定的移除
System.out.println("移除的id为:"+first.getId());
first = first.getNext();//这里first移到移除人的后方
helper.setNext(first);//辅助节点的next值指向新的first
}
System.out.println("最后一位id为:"+first.getId());
}
测试代码:
package LinkedList;
public class Josepfo {
public static void main(String[] args) {
//创建数量为5的约瑟夫环
CircleSingleLinkedList list = new CircleSingleLinkedList();
list.createJosepho(5);
list.showJosepho();
//起始位置为1,报数为2,总人数为5
System.out.printf("\n开始数数了:\n");
list.countBoy(1, 2, 5);
}
}
运行结果:
整理者:长路 时间:?-2020/8/6