问题描述:有n个人围成一个环循环报数,每次报数为m的人出局,剩下的人继续游戏,直到最后只剩一个人为止,返回胜利者的下标。
1.普通实现
思路:
设每个人的起始数据都是0,设置一个计数器,当计数器为m的时候把对应的人的数据设置为1,返回最后对应数据为0的人
的下标。此方法并不会删除数据,只是将对应的数据改变,最后当设置的循环条件不满足时,遍历全部数据找到那个与初始值
相同的元素即可。
看程序:
public class JosephTest{
public static int reverse(int n, int m) {
// 设置计数器
int count = 0;
// 定义指针
int pos = 0;
// 每个人的默认初始值为0
int[] arr = new int[n];
// 剩余的人
int left = arr.length;
while(left > 1) {
if(arr[pos] == 0) {
count++;
}
// 计数器为m时,修改对应的数据,计数器置0,剩余人数减1
if(count == m) {
arr[pos] = 1;
count = 0;
left--;
}
pos++;
// 指针循环
if(pos == arr.length) {
pos = 0;
}
}
/** 根据数据查找剩余的人 */
for(int x = 0; x < arr.length; x++) {
if(arr[x] == 0) {
return x;
}
}
return -1;
}
public static void main(String[] args) {
System.out.println(reverse(3, 3));
}
}
结果:1
2.双向链表实现
思路:
用一个循环的双向链表来保存对应的人,设结点的数据从0开始依次递增,就可以把结点数据当做下标来使用。设置计数器
的初始值为1,就包含了只有一个数据的情况,每当计数器的值为m,删除对应的结点,直到循环链表只剩一个结点为止。
先来看看同包中要用到的双向循环链表的代码:
class DoubleLink {
class Entry {
int data;
Entry next;
Entry prev;
public Entry() {
data = -1;
next = null;
prev = null;
}
public Entry(int data) {
this.data = data;
next = null;
prev = null;
}
}
public Entry head;
public DoubleLink() {
head = new Entry();
}
/**
* 尾插法
*/
public void insertTail(int data) {
Entry cur = head;
while (cur.next != null) {
cur = cur.next;
}
Entry entry = new Entry(data);
cur.next = entry;
entry.prev = cur;
}
/**
* 制作环
*/
public void makeLoop() {
Entry cur = head;
while (cur.next != null) {
cur = cur.next;
}
cur.next = head.next;
head.next.prev = cur;
}
/**
* 判断是否是环
*/
public boolean isLoop() {
Entry fast = head;
Entry slow = head;
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
}
/**
* 求环的长度
*/
public int getLoopLength() {
int len = 0;
boolean flag = false;
Entry fast = head;
Entry slow = head;
if (!isLoop()) {
return -1;
}
while (isLoop()) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow && flag == true) {
break;
}
if (fast == slow && flag == false) {
flag = true;
}
if (flag == true) {
len++;
}
}
return len;
}
}
看看约瑟夫环的实现:
public class JosephLinkTest {
public static void JosepLink(int n, int m) {
DoubleLink link = new DoubleLink();
// 对链表动态赋值,数据可以当做结点的序号来使用
for (int i = 0; i < n; i++) {
link.insertTail(i);
}
// 制作链表环
link.makeLoop();
// 定义开始指针指向第一个结点,计数器置1
DoubleLink.Entry cur = link.head.next;
int count = 1;
while (link.getLoopLength() > 1) {
// 当计数器等于约定的值时,删除对应结点
if (count == m) {
cur.prev.next = cur.next;
cur.next.prev = cur.prev;
cur = cur.next;
count = 1;
}
cur = cur.next;
count++;
}
// 输出剩余结点的值
System.out.println(cur.data);
}
public static void main(String[] args) {
JosepLink(1, 3);
}
}
结果:0