约瑟夫问题(循环单链表实现)原理及代码实现
循环单向链表学习目标
1. 循环链表的基本介绍及特点
2. 循环单链表的操作
3. 约瑟夫问题
4. 用循环单链表思路分析
5. 代码实现
6. 核心代码详解以及注意
一,循环单单链表的介绍及特点
在之前我们学习了单链表,循环单链表故名思意,首尾连接起来不就可以了?也即是最后一个节点的next指针指向头结点。
二,循环单链表的操作
它和单链表一样就是插入,删除,遍历。
插入:和单链表相似,除了尾节点插入需要把其next指向头结点。
删除:中间删除和单链表一样,末尾删除,先找到要删除元素的上一个元素(单链表无法对自身节点进行操作,都要借助前一位节点),然后把他的next指向头结点,删除的元素没有引用指向会被垃圾回收。
遍历:当最后一个节点的next指向头结点时,遍历结束。
三,约瑟夫问题
Josephu问题为:设编号为1,2,…n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
四,用循环链表进行分析解决的思路
1,先构成一个有n个结点的单循环链表
2,然后由k结点起从1开始计数,计到m时,对应结点从链表中删除
3,然后再从被删除结点的下一个结点又从1开始计数,直到最后一个结点从链表中删除算法结束
五,代码实现
package com.atxiaopeng.circlesinglelinkedlist;
public class CircleSingleLinkedList {
public static void main(String[] args) {
// TODO Auto-generated method stub
//
CreateLinkList cL = new CreateLinkList();
//cL.add(5);
cL.getListOut(6, 2, 5);
//cL.showList();
}
}
class CreateLinkList{
CreateNode first = null;
public void add(int n) {
CreateNode curr = null;
if (n < 1) {
System.out.println("数据有误!");
return;
}
for (int i = 1; i <= n; i++) {
CreateNode c = new CreateNode(i);
if (i == 1) {
first = c;
c.next = first;
curr = first;
}else {
curr.next = c;
c.next = first;
curr = curr.next;//指向c
}
}
}
public void showList() {
if (first == null) {
System.out.println("链表为空!");
return;
}
CreateNode curr = first;
while(true){
if (curr.next == first) {
System.out.println(curr.no);
break;
}
System.out.print(curr.no+" ");
curr = curr.next;
}
System.out.println();
}
//从链表中第K个元素开始数M个数循环取出元素
public void getListOut(int k,int m,int num) {
if (num == 0 || k<0 || k > num ||m < 0) {
System.out.println("数据输入错误!");
return;
}
add(num);
CreateNode he = first;
while (true) {
if (he.next == first) {
break;
}
he = he.next;
}
//报数前先让first和curr移动k-1个位置
for (int i = 0; i < k-1; i++) {
first = first.next;
he = he.next;
}
System.out.print("出队列的元素顺序为:");
while (true) {
if (he == first) {
break;
}
for (int i = 0; i < m-1; i++) {
first = first.next;
he = he.next;
}
System.out.print(first.no+" ");
first = first.next;
he.next = first;
}
System.out.println(he.no);
}
}
class CreateNode{
public int no;
public CreateNode next;
public CreateNode(int n) {
this.no = n;
}
}
以上就是全部代码
六,总结
用循环单链表解决约瑟夫问题的核心代码:
CreateNode he = first;
//第一次小循环
while (true) {
if (he.next == first) {
break;
}
he = he.next;
}
//第二次小循环
//报数前先让first和curr移动k-1个位置
for (int i = 0; i < k-1; i++) {
first = first.next;
he = he.next;
}
System.out.print("出队列的元素顺序为:");
//大循环
while (true) {
if (he == first) {
break;
}
//大循环内部的小循环
for (int i = 0; i < m-1; i++) {
first = first.next;
he = he.next;
}
System.out.print(first.no+" ");
//出队列的操作
first = first.next;
he.next = first;
}
System.out.println(he.no);
分析:
- 我们需要一个辅助节点,然后让他进行遍历,让它到链表的尾部并把它的next指向头结点first。
- 从第K个人开始报数,所以报数前,我们需要先把first节点的辅助节点同时移动k-1位。
- 然后开始报数,1到m,m出列,把first和辅助节点都移动m-1位,然后first出表,核心代码:first = first.next;辅助接点.next=first;
- 到最后当辅助节点 = first时,退出循环
- 总共一个大循环,里面包含了3中的小循环。而1循环是为了让辅助节点在链表的尾部,2循环是为了找到开始时的第k个人,同时把那个人当成first节点。这样就有点像是从K=1时第一个人开始的情形一样。
- 总而言之把要出链表的那个节点m当成first就行了,辅助节点始终指向first
欢迎指正,谢谢观看,仅供参考!
转载:https://blog.csdn.net/qq_39586922/article/details/104849677
查看评论