单向环形链表
文章目录
前言
提示:通过约瑟夫问题来具体了解环形单向链表
一、什么是单向环形链表?
定义:单向环形链表就是在单链表的基础上,单链表的最后一个节点指向链表的第一个节点,构成环状的链表。
如图所示:
二、创建单向环形链表思路
(实现代码在后面)
三、Josephu(约瑟夫问题)问题
1.Josephu问题实例:
设编号为1,2,… n的n个人围坐一圈, 约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
2.约瑟夫问题分析
四、详细代码(环形链表创建、遍历、约瑟夫问题)
代码如下(详细注释):
/**
* 环形单向链表
*/
public class CircularLinkedList {
public static void main(String[] args) {
//测试
CircularSinglyLinkedList circularSinglyLinkedList = new CircularSinglyLinkedList();
//1.创建环形链表
circularSinglyLinkedList.addBoy(25);
//2.遍历环形链表
//circularSinglyLinkedList.getLinkedList();
//3.约瑟夫问题(n的值要和创建环形链表的长度相同,这里为5)
circularSinglyLinkedList.josephuLinkedList(25,1,2);
}
}
/**
* 创建一个环形单向链表
*/
class CircularSinglyLinkedList {
//创建一个first节点,用于指向后面创建的第一个节点
Boy first = null;
/**
* 添加节点,创建单向环形链表
*/
public void addBoy(int nums){ //nums为环形链表中的节点个数
if (nums < 1){
System.out.println("环形链表的节点数不能小于1");
return;
}
//因为first节点不能动,所以我们定义一个辅助节点item
Boy item = first;
//通过循环创建环形链表
for (int i = 1; i <= nums; i++) {
//创建节点
Boy boy = new Boy(i);
if (i == 1){
first = boy; //把创建的节点赋值给first节点
first.setNext(first); //构成环
item = first; //让辅助节点指向第一个节点
}else {
item.setNext(boy); //指向新创建的节点
boy.setNext(first); //新节点指向第一个节点形成环
item = boy; //辅助节点指向新节点
}
}
}
/**
* 遍历环形链表
*/
public void getLinkedList(){
//辅助节点
Boy item = first;
//当链表为空
if (first == null){
System.out.println("环形链表不存在");
return;
}
//循环遍历
while (true){
System.out.println("环形链表节点序号:" + item.getNo());
if (item.getNext() == first){ //辅助节点指向第一个节点,那么遍历结束
break;
}
item = item.getNext(); //辅助节点后移
}
}
/**
* Josephu(约瑟夫问题)问题为:设编号为1,2,… n的n个人围坐一圈,
* 约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,
* 它的下一位又从1开始报数,数到m的那个人又出列,依次类推,
* 直到所有人出列为止,由此产生一个出队编号的序列。
*
* @param n 一共有多少个人
* @param k 从第k个人开始从1报数
* @param m 数到m的那个人出列
*/
public void josephuLinkedList(int n,int k,int m) {
//不合理的参数
if (n < 1 || k < 1 || k > n || m < 1) {
System.out.println("请输入合理的参数");
return;
}
//我们需要定义一个节点rear指向first的前一个节点(也就是最后一个节点)
Boy rear = first; //先让rear=first
//循环让rear指向first的前一个节点
while (true) {
if (rear.getNext() == first) { //此时rear就是指向first的前一个节点,退出循环
break;
}
rear = rear.getNext();
}
//首先我们要先将first和rear分别移动到开始报数的序号k和序号前一个k-1
for (int i = 0; i < k - 1; i++){
first = first.getNext();
rear = rear.getNext();
}
//循环让数到m的人出列,直到只剩下一个节点
while (true){
if (first == rear){ //只剩一个节点,循环结束
break;
}
//循环,让first到达要出列的节点,rear到达要出列节点的前一个节点
for (int i = 0; i < m - 1; i++) {
first = first.getNext();
rear = rear.getNext();
}
//打印需要出列的数到m的节点
System.out.println("出列节点顺序:" + first.getNo());
//让数到m的节点出列
first = first.getNext();
rear.setNext(first);
}
//打印剩下的一个节点
System.out.println("最后一个节点:" + first.getNo());
}
}
/**
* 创建一个Boy类表示节点
*/
class Boy {
private int no; //男孩的序号
private Boy next; //指向下一个节点
public Boy(int no) {
this.no = no;
}
public int getNo() {
return no;
}
public void setNo(int no) {
this.no = no;
}
public Boy getNext() {
return next;
}
public void setNext(Boy next) {
this.next = next;
}
}