1. 程式人生 > 實用技巧 >單向環形連結串列

單向環形連結串列

單向環形連結串列介紹

單向環形連結串列可以理解為單鏈表首尾相連的連結串列.

建立環形連結串列

  • 構建一個單向環形連結串列思路
    • 先建立第一個節點,讓first指向該節點,並形成環形 first.next = first
    • 新增輔助指標curBoy = first 待新增的節點為boy
    • 新增節點 curBoy.next = boy;boy.next = first;(成環) curBoy = boy;(指向下一個節點)
  • 遍歷
    • 先讓一個輔助針織curBoy指向first節點
    • 然後通過while迴圈遍歷該環形連結串列即可,結束條件為:curBoy.next == first

josephu問題

設編號為1,2,3......n的n個人圍坐一圈,約定編號為k(1<=k<=n)的人從1開始報數,數到m的那個人出列,他的下一位從1開始報數,數到m的那個人又出列,以此類推,知道所有人出列為止,由此產生一個出隊編號的序列.

  • 提示:用一個不帶頭結點的迴圈連結串列來處理josephu問題:先構建一個有n和節點的單迴圈連結串列,然後由k節點起從1開始計數,計數到m,對應節點從連結串列中刪除,然後被刪除的下一個節點又從1開始計數,知道最後一個節點從連結串列中刪除.

  • 需要建立一個輔助指標helper,事先應該指向環形連結串列的最後一個節點 (遍歷實現 結束條件 helper.next = first)

  • 報數前,先讓first和helper移動k-1次 (這是題中的從第k個人開始報數)

  • 當小孩報數時,讓first和helper指標同時移動m-1次

  • 這時就將first指向的小孩出圈.

    • first = first.next;
    • helper.next = first

程式碼實現

public class circleLinkedList {
    public static void main(String[] args) {

        CircleLinked circleLinked = new CircleLinked();
        // 測試新增
        circleLinked.add(5);
        circleLinked.list();
        System.out.println("測試出圈----------------");
        //測試出圈
        circleLinked.countBoy(5,1,2);
    }
}

//迴圈連結串列
class CircleLinked{
    //第一個節點 設定為null 是因為下面會進行復制
    private Boy first = null;

    /**
     *
     * @param num 向環形連結串列新增的節點數目
     */
    public void add(int num){
        if (num < 1){
            System.out.println("輸入無效資料!!");
            return;
        }
        Boy curBoy = null;  // 新增輔助指標
        for (int i = 1;i <= num;i++){
            Boy boy = new Boy(i);
            //第一個節點
            if (first == null){
                first = boy;
                first.setNext(first);
                curBoy = first;  //讓輔助指標指向first
            }else {
                curBoy.setNext(boy);
                boy.setNext(first);  //尾節點指向頭結點,成環
                curBoy = boy;  //指向下一個節點
            }
        }
    }
    //顯示連結串列
    public void list(){
        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();
        }
    }

    /**
     *
     * @param n 總共有多少和小孩
     * @param k 從第幾個人開始報數
     * @param m 數幾下
     */
    public void countBoy(int n,int k,int m){
        //校驗資料
        if (first == null || k < 1 || k > n){
            System.out.println("資料有誤");
            return;
        }
        Boy helper = first;
        //helper指向環形連結串列的最後一個節點
        while (true){
            if (helper.getNext() == first){
                break;
            }
            helper = helper.getNext();
        }
        ///小孩報數前,先讓 first 和 helper 移動 k - 1 次
        for (int i = 0;i < k-1;i++){
            first = first.getNext();
            helper = helper.getNext();
        }
        while (true){
            if (helper == first){ //圈中只有一個節點
                break;
            }

            //first 和 helper 移動m-1此
            for (int i = 0;i < m-1;i++){
                first = first.getNext();
                helper = helper.getNext();
            }
            //出圈小孩
            System.out.printf("出圈編號為%d\n",first.getNo());
            first = first.getNext();
            helper.setNext(first);
        }
        System.out.printf("出圈編號為%d\n",first.getNo());
    }
}

class Boy{
    private int no;
    private Boy next;

    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;
    }
}