数据结构与算法--Josephu(约瑟夫、约瑟夫环) 问题

本文介绍了如何用Java实现Josephus环问题,通过创建环形链表并实现从指定编号开始的报数删除操作,展示了如何处理单向链表的节点删除与链接。通过实例演示了链表的创建、遍历和删除过程,适用于学习链表和算法设计。
摘要由CSDN通过智能技术生成

Josephu 【约瑟夫、约瑟夫环】问题

Josephu

Josephu 问题为:设编号为1,2,… n的n个人围坐一圈,约定编号为k(1<=k<=n)的人从1开始报数,数到m 的那个人出列,它的下一位又从1开始报数,数到m的那个人又出列,依次类推,直到所有人出列为止,由此产生一个出队编号的序列。
在这里插入图片描述
实现思路:
创建环形链表:
1.添加节点:
第一个节点时,也需要形成一个环形。
遍历到最后一个节点将其添加,并且将其与第一个节点相连
怎么确定最后一个节点和第一节点。
第一个节点:可以一个first节点固定费不移动(跟单链表的头节点一样)
最后一个节点:Node.next == first,若相等则为最后一个节点
2.遍历节点
从第K个节点开始遍历,需要两个辅助指针一开始都指向第K个节点,一个指针不移动的作为遍历结束的标记,另一个则是进行遍历。
3.删除节点(提取节点出来排序)
从第k个节点开始数,每第m个将其提取出来排队,只要最后一个节点,并且将最后一个节点直接排在最后
遍历链表:
1.从第K个节点开始,遍历出第K个节点。
2.然每数m个节点,将其提取出来,而且在链表中删除提取出去的节点。
3.(红色线是,将节点删除之后的)问题来了:我怎么将单向链表,上一个节点的next,连接到下一个节点呢。
(若是双向就可以利用pre 与 next 实行自我删除,单向链表:找到需要删除节点的上一个节点 )
可以利用两个辅助变量来同时进行遍历, temp1: 当数完m个节点,temp1所指的节点是要提取出来排队的, 则
Node newNode = temp2;(先将节点保留下来排队)
此时好保留 newNode .next == temp2.next
temp1.next = temp2.next ;(就可以将节点删除)
temp2 =temp2.next;
temp1 = temp1.next;
取到最两个:
在这里插入图片描述
取倒最后一个时,也要将其取出来此次排序;最后环形链表为空

在这里插入图片描述

将取出的节点依次排序成一条单链表:
在这里插入图片描述

代码


public class Josepfu {
	public static void main(String[] args) {
		//创建节点
		Boy boy1 = new Boy(1, "路飞小同学");
		Boy boy2 = new Boy(2, "娜美小同学");
		Boy boy3 = new Boy(3, "索隆小同学");
		Boy boy4 = new Boy(4, "香吉士小同学");
		Boy boy5 = new Boy(5, "罗宾小同学");
		
		//创建环形单向链表
		CircleSingleLinkedList circleSingleLinkedList = new CircleSingleLinkedList();
		circleSingleLinkedList.add(boy5);
		circleSingleLinkedList.add(boy4);
		circleSingleLinkedList.add(boy3);
		circleSingleLinkedList.add(boy2);
		circleSingleLinkedList.add(boy1);
		
		//创建单链表进行排队
		SingleLinkedList singleLinkedList = circleSingleLinkedList.singleLinkedList;
		
		//显示链表
		
		circleSingleLinkedList.show(3, "索隆小同学");
		circleSingleLinkedList.del(3, "索隆小同学", 2);
		circleSingleLinkedList.show(3, "索隆小同学");
		singleLinkedList.list();
	}
		
}

//创建环形的单向链表
class CircleSingleLinkedList {
	//创建一个first节点,作为链表的标计
	Boy firstBoy = null;
	//添加节点,构建环形的链表
	//遍历需要一个辅助指针temp
	Boy temp =null;
	
	//创建单链表进行排队
	SingleLinkedList singleLinkedList = new SingleLinkedList();
	
	public void add(Boy boyNode) {
		//遍历需要一个辅助指针temp	
		while(true) {
			if(firstBoy == null) { 
				firstBoy=boyNode;
				firstBoy.setNxetBoy(firstBoy); // 说明没有第一个节点 ,第一个节点需要形成环形
				temp = firstBoy;
				//System.out.println("temp.getNxetBoy()"+temp.getNxetBoy());
				break;
			} 
			//若不是第一个节点,则遍历到最后一个节点
			else if(temp.getNxetBoy() == firstBoy ) {
				temp.setNxetBoy(boyNode);
				boyNode.setNxetBoy(firstBoy);
				break;	
			}
			temp =temp.getNxetBoy();
		}	
	}
	
	//遍历显示链表 ,送 第K个节点(no nameString 的节点)开始显示链表
	public void show(int no , String nameString) {
		//遍历需要一个辅助指针temp
		Boy temp = firstBoy;
		//找出 第K个节点 ,开始显示链表
		if(firstBoy == null ) {System.out.println("该链表为空");return;}
		while(temp != temp.getNxetBoy()) {
			//System.out.println("    "+temp);
			if(temp.getNo() == no && temp.getNameString() == nameString) {break;}
			temp = temp.getNxetBoy();
			
		}
		
		//已经找到第K个节点,开始显示链表 ,此时重新起个头节点,作为遍历结束的标志
		Boy fBoy = temp;
		do {
			if(temp==null) {break;}
			 System.out.println(temp);
			 temp = temp.getNxetBoy();		
		}while(temp != fBoy );	
	}
	
	//环形链表的删除
	public void del(int no , String nameString,int m) {
		//遍历需要一个辅助指针temp
		Boy temp = firstBoy;
		//System.out.println("firstBoy\t" + firstBoy);
		//System.out.println("temp\t" + temp);
		//找出 第K个节点 ,开始显示链表
		while(true) {
			if(temp.getNo() == no && temp.getNameString() == nameString) {break;} 
			temp = temp.getNxetBoy();
			
		}
		Boy temp1 = temp;        //temp2 移动m次后 指向的节点是需要删除 和 提取 出来的
 		Boy temp2 = temp.getNxetBoy();
 		while(true) {
 			//找到第K个节点开始,数m个节点
 			if(temp1 == temp2 ) {break;}
 			for(int i = 0 ; i <m -1; i++ ) {
 				temp2 = temp2.getNxetBoy();
 				temp1 = temp1.getNxetBoy();			
 			}
 			//节点移动m次
 			Boy savaBoy = temp2;
 			
 			temp1.setNxetBoy(temp2.getNxetBoy()); 
 			temp2 = temp2.getNxetBoy();
 			//System.out.println("temp2" + temp2);
 			//System.out.println("temp1" + temp1);
 			savaBoy.setNxetBoy(null); 
 			//System.out.println("savaboyNode"+ savaBoy);
 			singleLinkedList.add(savaBoy);
 			
 		}
 		//此时链表只剩下一个节点
 		firstBoy=null;
 		temp2 .setNxetBoy(null);
 		singleLinkedList.add(temp2);
 		
 		
 		//singleLinkedList.list();
	}
}


//创建节点类
public class Boy{
	private int no;
	private String nameString;
	private Boy nxetBoy;
	public Boy(int no , String nameString) {
		this.nameString = nameString;
		this.no = no;	
	}
	public int getNo() {
		return no;
	}
	public void setNo(int no) {
		this.no = no;
	}
	public String getNameString() {
		return nameString;
	}
	public void setNameString(String nameString) {
		this.nameString = nameString;
	}
	public Boy getNxetBoy() {
		return nxetBoy;
	}
	public void setNxetBoy(Boy nxetBoy) {
		this.nxetBoy = nxetBoy;
	}
	@Override
	public String toString() {
		return "Boy [no=" + no + ", nameString=" + nameString  +"]";
	}
	
}


public class SingleLinkedList {
	//先初始化一个头节点, 头节点不要动, 不存放具体的数据 	
	private Boy head = new Boy(0, "");
	
	 //返回头节点
    public Boy getHead() {
          return head;
          }
    
    /*
    //添加节点到单向链表
    //思路,当不考虑编号顺序时
    //1. 找到当前链表的最后节点
    //2. 将最后这个节点的next 指向 新的节点
     */
    public void add (Boy heroNode) {
          
          //因为head节点不能动,因此我们需要一个辅助遍历 temp
    	  Boy temp = head;
          boolean flag = false;
          //遍历链表,找到最后
          while(true) {
                //找到链表的最后
                if(temp.getNxetBoy() == null) {  break; }
                //if (temp.getNo() == heroNode.getNo()) { flag=true; break; } //说明该元素 链表 已经存在	
                //如果没有找到最后, 将将temp后移  
                temp = temp.getNxetBoy();
          }
          
          if(flag) {System.out.printf("该元素%s已经存在,添加失败",temp.getNxetBoy()); System.out.println();}
          else {
        	  //当退出while循环时,temp就指向了链表的最后
              //将最后这个节点的next 指向 新的节点
              temp.setNxetBoy(heroNode);
             
		}    
    }

  //显示单链表
    public void list() {
    	
    	//判断链表是否为空
    	if(head.getNxetBoy() == null){ System.out.println("该链表为空");  return; }
    	
    	//由于头节点是不能移动的,需要用到一个遍历变量 来 遍历 显示

    	Boy temp = head.getNxetBoy();  //显示遍历 ,不需要对头节点进行显示,即temp 被赋值为 头节点的 next (temp是指向 next)
    	    
    	while(true) {
    		if(temp == null ) {break;} //判断遍历到最后
    		//如果没有到最后,先显示本节点的消息 , 继续向后移
    		System.out.println("single"+temp);
    		temp=temp.getNxetBoy();
    	}
}
}



运行结果:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值