用java单向循环链表解决约瑟夫问题

@lyc1234

用Java单向循环链表解决约瑟夫问题

约瑟夫问题:设有n个人围坐在一张圆桌周围,先从某个人开始从1报数,数到m的人出列(即离开座位,不参与以后的报数),然后从出列的下一个人开始重新从1报数,数到m的人又出列,如此下去直到所有人都出列为止,试求出它们的出列顺序。

例如:当n=8,m=4时,若从第一个人(每个人的编号依次为1,2,…,n)开始报数,则得到的出列次序为:4,8,5,2,1,3,7,6;
此算法要求以n,m和s(从第s个人开始第1次报数)作为值参

思路

  1. 用java先构建一个单向循环链表
  2. java指针类,链表类
  3. 编写插入算法
  4. 编写出列算法:每次向后移动相同次数的指针,然后将指定位置的id输出,并删除该位置,继续下一次的移动。

代码实现

1.指针类

/*
通过每一个link内的next指向下一个link,从而达到构建指针的目的,方便使用和移动
*/
public class Link {
      public int iData; //id
      public double dData; //数据
      public double item;//用来返回值(如果函数要返回值,由整个公共变量带回)
      public Link next;/*指向下一个的节点,对另一个对象的引用,
      并不是包括了这个对象。编译器会自动初始化null值,不需要自己再重新实例化一个对象*/
      public Link(int id,double dd)//有参构造器 {
      {
    	  iData=id;
          dData=dd;
      }
	public void displayLink() {
    	  System.out.println("{"+iData+","+dData+"}");
      }
}

2.单向循环链表类

/*构建一个头节点,尾节点,并采用尾部插入的方式达到循环的目的
*/
public class LinkList2 {
	private Link first;//头节点
	private Link tail;//尾节点
	Link temp1=null;//这个是用来改变函数使用的指针节点位置,将形参带回实参
	public int count=0;//记录链表的长度
    public double item;//用来返回值
    LinkList2(){//无参构造器
    	first=tail=null;
    	count=0;//初始化表,将元素的个数清零
     }
	public void InsertList(Link k) {//在尾部传入节点
		if (count==0) {//如果是空表,那么头尾节点相等。
			tail=first=k;
			
		}
		else {
			tail.next=k;//将尾部节点连接到k
			k.next=first;//将k连接到头部节点
			tail=k;//然后将尾部节点更新为k
	  }
		count++;//元素的个数++
     }
	public int exitList(Link current,int i) {//出列算法,值参i为current后i位元素出列
		int k=1;//因为是输出current下一个节点的id,所以计数变量k要从1开始。
		while(k<i) {
				current=current.next;//移动i-1个位置的指针,然后输出下一个指针的id
				k++;
		  }
		int temp=current.next.iData;
		if(count!=1){//如果不是头部,就删除下一个节点
	      current.next=current.next.next;//直接将当前的next指针连到下下个指针
	      current=current.next;//移动节点
		}
		else {
			current=first=tail=null;//如果是头部,就将头部节点和尾部节点清空
		}
		  count--;//元素个数
		  temp1=current;//将当前的指针传出去,不然不能改变指针所在位置
		  return temp;//将值传出去
	  }
	public void displaylist() {//遍历算法,用来检查单向循环链表是否创建成功
  		  System.out.println("first --> last");
  		  Link current=first;//先将current设置为第一个节点
  		  while(current!=tail) {
  			  current.displayLink();//打印当前节点
  			  current=current.next;//将下一个指向赋给当前节点
  		  }
  		  current.displayLink();
  		  System.out.println("");
  	  }
	public boolean isEmpty() {//判断是否为空算法
		return(count==0);
	}
    public void josephus(int n,int m, int s) {//约瑟夫算法
    	for(int i=1;i<=n;i++) {
    		this.InsertList(new Link(i,0.0));//初始化链表
    	}//因为编号是1……n,所以从1开始,创建一个连接对象后传入链表
    	Link current=first;//将指针设置为头部节点
    	for(int i=0;i<s-1;i++) {//因为从第s个人开始,先移动s-1次到达要报数的第一个人处
    		current=current.next;
    	}
    	while(!this.isEmpty()) {//如果表为空,就退出
    		int i;//输出变量
    		i=this.exitList(current, m-1);//调用出列算法,因为是算自己当前对象,所以每次移动m-1次就好
    		current=temp1;
    		System.out.println(i);
    	}
    }
}

3.主程序类

public static  void main(String[] args) {
	 
	 LinkList2 l2=new LinkList2();
	 l2.josephus(8, 4, 1);
}

其实为什么要使用m-1次,我也没想明白……

发布了4 篇原创文章 · 获赞 0 · 访问量 226

猜你喜欢

转载自blog.csdn.net/weixin_43869261/article/details/101619628