鏈表 Linked List

鏈表介紹

  • 鏈表時有序的列表,可是它在內存中是存儲以下
    image

小結前端

  1. 鏈表是以節點的方式來存儲,是鏈式存儲
  2. 每一個節點包含 data域,next域:指向下一個節點
  3. 如圖:發現鏈表的各個節點不必定是連續存儲
  4. 鏈表帶頭節點的鏈表和沒帶頭節點的鏈表,根據實際的需求來肯定

單鏈表

單鏈表(帶頭節點)邏輯示意圖
imagejava

單鏈表的應用實例

使用帶 head頭的單向鏈表實現水滸傳英雄排行榜,管理面試

  1. 完成對英雄任務的增刪改查操做,注:刪除和修改,查找
  2. 第一種方式在添加英雄時,直接添加到鏈表的尾部,
  3. 第二種方時在添加英雄時,根據排名將英雄插入到指定位置(若是有這個排名,則添加失敗,並給出提示)

單鏈表的建立示意圖(添加),顯示單向鏈表的分析
image數據結構

添加-直接添加到末尾

public class SingleLinkedListDemo {
    public static void main(String[] args) {
        //進項測試
        //先建立節點
        HeroNode hero1 = new HeroNode(1, "松江", "及時雨");
        HeroNode hero2 = new HeroNode(2, "盧俊義", "玉麒麟");
        HeroNode hero3 = new HeroNode(3, "吳用", "智多星");
        HeroNode hero4 = new HeroNode(4, "林沖", "豹子頭");
        //加入 想要建立鏈表
        SingleLinkedList singleLinkedList = new SingleLinkedList();
        singleLinkedList.add(hero1);
        singleLinkedList.add(hero4);
        singleLinkedList.add(hero2);
        singleLinkedList.add(hero3);
        //顯示一把
        singleLinkedList.list();
    }
}

//定義 SingleLinkedListDemo 管理咱們的英雄
class SingleLinkedList {
    //先初始化一個頭節點,頭節點不要動,不存放具體的數據
    private HeroNode head = new HeroNode(0, "", "");

    //添加節點到單向鏈表
    /*
     * 思路:當不考慮編號順序時
     * 1. 找到當前鏈表的最後節點,
     * 2. 將最後這個節點的next 指向 新的節點
     * */
    public void add(HeroNode heroNode) {
        //由於 head節點不能動,所以咱們須要一個輔助遍歷 temp
        HeroNode temp = head;
        //遍歷鏈表找到最後,
        while (true) {
            //找到鏈表的最後
            if (temp.next == null) {
                break;
            }
            //若是沒有找到最後
            temp = temp.next;
        }
        //當退出 while循環時,temp就指向鏈表的最後
        //將最後這個節點的 next,指向新的節點
        temp.next = heroNode;
    }

    //顯示鏈表 遍歷
    public void list() {
        //先判斷鏈表是否爲空
        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;
        }
    }
}

//定義HeroNode,每一個HeroNode 對象就是一個節點
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;
    }

    //爲了顯示方法,咱們重寫 toString  next就不要顯示了
    @Override
    public String toString() {
        return "HeroNode{" +
                "no=" + no +
                ", name='" + name + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

添加-順序添加

image

//第二種方時在添加英雄時,根據排名將英雄插入到指定位置(若是有這個排名,則添加失敗,並給出提示)
    public void addByOrder(HeroNode heroNode) {
        //由於頭節點不能動,所以咱們仍然經過一個輔助指針(變量)來幫助咱們找到添加的位置
        //由於單鏈表,由於咱們找到的temp,是位於 添加位置的前一個界定啊,不然插入不了
        HeroNode temp = head;
        boolean flag = false;//flag 標誌添加的編號是否存在,默認爲false
        while (true) {
            if (temp.next == null) {//說明 temp已經在鏈表的最後
                break;//不管如何都要退出
            }
            if (temp.next.no > heroNode.no) {//位置找到,就在 temp的後面插入
                break;

            } else if (temp.next.no == heroNode.no) {//說明但願添加的 heroNode的編號依然存在
                flag = true;//說明編號存在
                break;
            }
            //若是上面的都沒有成立 temp要後移,遍歷當前鏈表
            temp = temp.next;
        }
        //判斷 flag的值
        if (flag) {//不能添加,說明編號已經存在
            System.out.println("準備插入的英雄的編號" + heroNode.no + "已經存在不能添加");
        } else {
            //插入到鏈表中 temp的後面
            heroNode.next = temp.next;
            temp.next = heroNode;
        }
    }

更新

思路:ide

  1. 先找到該節點,經過遍歷
  2. temp.neme = newHeroNode.name;
    temp.nickname = newHeroNode.nickname;
//說明
    //修改節點的信息,根據no編號修改,即 no編號不能修改
    //1.根據 newHeroNode 的 no來修改便可
    public void update(HeroNode newHeroNode) {
        //判斷是否爲空
        if (head.next == null) {
            System.out.println("鏈表爲空");
            return;
        }
        //找到須要的修改的節點,根據no編號
        //先定義一個輔助變量
        HeroNode temp = head.next;
        boolean flag = false;//表示是否找到該節點
        while (true) {
            if (temp == null) {
                break;//到鏈表的最後==>最後節點的下一個
            }
            if (temp.no == newHeroNode.no) {
                //找到了
                flag = true;
                break;
            }
            temp = temp.next;
        }
        //根據 flag判斷是否找到修改的節點
        if (flag) {
            temp.name = newHeroNode.name;
            temp.nickname = newHeroNode.nickname;
        } else {//沒有找到
            System.out.println("沒有找到編號" + newHeroNode.no + "的節點,不能修改");
        }
    }

刪除

image

//刪除節點
    //思路
    //1. head 不能動,所以咱們須要一個 temp輔助節點找到待刪除的前一個節點
    //2. 說明咱們在比較時,是 temp.next.no 和 須要刪除的節點的no比較
    public void del(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 = temp.next;
        }
        //判斷 flag
        if (flag) {
            //能夠刪除
            temp.next = temp.next.next;
        } else {//未找到該節點
            System.out.println("找刪除的" + no + "節點未找到");
        }

    }

單鏈表的面試題

  1. 求單鏈表中有效節點的個數
//方法:獲取單鏈表的節點的個數(若是是帶頭節點的鏈表,須要不統計頭節點)
    /**
     * @param head 鏈表的頭節點
     * @return int 返回的就是有效節點的個數
     */
    public static int getLength(HeroNode head) {
        if (head.next == null) { //空鏈表
            return 0;
        }
        int length = 0;
        //定義一個輔助的變量
        HeroNode cur = head.next;
        while (cur != null) {
            length++; 
            cur = cur.next;//遍歷
        }
        return length;
    }
  1. 查找單鏈表中的倒數第k個結點 【新浪面試題】
//查找單鏈表中的倒數第k個結點 【新浪面試題】

    /**
     * 思路:
     * 1. 編寫一個方法,接收 head節點,同時接收一個 index
     * 2. index 表示倒數第 index個節點
     * 3. 先把鏈表從到位遍歷,獲得鏈表的總的長度 [調用 getLength方法 獲取單鏈表的個數]
     * 4. 獲得 size後,咱們從鏈表的第一個開始遍歷(size - index)個,就能夠獲得
     * 5. 若是找到,則返回該節點,不然返回 null
     *
     * @param head 單鏈表頭節點
     * @param index 倒數第n個節點的下標
     * @return 倒數第n個節點
     */
    public static HeroNode findLastIndexNode(HeroNode head, int index) {
        //判斷若是鏈表爲空,返回null
        if (head.next == null) {
            return null;//沒有找到
        }
        //第一個遍歷獲得鏈表的長度(節點個數)
        int size = getLength(head);
        //第二次遍歷 size-index 位置,就是咱們倒數的第k個節點
        //想作一個index的校驗
        if (index <= 0 || index > size) {
            return null;
        }
        //先定義輔助變量,for 循環定義倒數的 index
        HeroNode cur = head.next;  //假如3個有效數據 //3-1=2
        for (int i = 0; i < size - index; i++) {
            cur = cur.next;
        }
        return cur;
    }
  1. 單鏈表的反轉【騰訊面試題,有點難度】
//單鏈表的反轉【騰訊面試題,有點難度】

    /**
     * 思路:
     * 1. 先定義一個節點 reverseHead = new heroNode();
     * 2. reverseHead節點;不存放具體的數據;做用就是表示單鏈表頭 next
     * 3. 從頭到位遍歷原來的鏈表,每遍歷一個節點,就將其取出,並放在新的鏈表 reverseHead的最前端
     * 4. 原來的鏈表的 head.next=reverseHead.next
     *
     * @param head 須要反轉的頭節點
     */
    public static void reverseList(HeroNode head) {
        //若是當前鏈表爲空,或者只有一個節點,無需反轉,直接返回
        if (head.next == null || head.next.next == null) {
            return;
        }
        //定義一個輔助的指針(變量),幫助咱們遍歷原來的鏈表
        HeroNode cur = head.next;
        HeroNode next = null;//指向當前節點[cur]的下一個節點
        //定義:reverseHead節點;不存放具體的數據;做用就是表示單鏈表頭 next
        HeroNode reverseHead = new HeroNode(0, "", "");
        //遍歷給原來的鏈表
        // 並頭到位遍歷原來的鏈表,每遍歷一個節點,就將其取出,並放在新的鏈表 reverseHead的最前端
        //動腦筋
        while (cur != null) {
            next = cur.next;//先暫時保存當前節點的下一個節點,由於後面須要使用
            cur.next = reverseHead.next;//將cur的下一個節點指向新的鏈表節點的頭部[最前端]
            reverseHead.next=cur;//將 cur鏈接到新的鏈表上
            cur = next;//讓 cur後移
        }
        //將 head.next 指向 reverseHead.next ,實現單鏈表的反轉
        head.next = reverseHead.next;
    }
  1. 從尾到頭打印單鏈表 【百度,要求方式1:反向遍歷 。 方式2:Stack棧】
//從尾到頭打印單鏈表 【百度,要求方式1:反向遍歷 。 方式2:Stack棧】
    /**
     * 思路:
     * 1. 上面的題的要求就是逆序打印單鏈表
     * 2. 方式1: 先將單鏈表進行反轉操做,而後在遍歷便可,這樣作的問題是會破壞原來的單鏈表結構,不建議
     * 3. 方式2: 能夠利用棧這個數據結構,將各個節點壓入到棧[先進後出,好比子彈進子彈夾]中,而後
     *      利用棧的先進後出的特色,就實現了逆序打印的效果
     * 採用方式二
     *
     * @param head 單鏈表頭部
     */
    public static void reversePrint(HeroNode head) {
        if (head.next == null) {
            return;//空鏈表不能打印
        }
        //建立一個棧,將各個節點壓入棧中
        //Stack特色; 先進後出
        Stack<HeroNode> stack = new Stack<>();
        HeroNode cur = head.next;
        //將鏈表的全部節點壓入棧中
        while (cur != null) {
            stack.push(cur);
            //必定rur要後移,這樣就能壓入下一個節點
            cur = cur.next;
        }
        //將棧中的節點打印,pop 出棧
        while (stack.size() > 0) {
            System.out.println(stack.pop());//彈棧
        }
    }
  1. 合併兩個有序的單鏈表,合併以後的鏈表依然有序
和反轉很像,建立一個新的鏈表發現鏈表中的哪個更小就把他假如到新的鏈表中

雙鏈表

相關文章
相關標籤/搜索