约瑟夫环

约瑟夫环来自于一个故事,故事的大概就是几个人围成一圈从1开始数数,每数到一个数字(比如是3),那么这个人出列,从下一个人开始重新数数,下一个数到3的人出列,依次循环下去,直到所有人出列。

约瑟夫环的实现可以采用数组、环形链表、容器类解决,环形链表需要创建结点对象,容器类需要维护容器类,最佳实现方法就是使用数组,当然,使用数组的时候就不能和链表或者容器类一样真实删除(因为真是删除要移动元素形成新的数组),而是模拟删除。下面是两种实现方法。

使用数组实现约瑟夫环

给数组全部赋1,报数的方法是加上该位置的数值,也就是每个位置加1,当该位置删除之时赋0,,也就是该位置在数组中被遍历到了但是不进行报数,实现了模拟删除。

import java.util.Scanner;

/**
 * 约瑟夫环
 * @author XY
 */

public class Joseph {
	public static void main(String[] args) {
//		Scanner scanner=new Scanner(System.in);
//		int cut=scanner.nextInt();//删除阈值
//		int N=scanner.nextInt();//数组的大小
		long time=System.currentTimeMillis();
		int cut=4;
		int N=10;//模拟输入值
		int[] num=new int[N];
		for (int i = 0; i < num.length; i++) {
			num[i]=1;//置初始值为1
		}
		StringBuffer sb=new StringBuffer();
		int res=N;//剩余元素的数量
		int point=0;//遍历指标
		int flag=0;//删除指标
		
		while(res>0){
			if(point==N) point=0;//实现数组首尾连接
			flag+=num[point];//删除指标加1或者0			
			if(flag==cut){//达到删除阈值
				sb.append(point+1+" ");
				num[point]=0;//置0不增加删除指标
				flag=0;
				res--;
			}
			point++;			
		}
		System.out.println((System.currentTimeMillis()-time)/1000.0);//运行时间
		System.out.println(sb);
	}
}


使用数组模拟删除操作,降低时间复杂度

上面的方法中,使用置0操作使得删除元素不参与报数,但是在数组中是被访问了,也就是说如果一个长度为1000的数组最后只剩下了2个位置参与报数的时候剩下的998个位置虽然不参与报数,但是程序还是访问了其他的998个位置,这就造成了很大的浪费。下面的方法就是避免这种不必要的数组访问,降低时间复杂度,在本文的程序中都设置了计时,在数组很小的时候分不出区别,但是在数组在1000000这个级别的时候区别就很明显。

避免无用的数组访问的方法也很简单,首先赋值如下:

012345
123450

在遍历的时候前驱结点pre=now(现结点),now=num[now]。因为上述遍历方式,现结点是通过上一个结点来访问的,那么删除操作就是num[pre]=num[now],这样就删除了now结点,下一次访问的时候通过pre直接到now的下一个结点,例如num[3]=num[4],下一次now=3的时候now=num[now]=5,不访问num[4],实现了删除操作。

import java.util.Scanner;

/**
 * 使用数组实现的约瑟夫环,设置模拟删除,降低时间复杂度
 * @author XY
 *
 */
public class JosephB {
	public static void main(String[] args) {
//		Scanner scan=new Scanner(System.in);
//		int N=scan.nextInt();
//		int cut=scan.nextInt();
		long time=System.currentTimeMillis();
		int N=10;
		int cut=4;//模拟输入值
		int[] num=new int[N];
		for (int i = 0; i < num.length; i++) {
			num[i]=i+1;//存入的内容不同
		}
		num[N-1]=0;//循环
		StringBuffer sb=new StringBuffer();
		int pre=N-1,now=0,flag=0,res=N;//前驱结点,现结点,删除标,剩余长度
		while(res>0){
			++flag;
			if(flag==cut){
				num[pre]=num[now];//删除操作
				flag=0;res--;
				sb.append(now+1+" ");
			}
			pre=now;
			now=num[now];//遍历
		}
		System.out.println((System.currentTimeMillis()-time)/1000.0);
		System.out.println(sb);
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值