重學數據結構 --- 單鏈表

鏈表

簡介

  • 鏈表是數據結構中一種十分重要的數據結構,不單單自己重要,並且也是樹、圖等數據結構的重要組成部分。java

  • 鏈表種類繁多:單向鏈表,循環鏈表,雙向鏈表等等。數組

  • 鏈表是一種在內存中隨意分佈的,由內存地址經過指針相互連接而成的順序結構。因爲其特殊的內存分佈特色,對於刪除和添加的操做相對數組效率較高。在查找和修改效率較低數據結構

咱們來學習一些單項鍊表的增刪改查。ide

功能實現

對於鏈表可謂是又愛又恨,愛是由於它是真的好用,恨是由於晦澀難懂,來來回回學了幾遍才搞懂。函數

定義單項鍊表節點類

鏈表中由兩部分構成:數據 + 地址指針。頭節點通常都是不存放數據,只是當作單項鍊表的開始索引,方便進行相關操做(頭節點不必定都存在),地址指針存放着下一個節點的內存地址,最後一個節點的寫一個指針地址爲空。數據部分按需定義便可,這裏數據部分有編號(no)和姓名(name)組成。學習

  • 代碼以下:
/**
 * 英雄節點類
 * @author laona
 */
class HeroNode {

    /**
     * 節點編號
     */
    int no;
    /**
     * 節點信息
     */
    String name;
    /**
     * 下一個節點
     */
    HeroNode next;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }
    
    // 重寫 toString 方法是爲了方便遍歷鏈表
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

實現單向鏈表

定義個空的頭節點,頭節點只須要存放下一個節點的內存地址便可,空的鏈表頭節點的 next == nullthis

  • 實現代碼以下:
/**
 * 單鏈表類
 * @author huaian
 */
class SingleLinked {
    /**
     * 空的頭節點,此時的 next == null
     */
    private HeroNode headNode = new HeroNode(0, "");
}

鏈表尾部添加節點

添加新的節點到鏈表,最重要的是找到鏈表的末尾節點(即鏈表的最後一個指向空的節點),這就須要遍歷鏈表。指針

遍歷鏈表須要一個臨時變量來獲取鏈表當前不爲空的節點 temp,直到最後一個爲空的節點。code

  • 循環體條件爲:
// 獲取下一個節點
temp = temp.next
  • 添加新節點語句爲:
temp.next = newNode;
  • 函數實現代碼以下:
/**
 * 添加新的節點到單鏈表
 * @param newNode {@link HeroNode} 新的節點
 */
public void addNode(HeroNode newNode) {
    HeroNode temp = headNode;
    while (true) {
        if (temp.next == null) {
            break;
        }
        temp = temp.next;
    }
    temp.next = newNode;
}

以上添加功能就完成了,那麼可否讓鏈表中的數據按照 no 的順序來排列呢?確定是能夠的。排序

按照升序添加節點

按照升序排序其實也不難,實現代碼和鏈表尾部添加節點的代碼差很少。須要弄明白的一個核心是:鏈表只能經過前一個節點的 next 去獲取下一個節點,那麼就須要傳入的 no 和前一個節點的 next.no 比較。

  • 比較條件:
temp.next.no >= newNode.no
  • 函數代碼以下:
/**
 * 按編號升序添加節點
 * @param newNode {@link HeroNode} 新的節點
 */
public void addNodeByOrder(HeroNode newNode){
    HeroNode temp = headNode;
    while (true) {
        if (temp.next == null) {
            break;
        }
        if (temp.next.no >= newNode.no) {
            // 不能有相同的節點
            if (temp.next.no == newNode.no) {
                System.out.printf("已存在索引相同的元素: no = %d,插入失敗\n", newNode.no);
                break;
            }
            newNode.next = temp.next;
            temp.next = newNode;
            break;
        } else {
            temp = temp.next;
        }
    }
    temp.next = newNode;
}

修改節點信息

修改節點信息就是比較當前節點的下一個節點信息和須要修改的節點信息是否相同,若是相容那麼就直接修改,不相同就繼續比較下一個節點。

  • 函數實現代碼以下:
/**
 * 根據節點編號修改信息
 * @param no 節點編號
 * @param newName 節點信息
 */
public void modifyByNo(int no, String newName) {
    HeroNode temp = headNode;
    while (true) {
        if (temp.next == null) {
            System.out.printf("不存在,編號爲:%d 的節點\n", no);
            break;
        }
        if (temp.next.no == no) {
            temp.next.name = newName;
            break;
        }
        temp = temp.next;
    }
}

刪除節點

有了前面的鋪墊,刪除節點就很簡單了。刪除節點須要明確是刪除哪個節點,這裏就經過 no 刪除節點,這裏也須要經過比較當前節點的下一個節點信息和待刪除的節點信息是否相同,相同則刪除。

  • 刪除代碼(經過把當前的節點的 next 指向當前節點的 next.next):
// 經過把當前的節點的 next 指向當前節點的 next.next
temp.next = temp.next.next;
  • 函數代碼以下:
/**
 * 經過 no 刪除節點
 * @param no 節點編號
 */
public void dropNodeByINo(int no) {
    HeroNode temp = headNode;
    while (true) {
        if (temp.next == null) {
            System.out.printf("不存在,編號爲:%d 的節點\n", no);
            break;
        }
        if (temp.next.no == no) {
            temp.next = temp.next.next;
            break;
        }
        temp = temp.next;
    }
}

遍歷鏈表

方便查看鏈表的內容,編寫一個函數遍歷鏈表,代碼以下:

/**
 * 遍歷單鏈表的全部節點
 */
public void list() {
    HeroNode temp = headNode;
    if (headNode.next == null) {
        System.out.println("該鏈表爲空~!");
        return;
    }
    while (true) {
        if (temp.next == null) {
            break;
        }
        System.out.println(temp.next);
        temp = temp.next;
    }
}

單鏈表類的總體

爲了方便查看這裏仍是把 SingleLinked 的代碼總體代碼貼一下(節省篇幅,函數只添加頭部,函數體省略)

  • SingleLinked 類的代碼以下:
/**
 * 單鏈表類
 * @author huaian
 */
class SingleLinked {
    private HeroNode headNode = new HeroNode(0, "");

    /**
     * 經過 no 刪除節點
     * @param no 節點編號
     */
    public void dropNodeByINo(int no) {
       ······
    }

    /**
     * 根據節點編號修改信息
     * @param no 節點編號
     * @param newName 節點信息
     */
    public void modifyByNo(int no, String newName) {
        ······
    }

    /**
     * 按編號升序添加節點
     * @param newNode {@link HeroNode} 新的節點
     */
    public void addNodeByOrder(HeroNode newNode){
        ······
    }

    /**
     * 添加新的節點到單鏈表
     * @param newNode {@link HeroNode} 新的節點
     */
    public void addNode(HeroNode newNode) {
        ······
    }

    /**
     * 遍歷單鏈表的全部節點
     */
    public void list() {
        ······
    }
}

/**
 * 英雄節點類
 * @author huaian
 */
class HeroNode {

    /**
     * 節點編號
     */
    int no;
    /**
     * 節點信息
     */
    String name;
    /**
     * 下一個節點
     */
    HeroNode next;

    // 構造傳參
    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

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

小結

單鏈表的核心是須要經過上一個節點的 next 才能獲取下一個節點的信息。弄懂這點,鏈表就不難了。

在查找、比較的時候,須要明白和上個節點比較,仍是當前節點比較?須要操做的是當前節點仍是下一個節點?等等。


人若無名,專心練劍! 喜歡的朋友能夠留下你的贊!

相關文章
相關標籤/搜索