03-n個數字形成的圓圈中循環刪除第m個數字

2020年2月24日找工作中金所的筆試題之一

題目描述

n個數字(1, 2, … , n)形成一個圓圈。從數字1開始,每次從這個圓圈中刪除第m個數字(第一個爲當前數字本身,第二個爲當前數字的下一個數字)。當一個數字刪除後,從被刪除數字的下一個繼續刪除第m個數字。求出在這個圓圈中剩下的最後一個數字。

思路

  • 思路1 個人筆試時候的做法,把問題複雜化了感覺
    (1) 用循環隊列,初始化隊列元素的值爲n個連續的數
    (2) 循環報數,用累加器來輔助找到每次需要被刪除的元素
    (3) 每次有人被淘汰,先把這個數刪除掉,隊列的長度減1
    (4) 當循環隊列長度爲1的時候,輸出僅存的這個數
    (5) 用數組模擬循環隊列 or 用鏈表模擬

思路2 後來網上查閱到的容易理解的做法
先構建遞推公式,再使用循環或者遞歸都能輕鬆求解。
這個問題的其實就是約瑟夫問題的小變形,數據範圍由約瑟夫問題的0到N-1,變爲了 1 到 N
由於我們要求解的是n個元素,第m個數字,要找到最後留下的一個數字,那麼我在這裏假設得到的結果是f(n,m),
根據“左加右減”的數學原理和環形的特徵,得到遞推公式:
{ 0 ,n = 1
f(n,m)= {
{ (f(n-1,m) + m)%n ,n > 1

根據這個來調整代碼,就可以得到數據範圍是1 到 N , 目標值是M的答案

代碼

思路1代碼

public class Joseph {
	public int getResult(int n, int m) {
		if (m == 1) {	// m 是 1,直接返回n 就行了,不需要多跑程序
			return n;
		}
		ListNode head = new ListNode(1);
		ListNode p = head;
		int counts = 1; // 創建用的計數器
		while (counts < n) {
			// 尾插法創建單鏈表
			ListNode q = new ListNode(counts + 1);
			q.next = p.next;
			p.next = q;
			p = p.next;
			counts ++;
		}
		// 使單鏈表變爲循環鏈表
		p.next = head;

		int count = 0; // 刪除用的計數器
		ListNode del = head;
		while (del.next != del) { // 使其一直遍歷、計數、刪除
			count++;
			System.out.println(del.val + " " + count);
			if (count == m) {
				// 刪除這個結點
				del.val = del.next.val;
				del.next = del.next.next;
				count = 1; // 計數器清零
			}
			del = del.next;
		}
		return del.val;
    }
}

思路2代碼

public class Jpseph2 {
	public int getResult(int n, int m) {
        int fn = 1;
        for (int i = 2; i <= n; i++) {
        	fn = (fn + m - 1) % i;
        }
		return fn;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章