约瑟夫问题:N个人围成一圈,从第一个开始报数,第M个将被杀掉,最后剩下一个,其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。
代码实现:
package com.xsw.linklist.linklist;
import com.xsw.linklist.bean.Node;
/**
* 环形单链表 解决约瑟夫问题
*/
public class SimpleCircleLinklist {
public Node head;
//添加
public void add(int num){
if (num<1){
return ;
}
Node cur = null ; //辅助指针,指向最后一个节点
for (int i=1;i<=num;i++){
if (i==1){//初始化第一个节点,并指向头节点
head = new Node(i);
head.setNext(head); //形成环形链表
cur = head ;
}else{
Node node = new Node(i);//初始化其他节点
cur.setNext(node);//将最后节点指针指向新加入的节点
node.setNext(head); //将新加入的节点指向头节点
cur = cur.getNext() ; //后移辅助指针
}
}
}
/**
* 解决约瑟夫问题:N个人围成一圈,从第k个开始报数,第M个将被杀掉,最后剩下一个,
* 其余人都将被杀掉。例如N=6,M=5,被杀掉的顺序是:5,4,6,2,3。
*
* 实现思路:
* 1、被杀掉即意味着从链表中删除该节点 因此定义 pre、target指针
* 2、当pre=target时,说明链表中只剩一个节点
* @param n n个人
* @param k 从第k个开始报数
* @param m 第m个被杀掉
*/
public void dealJoseph(int n,int k,int m) {
if (n < m || n < 2 || m < 0) {
System.out.println("输入参数有误!");
}
this.add(n);//初始化环形链表
int count = 1 ; //计数器
Node pre = head; //辅助指针,被杀掉的人的前一个(先定位到尾结点)
Node target = head; //杀掉的人(先定位到头节点)
//1、先遍历把pre定位到尾结点for
for (int i = 1; i < n; i++) {
pre = pre.getNext();//遍历结束后pre就指向了尾结点
}
// 2、先定位到第k个人
for (int i = 1; i < k; i++) {
target = target.getNext();
pre = pre.getNext();
}
//3.开始杀人
while(pre!=target){ //如歌pre等于target,说明链表中只剩下一个人,存活
if (count == m){ //target就是当前要干掉的
//杀掉,从链表中移除
System.out.println(target.getData()+"被杀掉");
pre.setNext(target.getNext());
target = target.getNext() ;
count = 1 ;//重新计数
}else{
//移动pre、target指针
pre = pre.getNext();
target = target.getNext() ;
count++ ; //报数
}
}
System.out.println("幸存者是:"+pre.getData());
}
//遍历
public void list(){
Node cur = head ; //辅助节点帮助遍历
while(true){
if (cur.getNext()==head){
System.out.println(cur.getData());
break;
}
System.out.println(cur.getData());
cur = cur.getNext() ;
}
}
}