约瑟夫环(数三退一)

 

题目是这样(没什么注释):

数三退一问题是:

有一圈(500人)孩子,手拉手围成一个圈,从第一个孩子开始数1,第二个孩子数2,第三个孩子数3,这时候数3的孩子退出,从下一个孩子开始数1,一直循环,直到最后剩下一个孩子,问这个孩子的位置?

加1主要是因为编号是从 1 开始的,而不是从 0 开始的,因为求出的结果是数组中的下标

第一种解法(for循环):

public void result(){
    int sum = 0;
    for (int i = 2; i <= 500; i++) {
        sum = (sum + 3) % i;
    }
    System.out.println(sum + 1);   //输出剩下的那个人的位置
}
// 递归方式
public int f(int n, int m) {
    if(n == 1)   return n;
    return (f(n - 1, m) + m - 1) % n + 1;
}

第二种解法(使用集合):

public static void yuesefu(int totalNum, int countNum, int startNO) {
		// 初始化人数
		List<Integer> start = new ArrayList<Integer>();
		for (int i = 1; i <= totalNum; i++) {
			start.add(i);
		}
		// 从下标为K开始计数
		int k = startNO - 1;
		while (start.size() > 0) {
			System.out.println(start);
			// 第m人的索引位置
			k = (k + countNum) % (start.size()) - 1;
			// 判断是否到队尾 到队尾时候k=-1
			if (k < 0) {
				System.out.println(start.get(start.size() - 1));
				start.remove(start.size() - 1);
				k = 0;
			} else {
				System.out.println(start.get(k));
				start.remove(k);
			}
		}
	}

public static void main(String[] args){
    yuesefu(500, 3, 1);
}

第三种解法(使用数组):

    public void result2(){
        boolean[] kids;    
		kids = new boolean[500];    //声明一个500人的数组

		for (int i = 0; i < kids.length; i++) {
			kids[i] = true;
		}
		int leftNum = kids.length;
		int countNum = 0;
		int index = 0;
		while (leftNum > 1) {
			if (kids[index]) {
				countNum++;
			}
			if (countNum == 3) {
				kids[index] = false;
				countNum = 0;
				leftNum--;
			}
			index++;
			if (index == 500) {
				index = 0;
			}
		}

		for (int i = 0; i < kids.length; i++) {
			if (kids[i]) {
				System.out.println(i+1);
			}
		}
    }

第四种解法(使用面向对象方法): 

public static void main(String[] args) {
		// 数3退1;每数到3就退出一个,面向对象的方法
		// 面向对象,考虑问题中出现的名词:小孩类,圈类
		KidCircle kc = new KidCircle(500);
		Kid k = kc.first;// 先取出kc的第一个孩子
		int countNum = 0;// 用来数数,数到3退出

		while (kc.count > 1) {
			countNum++;
			if (countNum == 3) {
				countNum = 0;
				kc.delete(k);
			}
			k = k.right;// 因为是圆环,所以,每次取k孩子的右孩子
		}
		System.out.println("剩余的小孩的编号为:" + (kc.first.id+1));

	}

}

// 小孩类
class Kid {
	int id;// 小孩原始编号
	/*
	 * 每个小孩都有左小孩和右相邻小孩
	 */
	Kid left;
	Kid right;
}

// 圈类
class KidCircle {
	int count = 0;// 小孩编号
	Kid first;// 圈中第一个小孩
	Kid last;// 圈中最后一个小孩

	/*
	 * 写完成员变量以后首先完成其构造方法
	 */
	public KidCircle(int n)// 构造方法一般用来初始化
	{
		// 构造n个小孩的圈
		for (int i = 0; i < n; i++) {
			add();
		}
	}

	// 向圈中添加小孩
	void add() {
		Kid k = new Kid();
		k.id = count;
		if (count <= 0) // 当前圈中没有小孩
		{
			first = k;
			last = k;
			k.left = k;
			k.right = k;
		} else // 当前圈中有小孩情况
		{
			last.right = k;
			k.left = last;
			k.right = first;
			first.left = k;
			last = k;
		}
		count++;
	}

	// 向圈中删除小孩
	void delete(Kid k) {
		if (count <= 0)
			return;
		else if (count == 1) // 这个地方为什么
		{
			first = last = null;
		} else {
			k.left.right = k.right;
			k.right.left = k.left;
			if (k == first) // 当要删除小孩为第一个小孩时
				first = k.right;
			else if (k == last) {
				last = k.left;
			}
		}
		count--;
	}
代码运行结果:436

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值