【問題】
首先,讓小朋友們圍成一個大圈。然後,隨機指定一個數 m, 讓編號爲 0 的小朋友開始報數。每次喊到 m-1 的那個小朋友要出列唱首歌,並且不再回到圈中,從他的下一個小朋友開始,繼續 0…m-1 報數… 這樣下去… 直到剩下最後一個小朋友,哪個小朋友會在最後表演呢?(注:小朋友的編號是從 0 到 n-1)
如果沒有小朋友,請返回 - 1
【題解一、數組模擬環】
public int Solution1(int n, int m) {
if (n == 0) {
return -1;
}
int k,index,count;
boolean[] flag = new boolean[n];
k = 0; // 記錄循環次數
index = 0; // 遊標,記錄數組下標
count = 0; // 記錄跳出個數
while (count < n-1) {
count ++;
// 當k=m的時候,不進入循環
while (k < m) {
// 當前數值未出列,則循環次數+1,當前數值已出列,則不處理
if (!flag[index]) {
// 跳出該數值
if (k == m-1) {
flag[index] = true;
}
k++;
}
index ++;
if (index == n) {
index = 0;
}
}
k = 0;
}
index = 0;
while (index < n) {
if (!flag[index]) break;
index ++;
}
return index;
}
PS:可以用list(arraylist或者linkedlist來代替普通數組、這類動態數組在該類問題上可以提升效率)
【題解二、鏈表模擬】
class ListNode {
int val;
ListNode next = null;
ListNode(int val) {
this.val = val;
}
}
public class Solution {
// 鏈表解法一
public int Solution2(int n, int m) {
if (n <= 0 || m <= 0) {
return -1;
}
ListNode head = new ListNode(0);
ListNode node = head;
for (int i = 1; i < n; i++) {
node.next = new ListNode(i);
node = node.next;
}
node.next = head;
int k = 0;
while (node.next != node) {
if (++k == m) {
node.next = node.next.next;
k = 0;
} else {
node = node.next;
}
}
return node.val;
}
// 鏈表解法二
public int Solution3(int n, int m) {
int index,count,k;
// 建立鏈表
ListNode p; // 鏈表遊標
ListNode head = new ListNode(0); // 表頭
p = head;
index = 1;
while (index < n) {
ListNode l = new ListNode(index);
p.next = l;
p = p.next;
index ++;
}
p.next = head; // 構成閉環
k = 0;
p = head;
// 當只有一個節點的時候跳出
while (p.next != p) {
while (k < m - 2) {
p = p.next;
k ++;
}
k = 0;
// m-1的節點跳出
p.next = p.next.next;
// 把遊標P點位到m位置
p = p.next;
}
return p.val;
}
}
【題解三、數學算式】
f(N,M) = ( f(N−1,M) + M ) % N
// 非遞歸
public int Solution4(int n, int m) {
if (n <= 0 || m <= 0) {
return -1;
}
int num = 0;
for (int i = 2; i <= n; i++) {
num = (num + m) % i;
}
return num;
}
// 遞歸算法
public int Solution5(int n, int m) {
if (n <= 0 || m <= 0) {
return -1;
}
if (n == 1) return 0;
return (Solution5(n-1,m) + m) % n;
}