1.6 單向環形連結串列和約瑟夫問題
阿新 • • 發佈:2020-08-28
-
基本介紹
- 單向迴圈連結串列和單向連結串列基本一樣,尾節點的next指向第一個節點
- 當連結串列中只有一個節點時,該節點的next指向本身
-
約瑟夫問題
- 基本介紹
- 設編號為 1,2,… n 的n個人圍坐一圈,約定編號為 k(1<=k<=n)的人從1開始報數,數到m的那個人出列,它的下一位又從1開始報數,數到m的那個人又出列,依次類推,直到所有人出列為止,由此產生一個出隊編號的序列
- 例如:編號為1,2,3,4,5 從1開始,數2次,則出隊編號為2,4,1,5,3
- 實現思路
- 採用單向迴圈連結串列,將數到那個在環形連結串列中刪除後,重新構建環形連結串列,直到剩下最後一個節點
- 由於採用的單向連結串列,所以需要兩個指標,current指向當前的節點(也就是要出隊的節點,初始值為head節點),currentBefore指向當前節點的前一個節點(用來刪除當前節點,初始值為head節點的前一個節點)
- 將兩個節點同時移動k-1次,第一次的報號位置,由於本身也要報號,所以k-1次(兩個指標必須相連,且current在後)
- 開始報號,將兩個節點同時移動k-1次(原理同上)
- 出隊current = current.next; currentBefore = current;
- 當current == currentBefore; 佇列中就只剩下了一個節點(結束)
- 基本介紹
-
程式碼實現
-
public class CircleSingleLinkedListDemo { public static void main(String[] args) { CircleSingleLinkedLis circleSingleLinkedLis = new CircleSingleLinkedLis(); // 建立並顯示單向環形連結串列 circleSingleLinkedLis.create(10); circleSingleLinkedLis.show(); // 實現約瑟夫問題 System.out.println("---約瑟夫問題---"); circleSingleLinkedLis.josephu(5, 10, circleSingleLinkedLis.size()); } } // 單向環形連結串列類 class CircleSingleLinkedLis { private Children head; // 頭節點,將加入的第一個節點作為頭節點(不能動,在約瑟夫環中可以動) // 建立單向環形連結串列(引數為有幾個節點) public void create(int number) { if (number < 2) { System.out.println("至少兩個節點"); return; } Children current = null; // 尾指標 指向下一個節點為頭節點的節點(方便插入,不需要每次都遍歷) for (int i = 1; i <= number; i++) { // i作為節點的編號 Children children = new Children(i); if (null == head) { // 第一個節點 自己指向自己 單獨處理也是為了防止空指標 head = children; children.next = head; current = head; } else { // 先將當前節點的下一個節點指向要新增的節點,再後移當前節點,再將當前節點的下一個節點指向頭節點 current.next = children; current = current.next; children.next = head; } } } // 顯示單向環形連結串列 public void show() { if (null == head) { System.out.println("單向環形連結串列為空"); } Children temp = head; while (null != head) { System.out.println(temp); temp = temp.next; // 後移節點 if (temp == head) { break; // 到頭節點了 退出 } } } // 獲取連結串列節點個數 public int size() { int sum = 0; Children temp = head; while (null != temp ) { sum++; temp = temp.next; if (temp == head) { break; } } return sum; } /** * 約瑟夫問題實現(一群小孩圍在一起,由第k個小孩報n下數,停止報數的那個小孩出圈,問小孩出圈的順序) * 採用兩個指標(一個指向當前報數的孩子,另一個指向報數孩子的前一個孩子,因為單鏈表刪除必須拿到當前節點的前一個節點) * @param start 開始報數的節點 * @param count 報多少下 * @param size 總共有多少個小孩(此處不能連結串列的有效個數) */ public void josephu(int start, int count, int size) { if (null == head || start < 1 || start > size || size > this.size()) { System.out.println("輸入的資料不合法"); return; } Children current = head; // 當前節點 Children currentBefore = current; // 當前節點的前一個節點 while (currentBefore.next != head) { // 尋找當前節點的前一個節點 currentBefore = currentBefore.next; // 指標後移 } for (int i = 0; i < start - 1; i++) { // 將兩個指標移動到起始位置 例 1 -> 3 需移動2次 則是 start - 1 current = current.next; currentBefore = currentBefore.next; } while (current != currentBefore) { // 如果圈中只剩一個小孩 則兩個指標重合 // 從起始位置報數 由於起始節點本身也要報數 則是移動 count - 1 for (int i = 0; i < count - 1; i++) { current = current.next; currentBefore = currentBefore.next; } // 報數完畢 移除當前節點 並重新構建連結串列 System.out.println(current); current = current.next; // 後移當前指標 currentBefore.next = current; // 重新構建連結串列 } System.out.println(current); // 輸出圈中最後一個節點的資訊 } } // 連結串列節點類 class Children { public int no; public Children next; // 指向下一個節點 public Children(int no) { this.no = no; } @Override public String toString() { return "Children{" + "no=" + no + '}'; } }