單鏈表雙鏈表的實現和常見問題

什麼是鏈表?

1,鏈表是以節點的方式存儲的,鏈式存儲

2,每個節點包含data域,next域指向下一個節點

3,鏈表的各個節點不一定是鏈式存儲的

4,鏈表分帶頭節點的鏈表和不帶頭結點的鏈表

下面用代碼實現一個單向鏈表

首先定義一個類用來存儲到鏈表中

/**
 * 定義HeroNode,每個HeroNode對象就是一個節點
 */
public class HeroNode {
    public int no;//編號
    public String name;//名字
    public String nickName;//暱稱
    public HeroNode next;//指向的下一個節點

    public HeroNode(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

鏈表實現類

/**
 * 定義SingleLinkedList管理我們的英雄
 */
public class SingleLinkedList {

    //初始化一個頭節點,頭結點不動,不存放任何數據
    public HeroNode head = new HeroNode(0, "", "");


    /**
     * 添加節點到單向鏈表
     * 當不考慮編號順序時
     * 1,找到當前鏈表的最後節點
     * 2,將最後這個節點的next指向新的節點
     *
     * @param heroNode
     */
    public void add(HeroNode heroNode) {

        //因爲頭結點不能動,因此我們需要一個輔助遍歷的temp
        HeroNode temp = head;

        //遍歷鏈表,找到最後的節點
        while (true) {
            //如果指向下一個鏈表是null 就是最後一個
            if (temp.next == null) {
                break;
            }
            //如果不是空 就是還沒到最後一個,將temp後移
            temp = temp.next;
        }
        //當退出while循環時,temp就指向了鏈表的最後
        //將最後這個節點的next指向新的節點即可
        temp.next = heroNode;
    }


    //第二種插入方式在添加英雄時,根據排名將英雄輸入到指定位置
    public void addByOrder(HeroNode heroNode) {

        //使用輔助變量
        //我們找的temp是位於添加位置的前一個節點
        HeroNode temp = head;
        boolean flag = false;//如果要添加的編號已存在flag就變成true
        while (true) {
            if (temp.next == null) {//說明已經到了鏈表的最後
                break;
            }
            if (temp.next.no > heroNode.no) {//如果temp的下一個節點的編號大於要插入節點的編號,位置找到,在temp的後面插入
                break;
            }else if(temp.next.no == heroNode.no){//編號相同 已存在
                flag = true;
                break;
            }
            //三種條件都不滿足,指針後移
            temp = temp.next;
        }

        if(flag){//已存在 不能添加
            System.out.println("編號"+heroNode.no+"已存在");
        }else{
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }


    //根據編號修改
    public void edit(HeroNode newHeroNode){

        HeroNode temp = head.next;//直接指向頭結點的下一個
        boolean flag = false;//表示是否找到節點
        while(true){
            if(temp == null){
                break;//表示已經遍歷完鏈表
            }
            if(temp.no == newHeroNode.no){
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else{
            System.out.println("沒有找到編號"+newHeroNode.no+"的英雄");
        }
    }

    //刪除節點
    public void delete(int no){
        //輔助變量
        HeroNode temp = head;
        boolean flag = false;//標誌是否找到待刪除節點
        while(true){
            if(temp.next == null){
                break;
            }
            if(temp.next.no == no){
                //找到了待刪除節點的前一個節點temp
                flag = true;
                break;
            }
            //後移
            temp = temp.next;
        }

        if(flag){
            //跳過要刪除的節點,jvm垃圾回收機制會清理沒有作用的節點
            temp.next = temp.next.next;
        }else{
            System.out.println("要刪除的節點不存在");
        }

    }

    //顯示鏈表
    public void show() {
        //如果鏈表爲空就直接返回
        if (head.next == null) {
            System.out.println("鏈表爲空");
            return;
        }
        //再次使用一個輔助變量來遍歷
        HeroNode temp = head.next;
        while (true) {
            //判斷鏈表是否到最後
            if (temp == null) {
                break;
            }

            //輸出節點信息
            System.out.println(temp);
            //將temp後移
            temp = temp.next;
        }
    }

}

問題1,統計鏈表中有效節點的個數

    public static int getLength(SingleLinkedList link){
        if(link.head.next == null){
            //空鏈表 返回0
            return 0;
        }
        int length = 0;
        HeroNode current = link.head;
        //頭結點不是有效節點 不統計頭結點
        while(current.next != null){
            length += 1;
            current = current.next;
        }
        return length;
    }

問題2,獲取鏈表中倒數第k個元素的值

    public static HeroNode findLastIndexNode(SingleLinkedList link,int index){

        //如果鏈表爲空 直接返回null 沒找到
        if(link.head.next == null){
            return null;
        }
        //遍歷獲取鏈表的有效個數,調用剛纔的方法
        int size = getLength(link);
        //校驗數據是否合法
        if(index <= 0 || index > size){
            return null;
        }
        //遍歷size-index的位置,就是倒數第k個節點
        HeroNode current = link.head.next;
        for(int i = 0;i < size-index;i++){
            current = current.next;
        }
        return current;
    }

問題3,實現單鏈表的反轉

    public static void reverseList(SingleLinkedList link){
        //如果鏈表爲空,直接返回空
        if(link.head.next == null){
            return ;
        }

        //定義一個車輔助指針,幫助我們遍歷原來的鏈表
        HeroNode current = link.head.next;
        HeroNode next = null;//當前節點的下一個節點
        //定義一個新的頭結點
        HeroNode reverseHead = new HeroNode(0, "", "");

        //遍歷原來的鏈表,每遍歷一個節點,就將其取出,並放在新的頭結點的最前端
        while (current != null){
            next = current.next;//暫時保存當前節點的下一個節點
            current.next = reverseHead.next;//將當前節點的下一個節點指向新的連別的最前端
            reverseHead.next = current;//再將新的鏈表的最前端指向當前節點
            current = next;//當前節點後移
        }

        //原來的頭結點指向新的頭結點,實現反轉
        link.head.next = reverseHead.next;
    }

問題4,反向打印單鏈表

對於這個問題,我們可以對鏈表先進行反轉然後再遍歷輸出,但是這樣會破壞原來鏈表的結構,沒必要。

所以採取另外一種方法,利用棧的先進後出的特性,把鏈表中的元素壓入棧中在取出打印。

    public static void reversePrint(SingleLinkedList link){
        if(link.head.next == null){
            return ;
        }
        Stack<HeroNode> stack = new Stack<>();
        HeroNode current = link.head.next;
        while(current != null){
            stack.push(current);//添加到棧
            current = current.next;//後移
        }
        while (!stack.empty()){
            System.out.println(stack.pop());
        }
    }

雙向鏈表的定義及基本操作

首先定義一個節點類

public class HeroNode2 {
    public int no;//編號
    public String name;//名字
    public String nickName;//暱稱
    public HeroNode2 next;//指向當前節點的下一個節點
    public HeroNode2 pre;//指向當前節點的前一個節點

    public HeroNode2(int no, String name, String nickName) {
        this.no = no;
        this.name = name;
        this.nickName = nickName;
    }

    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickName='" + nickName + '\'' +
                '}';
    }
}

雙向鏈表

public class DoubleLinkedList {
    //初始化一個頭節點,頭結點不動,不存放任何數據
    public HeroNode2 head = new HeroNode2(0, "", "");

    //顯示鏈表
    public void show() {
        //如果鏈表爲空就直接返回
        if (head.next == null) {
            System.out.println("鏈表爲空");
            return;
        }
        //再次使用一個輔助變量來遍歷
        HeroNode2 temp = head.next;
        while (true) {
            //判斷鏈表是否到最後
            if (temp == null) {
                break;
            }

            //輸出節點信息
            System.out.println(temp);
            //將temp後移
            temp = temp.next;
        }
    }

    //在雙向鏈表的最後添加一個節點
    public void add(HeroNode2 heroNode) {

        //因爲頭結點不能動,因此我們需要一個輔助遍歷的temp
        HeroNode2 temp = head;

        //遍歷鏈表,找到最後的節點
        while (true) {
            //如果指向下一個鏈表是null 就是最後一個
            if (temp.next == null) {
                break;
            }
            //如果不是空 就是還沒到最後一個,將temp後移
            temp = temp.next;
        }
        //當退出while循環時,temp就指向了鏈表的最後

        temp.next = heroNode;
        heroNode.pre = temp;
    }

    //根據編號修改
    public void edit(HeroNode2 newHeroNode){

        HeroNode2 temp = head.next;//直接指向頭結點的下一個
        boolean flag = false;//表示是否找到節點
        while(true){
            if(temp == null){
                break;//表示已經遍歷完鏈表
            }
            if(temp.no == newHeroNode.no){
                //找到
                flag = true;
                break;
            }
            temp = temp.next;
        }
        if(flag){
            temp.name = newHeroNode.name;
            temp.nickName = newHeroNode.nickName;
        }else{
            System.out.println("沒有找到編號"+newHeroNode.no+"的英雄");
        }
    }

    //刪除節點
    //找到待刪除的節點,自我刪除即可
    public void delete(int no){
        //輔助變量
        HeroNode2 temp = head.next;
        boolean flag = false;//標誌是否找到待刪除節點
        while(true){
            if(temp == null){
                break;
            }
            if(temp.no == no){
                //找到了待刪除節點的前一個節點temp
                flag = true;
                break;
            }
            //後移
            temp = temp.next;
        }

        if(flag){
            temp.pre.next = temp.next;

            //如果要刪除的是最後一個節點,就不能執行下面一句操作(待刪除節點後一個節點的pre指向待刪除節點的前一個節點)
            if(temp.next != null){
                temp.next.pre = temp.pre;
            }
        }else{
            System.out.println("要刪除的節點不存在");
        }

    }

    //按順序添加
    public void addByOrder(HeroNode2 node){
        //使用輔助變量
        //我們找的temp是位於添加位置的前一個節點
        HeroNode2 temp = head;
        boolean flag = false;//如果要添加的編號已存在flag就變成true
        while (true) {
            if (temp.next == null) {//說明已經到了鏈表的最後
                break;
            }
            if (temp.next.no > node.no) {//如果temp的下一個節點的編號大於要插入節點的編號,位置找到,在temp的後面插入
                break;
            }else if(temp.next.no == node.no){//編號相同 已存在
                flag = true;
                break;
            }
            //三種條件都不滿足,指針後移
            temp = temp.next;
        }

        if(flag){//已存在 不能添加
            System.out.println("編號"+node.no+"已存在");
        }else{
            node.next = temp.next;
            node.pre = temp;
            temp.next = node;
            temp.next.pre = node;
        }
    }

}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章