1. 程式人生 > 其它 >學習筆記-雙向連結串列與環形連結串列

學習筆記-雙向連結串列與環形連結串列

技術標籤:連結串列資料結構java

雙向連結串列

在單鏈表的基礎上,每個節點增加了一個pre屬性指向前一個節點。

因為單鏈表只有next指標,因此只能單向遍歷。而雙向連結串列可以從前或從後遍歷。另外也可以自我刪除,也就是不用藉助被刪節點的前一個節點進行刪除,直接靠自己就行。

在這裡插入圖片描述

增刪改查

節點增加了pre指向前一個節點

//雙向連結串列的節點
class StudentNode2{
    public int no;
    public String name;
    StudentNode2 next;
    StudentNode2 pre;

    public StudentNode2
(int no, String name) { this.no = no; this.name = name; } @Override public String toString() { return "StudentNode{" + "no=" + no + ", name='" + name + '\'' + '}'; } }

先遍歷找到連結串列中的最後一個元素,再分別連上next和pre

//向連結串列中增加元素,直接加在末尾
    public void add(StudentNode2 node){
        //遍歷連結串列,找到連結串列的最後
        StudentNode2 temp=head;
        while (true){
            if (temp.next==null){
                break;
            }else if(temp.next.no==node.no){
                throw new RuntimeException("重複新增!"
); } //遍歷連結串列 temp=temp.next; } //temp指向連結串列最後一個元素,向temp後新增新元素 temp.next=node; node.pre=temp; }

找到新增位置的前一個,然後node.next=temp.next;連上後續不丟失,temp.next=node;與前面連線,node.pre = temp;反向連線,如果是加在連結串列的最末尾,後面是null,node.next有個空指標異常的問題,因此要加個判斷。

//向連結串列中有順序的新增元素,如果編號重複,新增失敗
    public void addByOrder(StudentNode2 node){
        //根據編號找到需要新增的位置
        StudentNode2 temp=head;
        while (true){
            if(temp.next==null){
                break;
            }
            if(temp.next.no>node.no){
                break;
            }
            if(temp.next.no==node.no){
                throw new RuntimeException("重複新增!");
            }
            temp=temp.next;
        }

        node.next=temp.next;
        temp.next=node;
        node.pre = temp;
        if(node.next!=null) {
            node.next.pre = node;
        }
    }

沒啥變化

//根據編號修改連結串列
    public void updata(int no,String name){
        //遍歷找到需要修改的節點
        StudentNode2 temp=head.next;
        //是否找到該節點
        boolean flag=false;
        while (true){
            if(temp==null){
                break;
            }else if(temp.no==no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){
            temp.name=name;
        }else {
            System.out.println("沒有這個編號,修改失敗");
        }
    }

可以自我刪除,因此temp直接指向要被刪除的節點,考慮刪的是最後一個節點,同樣有空指標異常的問題,要加一層判斷。

//根據編號刪除節點
    public void del(int no){
        //找到這個節點
        StudentNode2 temp=head.next;
        //是否找到該節點
        boolean flag=false;
        while (true){
            if(temp==null){
                break;
            }else if(temp.no==no){
                flag=true;
                break;
            }
            temp=temp.next;
        }
        if(flag){
            temp.pre.next=temp.next;
            if(temp.next!=null) {
                temp.next.pre = temp.pre;
            }
        }else {
            System.out.println("沒有這個編號,刪除失敗");
        }

    }

沒什麼變化

//顯示連結串列
    public void show(){
        //遍歷整個連結串列,並打出資訊
        StudentNode2 temp=head.next;
        while (true){
            if(temp==null){
                break;
            }
            System.out.println(temp);
            temp=temp.next;
        }
    }

環形連結串列

在這裡插入圖片描述
無頭結點的環形連結串列。最後一個節點next指向第一個節點,形成一個閉環。如果只有一個節點,就自己指向自己。

Josephu問題

Josephu 問題為:設編號為 1,2,… n 的 n 個人圍坐一圈,約定編號為 k(1<=k<=n)的人從 1 開始報數,數到m 的那個人出列,它的下一位又從 1 開始報數,數到 m 的那個人又出列,依次類推,直到所有人出列為止,由此
產生一個出隊編號的序列。

用一個不帶頭結點的迴圈連結串列來處理 Josephu 問題:先構成一個有 n 個結點的單迴圈連結串列,然後由 k 結點起從 1 開始計數,計到 m 時,對應結點從連結串列中刪除,然後再從被刪除結點的下一個結點又從 1 開始計數,直到最後一個結點從連結串列中刪除演算法結束。

程式碼實現

節點設定

//小孩節點
class Boy{
    public int no;
    public Boy next;
    public Boy(int no){
        this.no=no;
    }
}

建立一個有num個節點的迴圈連結串列。
cur-輔助指標-指向要插入的前一個節點
建立時,第一個節點是不一樣的,賦值,然後讓他指向自己形成閉環,然後讓cur指向first,附上初值。
接下來的每一個都是連上前後兩條線,然後讓cur移動到新的節點上。

//按照num建立環形連結串列
    public void add(int num){
        if(num<1){
            System.out.println("資料不對");
            return;
        }
        Boy cur=null;
        for(int i=1;i<=num;i++){
            Boy boy=new Boy(i);
            if(i==1){
                first=boy;
                first.next=first;
                cur=first;
            }else {
                cur.next = boy;
                boy.next = first;
                cur = boy;
            }
        }
    }

列印時first不變,藉助輔助變數,直接列印,當cur.next==first時,cur指向最後一個元素,結束迴圈。

public void show(){
        Boy cur=first;
        if(first==null){
            System.out.println("空");
            return;
        }
        while (true){
            System.out.println("編號:"+cur.no);
            if(cur.next==first){
                break;
            }
            cur=cur.next;
        }
    }

解決約瑟夫問題,k表示從第幾個人開始,m是間隔幾人,num是總人數,用來建立連結串列。
輔助指標cur永遠指向first前一個節點,也就是一開始指向最後一個節點。
首先把cur移動到末尾
然後first和cur同時移動k-1次,因為從他開始,要讓first指向那個開始的人。first一開始指向1,如果從3開始,只要移動2次。
然後開始出列,當cur=first時代表連結串列裡只剩下一個元素,迴圈停止。
出列時,cur和first同時移動m-1次,實現間隔,然後將first向前一位,cur.next=first刪去目標。

public void out(int k,int m,int num){
        if(k<1 | m>num | num<1 |k>num |m<1 |first==null){
            System.out.println("誤");
            return;
        }
        Boy cur=first;
        //cur去末尾
        while (true){
            if(cur.next==first){
                break;
            }
            cur=cur.next;
        }
        //同時移動到k-1處
        for (int i=0;i<k-1;i++){
            first=first.next;
            cur=cur.next;
        }
        //出列
        while (true){
            if(cur==first){
                System.out.println("剩下:"+cur.no);
                break;
            }
            for (int j=0;j<m-1;j++){
                first=first.next;
                cur=cur.next;
            }
            System.out.println("離開:"+first.no);
            first=first.next;
            cur.next=first;
        }
    }