約瑟夫環問題
已知 n 個人(n>=1)圍坐一圓桌周圍,從 1 開始順序編號,從序號為 1 的人開始報數,順時針數到 m 的那個人出列。下一個人又從 1 開始報數,數到m 的那個人又出列。依此規則重復下去,直到所有人全部出列。請問最后一個出列的人的初始編號。
要求
輸入人數 n,所報數 m,輸出最后一個人的初始編號。
解決思路
- 首先因為是圓桌問題,使用鏈表解決的話需要構建循環鏈表。
- 接着是出列問題,這里我的設計思路是將指向鏈表的指針移動到需要出列的人的位置,然后根據正常的鏈表刪除進行操作即可。
- 最后是類的設計,可以將 head 、 m、 n、鏈表構建、鏈表刪除等整合到一起進行封裝。
代碼
1 // 6.cpp -- using gcc compiler 2 #include <iostream> 3 using namespace std; 4 5 typedef struct List 6 { 7 int num; 8 struct List *next; 9 }*pList; 10 11 class Josephus 12 { 13 public: 14 Josephus() {} 15 Josephus(int number, int mes): 16 n(number), 17 m(mes){} 18 void set(); 19 void creat(); 20 void del(); 21 private: 22 pList head; 23 int n, m, tmp_n; 24 }; 25 26 void Josephus::set() 27 { 28 cout << "please input the number of the people: "; 29 cin >> n; 30 tmp_n = n; 31 cout << "please input the m: "; 32 cin >> m; 33 } 34 35 void Josephus::creat() 36 { 37 pList p1, p2; 38 pList p = new List; 39 n += 1; 40 p -> num = 1; 41 p2 = head = p; 42 for(int i = 2; i < n; i++) 43 { 44 p = new List; 45 p -> num = i; 46 47 p1 = p2; 48 p2 = p; 49 p1 -> next = p2; 50 } 51 52 p2 -> next = head; 53 // output each number of the circle list's members. 54 // and it should be: 1, 2, ..., n 55 p = head; 56 cout << "Now, the \"num\" member of the list is: " << endl; 57 while(n--) 58 { 59 if(n == 0) 60 cout << p-> num << "."; 61 else 62 { 63 cout << p->num << ", "; 64 p = p -> next; 65 } 66 } 67 } 68 69 void Josephus::del() 70 { 71 pList p1 = NULL; 72 pList p2 = head; 73 74 n = tmp_n + 1; 75 while(n--) 76 { 77 int s = m - 1; 78 while(s--) 79 { 80 p1 = p2; 81 p2 = p2 -> next; 82 } 83 84 if(n == 0) 85 { 86 p2 = p2 -> next; 87 p1 -> next = NULL; 88 cout << "The result is: " << p2 -> num << endl; 89 } 90 else 91 { 92 p2 = p2 -> next; 93 p1 -> next = p2; 94 } 95 } 96 } 97 98 int main() 99 { 100 Josephus t; 101 t.set(); 102 t.creat(); 103 cout << endl; 104 t.del(); 105 return 0; 106 }
代碼解析
5-9行定義了鏈表的結構,其中 num就是初始編號
23行的 tmp_n 負責記錄 n 的初始值
75行開始進行循環,共循環 n+1 次,每次都將 p2 移動到需要被排除的人的位置。
n=0時為最后一次循環,此時p1、p2都指向最后一個人,輸出 num。
n!=0則去掉p2指向的節點,開始下一次循環。
解決約瑟夫環問題的關鍵在於去掉計數為 m 的那個人,並從他之后開始重新計數。
而以上的解決方案巧妙的利用了循環鏈表來解決問題。因為對鏈表的了解不算深入,所以暫時還想不出更好的解決方案。
總結
解決鏈表問題的關鍵在於對鏈表的構建以及其他操作,而鏈表的問題只靠想象是不可能的解決的,最好還是自己動手畫圖,這樣一切都會變得清晰明了,問題也會迎刃而解。