Java學習——資料結構之約瑟夫問題
阿新 • • 發佈:2021-11-30
約瑟夫 問題描述:設編號為1、2、3... ...n的n個人圍坐一圈,約定編號為k(1<=k<=n)的人從1開始報數,數到m的那個人先出列,他的下一位又從1開始報數,數到m的那個人又出列,依次類推,直到所有人都出列為止,由此產生一個出隊編號的序列,求此序列。
演算法思路:
先構成一個有n個結點的單迴圈連結串列,然後由k結點起從1開始計數,計到m時,儲存對應節點的編號,並將節點從連結串列中刪除,然後再從被刪除結點的下一個結點又從1開始計數,直到最後一個結點從連結串列刪除。
程式碼分析:
1.構建環形連結串列:
-
建立第一個結點,並讓first指向該結點,並將該節點的next域指向自己形成環形。
-
每建立一個新節點,就把該節點加入到已有的環形連結串列中
2.遍歷環形連結串列
-
定義一個輔助變數curBoy ,指向first節點
-
通過while迴圈找到最後一個加入環形連結串列的結點,並將curBoy指向這個節點,即:curBoy.next==first
-
(新增這個輔助變數的目的是儲存first的前趨,便於刪除報數完畢後first指向的結點)
3.報數
-
每一輪報數時,使用for迴圈m次,每迴圈一次,first和curBoy同時往下一位報數的人的方向移動一次。
-
當迴圈完畢m次時,儲存first的編號,然後刪除first結點:
-
int n=first.no
-
first=first.next;
-
curBoy.next=first
-
-
每一輪報數結束,使用陣列儲存出列的編號,當圈中只剩下一個人時,直接將其編號新增至陣列,最後依次列印陣列的值即為出隊編號的序列。
程式碼實現
結點:
//建立一個Boy類,表示一個節點 class Boy{ private int no; //編號 private Boy next; //指向下一個結點,預設null public Boy(int no) { this.no = no; } public int getNo() {return no; } public Boy getNext() { return next; } public void setNext(Boy next) { this.next = next; } }
連結串列:
//建立一個環形的單向連結串列 class CircleLinkedList{ //建立一個first結點 當前沒有編號 private Boy first = null; //新增小孩結點 構建一個環形的連結串列 public void addBoy(int nums){ //nums代表加入小孩的個數 if(nums<1){ System.out.println("nums的值不正確,新增失敗"); return; } Boy curBoy = null; //輔助變數,幫助建立環形連結串列 //使用for迴圈來建立環形連結串列 for(int i=1;i<=nums;i++) { Boy boy = new Boy(i); //如果是第一個小孩 if(i==1){ first=boy; first.setNext(first); //一個小孩也可以構成一個環狀 curBoy=first; }else { curBoy.setNext(boy); curBoy=boy; boy.setNext(first); } } } //遍歷當前的環形連結串列 public void showBoy(){ //判斷連結串列是否為空 if(first==null) { System.out.println("連結串列為空,沒有任何小孩!"); return; } Boy curBoy = first; while (true){ System.out.printf("小孩的編號:%d\n",curBoy.getNo()); if(curBoy.getNext()==first){ //說明已經遍歷完畢 break; } curBoy=curBoy.getNext(); //curBoy後移直到其next指向first } } //根據使用者的輸入,計算出小孩的出圈順序 /* startNo 開始的小孩的編號 countNo 表示數幾下 nums 表示最初的小孩的個數 */ public int[] countBoy(int startNo,int countNo,int nums){ Boy boyFirst = first; for (int i=1;i<startNo;i++) boyFirst=boyFirst.getNext(); //遍歷到開始報數的小孩 Boy pre=boyFirst; //記錄每一輪開始報數的前一個小孩,也就是每一圈的最後一個小孩 for (int i=1;i<nums;i++) pre = pre.getNext(); int[] boys = new int[nums]; //記錄出圈的編號序列 for (int j=0;j<nums;j++) { for (int i = 0; i < countNo - 1; i++) { //下一圈的開始和最後一起後移,一直移到出圈的小孩 pre = pre.getNext(); boyFirst = boyFirst.getNext(); } boys[j] = boyFirst.getNo();//記錄出圈的小孩編號 boyFirst=boyFirst.getNext();//刪除出圈小孩的結點 pre.setNext(boyFirst); } return boys; } }