num个人围成一圈,从start位置开始报数,每报到step的人就出局,直到剩下最后一人。
约瑟夫环问题,可以使用链表和数组方式解决方式:
链表方式好理解,两个指针跟着走,一步一步报数,出圈,直至两个指针指向同一个位置为止;
数组方式,不需要一步一步跟着报数,直接取模移到需要出圈的位置,数据出圈,构建新的数组,再报数,出圈,直至数组长度为1为止;区别在于:数组取模方式不用跟着数,对于num较大step较大的场景更省时间;网上还有另外一种直接用数学公式计算的方式,算法更省时间;
双指针链表方式解决 JosephLinked.java
/**
* 节点
*/
class SignLinkedNode implements Serializable {
public Integer value;
//下一个节点
public SignLinkedNode next;
public SignLinkedNode() {
}
public SignLinkedNode(Integer value) {
this.value = value;
}
@Override
public String toString() {
return String.valueOf(value);
}
}
/**
* 约瑟夫环问题-链表解法
* num个人围成一圈,从s开始报数,每报到step的人就出局,直到剩下最后一人
*/
public class JosephLinked {
//头指针
SignLinkedNode head;
//尾指针
SignLinkedNode current;
//链表长度
int size;
/**
* 添加节点
* @param node
*/
public void add(SignLinkedNode node) {
if (size == 0) {
head = node;
current = node;
} else {
current.next = node;
}
size++;
//尾指针指向最后一个元素
node.next = head;
//当前指向该元素
current = node;
}
/**
* 遍历节点
*/
public void show() {
SignLinkedNode temp = head;
do {
System.out.printf(temp + ", ");
temp = temp.next;
} while (temp != head);
System.out.println();
}
/**
* 移动指针
* @param start
* @param step
*/
public void run(int start, int step) {
int i = 0;
//2个指针都移动到起点
while (i != start) {
head = head.next;
current = current.next;
i++;
}
i = 1;//从1开始报数
//报数到step
while (i != step) {
head = head.next;
current = current.next;
i++;
}
//出局人
System.out.println("出局人: " + head);
//移除人
current.next = head.next;
head = head.next;
size--;
if (current == head) {
System.out.println("最后出局人: " + head);
return;
}
//起始位置开始数
run(0, step);
}
/**
* 数组实现
*
* @param start 起点
* @param step 出局报数
* @return 位置从0开始
*/
private static int initJoseph(int num, int start, int step) {
//头结点
JosephLinked josephLinked = new JosephLinked();
for (int i = 0; i < num; i++) {
josephLinked.add(new SignLinkedNode(i + 1));
}
//遍历输出
josephLinked.show();
//开始报数
josephLinked.run(start, step);
return josephLinked.head.value;
}
public static void main(String[] args) {
// System.out.println(initJoseph(523, 24, 79));
System.out.println(initJoseph(5, 2, 2));
}
}
数组取模方式解决 JosephArray.java
/**
* 约瑟夫环问题
* num个人围成一圈,从s开始报数,每报到step的人就出局,直到剩下最后一人
*/
public class JosephArray {
/**
* 数组实现
*
* @param arr 人
* @param start 起点
* @param step 出局报数
* @return 位置从0开始
*/
private static int jose(int[] arr, int start, int step) {
// System.out.println(Arrays.toString(arr));
//圈子只剩1人
if (arr.length == 1) {
System.out.println("最后出局人: " + arr[0]);
return arr[0];
}
//出局人的位置
int i = (start + step - 1) % arr.length;
//出局人
System.out.println("出局人: " + arr[i]);
//下一个人起始
int w = i % (arr.length - 1);
//新环-缩小一个人
int[] arr2 = new int[arr.length - 1];
for (int j = 0, c = 0; j < arr.length; j++) {
if (j != i) {
arr2[c++] = arr[j];
}
}
return jose(arr2, w, step);
}
private static int initJoseph(int num, int start, int step) {
//初始化人
int[] arr = new int[num];
for (int i = 0; i < num; i++) {
arr[i] = i + 1;
}
return jose(arr, start, step);
}
public static void main(String[] args) {
// System.out.println(initJoseph(523, 24, 79));
System.out.println(initJoseph(5, 2, 2));
}
}