Java實現(xiàn)單向鏈表基本功能

一、前言

最近在回顧數據結構與算法占哟,有部分的算法題用到了棧的思想捶箱,說起棧又不得不說鏈表了。數組和鏈表都是線性存儲結構的基礎膘流,棧和隊列都是線性存儲結構的應用~

本文主要講解單鏈表的基礎知識點絮缅,做一個簡單的入門~如果有錯的地方請指正

二鲁沥、回顧與知新

說起鏈表,我們先提一下數組吧耕魄,跟數組比較一下就很理解鏈表這種存儲結構了画恰。

2.1回顧數組

數組我們無論是C、Java都會學過:

  • 數組是一種連續(xù)存儲線性結構吸奴,元素類型相同允扇,大小相等
image

數組的優(yōu)點:

  • 存取速度快

數組的缺點:

  • 事先必須知道數組的長度
  • 插入刪除元素很慢
  • 空間通常是有限制的
  • 需要大塊連續(xù)的內存塊
  • 插入刪除元素的效率很低

2.2鏈表說明

看完了數組,回到我們的鏈表:

  • 鏈表是離散存儲線性結構

n個節(jié)點離散分配则奥,彼此通過指針相連考润,每個節(jié)點只有一個前驅節(jié)點,每個節(jié)點只有一個后續(xù)節(jié)點读处,首節(jié)點沒有前驅節(jié)點糊治,尾節(jié)點沒有后續(xù)節(jié)點。

image

鏈表優(yōu)點:

  • 空間沒有限制
  • 插入刪除元素很快

鏈表缺點:

  • 存取速度很慢

鏈表相關術語介紹罚舱,我還是通過上面那個圖來說明吧:

image

確定一個鏈表我們只需要頭指針井辜,通過頭指針就可以把整個鏈表都能推導出來了~

鏈表又分了好幾類:

  • 單向鏈表
    • 一個節(jié)點指向下一個節(jié)點
  • 雙向鏈表
    • 一個節(jié)點有兩個指針域
  • 循環(huán)鏈表
    • 能通過任何一個節(jié)點找到其他所有的節(jié)點,將兩種(雙向/單向)鏈表的最后一個結點指向第一個結點從而實現(xiàn)循環(huán)

操作鏈表要時刻記住的是:

  • 節(jié)點中指針域指向的就是一個節(jié)點了馆匿!

三抑胎、Java實現(xiàn)鏈表

算法:

  • 遍歷
  • 查找
  • 清空
  • 銷毀
  • 求長度
  • 排序
  • 刪除節(jié)點
  • 插入節(jié)點

首先,我們定義一個類作為節(jié)點

  • 數據域
  • 指針域

為了操作方便我就直接定義成public了渐北。


public class Node {

    //數據域
    public int data;

    //指針域阿逃,指向下一個節(jié)點
    public Node next;

    public Node() {
    }

    public Node(int data) {
        this.data = data;
    }

    public Node(int data, Node next) {
        this.data = data;
        this.next = next;
    }
}

3.1創(chuàng)建鏈表(增加節(jié)點)

向鏈表中插入數據:

  • 找到尾節(jié)點進行插入
  • 即使頭節(jié)點.next為null,不走while循環(huán)赃蛛,也是將頭節(jié)點與新節(jié)點連接的(我已經將head節(jié)點初始化過了恃锉,因此沒必要判斷頭節(jié)點是否為null)~

    /**
     * 向鏈表添加數據
     *
     * @param value 要添加的數據
     */
    public static void addData(int value) {

        //初始化要加入的節(jié)點
        Node newNode = new Node(value);

        //臨時節(jié)點
        Node temp = head;

        // 找到尾節(jié)點
        while (temp.next != null) {
            temp = temp.next;
        }

        // 已經包括了頭節(jié)點.next為null的情況了~
        temp.next = newNode;

    }

3.2遍歷鏈表

上面我們已經編寫了增加方法,現(xiàn)在遍歷一下看一下是否正確~~~

從首節(jié)點開始呕臂,不斷往后面找破托,直到后面的節(jié)點沒有數據:


    /**
     * 遍歷鏈表
     *
     * @param head 頭節(jié)點
     */
    public static void traverse(Node head) {

        //臨時節(jié)點,從首節(jié)點開始
        Node temp = head.next;

        while (temp != null) {

            System.out.println("關注公眾號Java3y:" + temp.data);

            //繼續(xù)下一個
            temp = temp.next;
        }
    }

結果:

image

3.3插入節(jié)點

  1. 插入一個節(jié)點到鏈表中歧蒋,首先得判斷這個位置是否是合法的土砂,才能進行插入~
  2. 找到想要插入的位置的上一個節(jié)點就可以了

    /**
     * 插入節(jié)點
     *
     * @param head  頭指針
     * @param index 要插入的位置
     * @param value 要插入的值
     */
    public static void insertNode(Node head, int index, int value) {

        //首先需要判斷指定位置是否合法,
        if (index < 1 || index > linkListLength(head) + 1) {
            System.out.println("插入位置不合法谜洽。");
            return;
        }

        //臨時節(jié)點萝映,從頭節(jié)點開始
        Node temp = head;

        //記錄遍歷的當前位置
        int currentPos = 0;

        //初始化要插入的節(jié)點
        Node insertNode = new Node(value);

        while (temp.next != null) {

            //找到上一個節(jié)點的位置了
            if ((index - 1) == currentPos) {

                //temp表示的是上一個節(jié)點

                //將原本由上一個節(jié)點的指向交由插入的節(jié)點來指向
                insertNode.next = temp.next;

                //將上一個節(jié)點的指針域指向要插入的節(jié)點
                temp.next = insertNode;

                return;

            }

            currentPos++;
            temp = temp.next;
        }

    }

3.4獲取鏈表的長度

獲取鏈表的長度就很簡單了,遍歷一下阐虚,每得到一個節(jié)點+1即可~


    /**
     * 獲取鏈表的長度
     * @param head 頭指針
     */
    public static int  linkListLength(Node head) {

        int length = 0;

        //臨時節(jié)點序臂,從首節(jié)點開始
        Node temp = head.next;

        // 找到尾節(jié)點
        while (temp != null) {
            length++;
            temp = temp.next;
        }

        return length;
    }

3.5刪除節(jié)點

刪除某個位置上的節(jié)點其實是和插入節(jié)點很像的, 同樣都要找到上一個節(jié)點实束。將上一個節(jié)點的指針域改變一下奥秆,就可以刪除了~


    /**
     * 根據位置刪除節(jié)點
     *
     * @param head  頭指針
     * @param index 要刪除的位置
     */
    public static void deleteNode(Node head, int index) {

        //首先需要判斷指定位置是否合法逊彭,
        if (index < 1 || index > linkListLength(head) + 1) {
            System.out.println("刪除位置不合法。");
            return;
        }

        //臨時節(jié)點构订,從頭節(jié)點開始
        Node temp = head;

        //記錄遍歷的當前位置
        int currentPos = 0;

        while (temp.next != null) {

            //找到上一個節(jié)點的位置了
            if ((index - 1) == currentPos) {

                //temp表示的是上一個節(jié)點

                //temp.next表示的是想要刪除的節(jié)點

                //將想要刪除的節(jié)點存儲一下
                Node deleteNode = temp.next;

                //想要刪除節(jié)點的下一個節(jié)點交由上一個節(jié)點來控制
                temp.next = deleteNode.next;

                //Java會回收它侮叮,設置不設置為null應該沒多大意義了(個人覺得,如果不對請指出哦~)
                deleteNode = null;

                return;

            }
            currentPos++;
            temp = temp.next;
        }
    }

3.6對鏈表進行排序

前面已經講過了8種的排序算法了【八種排序算法總結】,這次挑簡單的冒泡排序吧(其實我是想寫快速排序的悼瘾,嘗試了一下感覺有點難.....)


    /**
     * 對鏈表進行排序
     *
     * @param head
     *
     */
    public static void sortLinkList(Node head) {

        Node currentNode;

        Node nextNode;

        for (currentNode = head.next; currentNode.next != null; currentNode = currentNode.next) {

            for (nextNode = head.next; nextNode.next != null; nextNode = nextNode.next) {

                if (nextNode.data > nextNode.next.data) {

                    int temp = nextNode.data;
                    nextNode.data = nextNode.next.data;

                    nextNode.next.data = temp;

                }
            }

        }
    }

3.7找到鏈表中倒數第k個節(jié)點

這個算法挺有趣的:設置兩個指針p1签赃、p2,讓p2比p1快k個節(jié)點分尸,同時向后遍歷,當p2為空歹嘹,則p1為倒數第k個節(jié)點


    /**
     * 找到鏈表中倒數第k個節(jié)點(設置兩個指針p1箩绍、p2,讓p2比p1快k個節(jié)點尺上,同時向后遍歷材蛛,當p2為空,則p1為倒數第k個節(jié)點
     *
     * @param head
     * @param k    倒數第k個節(jié)點
     */
    public static Node findKNode(Node head, int k) {

        if (k < 1 || k > linkListLength(head))
            return null;
        Node p1 = head;
        Node p2 = head;

        // p2比怕p1快k個節(jié)點
        for (int i = 0; i < k - 1; i++)
            p2 = p2.next;

        // 只要p2為null怎抛,那么p1就是倒數第k個節(jié)點了
        while (p2.next != null) {

            p2 = p2.next;
            p1 = p1.next;
        }
        return p1;

    }

經過評論去提醒:也可以使用(-k+1+LinkList.length)%LinkList.length來獲取倒數的節(jié)點位置~

3.8刪除鏈表重復數據

跟冒泡排序差不多卑吭,只要它相等,就能刪除了~


  /**
     * 刪除鏈表重復數據(跟冒泡差不多马绝,等于刪除就是了)
     *
     * @param head 頭節(jié)點
     */
    public static void deleteDuplecate(Node head) {

        //臨時節(jié)點豆赏,(從首節(jié)點開始-->真正有數據的節(jié)點)
        Node temp = head.next;

        //當前節(jié)點(首節(jié)點)的下一個節(jié)點
        Node nextNode = temp.next;

        while (temp.next != null) {

            while (nextNode.next != null) {

                if (nextNode.next.data == nextNode.data) {

                    //將下一個節(jié)點刪除(當前節(jié)點指向下下個節(jié)點)
                    nextNode.next = nextNode.next.next;

                } else {

                    //繼續(xù)下一個
                    nextNode = nextNode.next;
                }
            }

            //下一輪比較
            temp = temp.next;
        }

    }

3.9查詢鏈表的中間節(jié)點

這個算法也挺有趣的:一個每次走1步,一個每次走兩步富稻,走兩步的遍歷完掷邦,然后走一步的指針,那就是中間節(jié)點


    /**
     * 查詢單鏈表的中間節(jié)點
     */

    public static Node searchMid(Node head) {

        Node p1 = head;
        Node p2 = head;

        // 一個走一步椭赋,一個走兩步抚岗,直到為null,走一步的到達的就是中間節(jié)點
        while (p2 != null && p2.next != null && p2.next.next != null) {

            p1 = p1.next;
            p2 = p2.next.next;

        }

        return p1;

    }

3.10通過遞歸從尾到頭輸出單鏈表


    /**
     * 通過遞歸從尾到頭輸出單鏈表
     *
     * @param head 頭節(jié)點
     */
    public  static  void printListReversely(Node head) {
        if (head != null) {
            printListReversely(head.next);
            System.out.println(head.data);
        }
    }

3.11反轉鏈表


    /**
     * 實現(xiàn)鏈表的反轉
     *
     * @param node 鏈表的頭節(jié)點
     */
    public static Node reverseLinkList(Node node) {

        Node prev ;
        if (node == null || node.next == null) {
            prev = node;
        } else {
            Node tmp = reverseLinkList(node.next);
            node.next.next = node;
            node.next = null;
            prev = tmp;
        }
        return prev;

    }
image

反轉鏈表參考自:

四哪怔、最后

理解鏈表本身并不難宣蔚,但做相關的操作就弄得頭疼,head.next next next next ....(算法這方面我還是薄弱啊..腦子不夠用了.....)寫了兩天就寫了這么點東西...

操作一個鏈表只需要知道它的頭指針就可以做任何操作了

  • 添加數據到鏈表中

    • 遍歷找到尾節(jié)點认境,插入即可(只要while(temp.next != null)胚委,退出循環(huán)就會找到尾節(jié)點)
  • 遍歷鏈表

    • 從首節(jié)點(有效節(jié)點)開始,只要不為null元暴,就輸出
  • 給定位置插入節(jié)點到鏈表中

    • 首先判斷該位置是否有效(在鏈表長度的范圍內)
    • 找到想要插入位置的上一個節(jié)點
      • 將原本由上一個節(jié)點的指向交由插入的節(jié)點來指向
      • 上一個節(jié)點指針域指向想要插入的節(jié)點
    • image
  • 獲取鏈表的長度

    • 每訪問一次節(jié)點篷扩,變量++操作即可
  • 刪除給定位置的節(jié)點

    • 首先判斷該位置是否有效(在鏈表長度的范圍內)
    • 找到想要插入位置的上一個節(jié)點
      • 將原本由上一個節(jié)點的指向后面一個節(jié)點
    • image
  • 對鏈表進行排序

    • 使用冒泡算法對其進行排序
  • 找到鏈表中倒數第k個節(jié)點

    • 設置兩個指針p1、p2茉盏,讓p2比p1快k個節(jié)點鉴未,同時向后遍歷枢冤,當p2為空,則p1為倒數第k個節(jié)點
  • 刪除鏈表重復數據

    • 操作跟冒泡排序差不多铜秆,只要它相等淹真,就能刪除了
  • 查詢鏈表的中間節(jié)點

    • 這個算法也挺有趣的:一個每次走1步,一個每次走兩步连茧,走兩步的遍歷完核蘸,然后走一步的指針,那就是中間節(jié)點
  • 遞歸從尾到頭輸出單鏈表

    • 只要下面還有數據啸驯,那就往下找客扎,遞歸是從最后往前翻
  • 反轉鏈表

    • 有遞歸和非遞歸兩種方式罚斗,我覺得是挺難的徙鱼。可以到我給出的鏈接上查閱~

PS:每個人的實現(xiàn)都會不一樣针姿,所以一些小細節(jié)難免會有些變動袱吆,也沒有固定的寫法,能夠實現(xiàn)對應的功能即可~

參考資料:

如果文章有錯的地方歡迎指正距淫,大家互相交流绞绒。習慣在微信看技術文章,想要獲取更多的Java資源的同學榕暇,可以關注微信公眾號:Java3y

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末蓬衡,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子彤枢,更是在濱河造成了極大的恐慌撤蟆,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,430評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堂污,死亡現(xiàn)場離奇詭異家肯,居然都是意外死亡,警方通過查閱死者的電腦和手機盟猖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,406評論 3 398
  • 文/潘曉璐 我一進店門讨衣,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人式镐,你說我怎么就攤上這事反镇。” “怎么了娘汞?”我有些...
    開封第一講書人閱讀 167,834評論 0 360
  • 文/不壞的土叔 我叫張陵歹茶,是天一觀的道長。 經常有香客問我,道長惊豺,這世上最難降的妖魔是什么燎孟? 我笑而不...
    開封第一講書人閱讀 59,543評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮尸昧,結果婚禮上揩页,老公的妹妹穿的比我還像新娘。我一直安慰自己烹俗,他們只是感情好爆侣,可當我...
    茶點故事閱讀 68,547評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著幢妄,像睡著了一般兔仰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蕉鸳,一...
    開封第一講書人閱讀 52,196評論 1 308
  • 那天斋陪,我揣著相機與錄音,去河邊找鬼置吓。 笑死,一個胖子當著我的面吹牛缔赠,可吹牛的內容都是我干的衍锚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,776評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼嗤堰,長吁一口氣:“原來是場噩夢啊……” “哼戴质!你這毒婦竟也來了?” 一聲冷哼從身側響起踢匣,我...
    開封第一講書人閱讀 39,671評論 0 276
  • 序言:老撾萬榮一對情侶失蹤告匠,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后离唬,有當地人在樹林里發(fā)現(xiàn)了一具尸體后专,經...
    沈念sama閱讀 46,221評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,303評論 3 340
  • 正文 我和宋清朗相戀三年输莺,在試婚紗的時候發(fā)現(xiàn)自己被綠了戚哎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,444評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡嫂用,死狀恐怖型凳,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情嘱函,我是刑警寧澤甘畅,帶...
    沈念sama閱讀 36,134評論 5 350
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響疏唾,放射性物質發(fā)生泄漏蓄氧。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,810評論 3 333
  • 文/蒙蒙 一荸实、第九天 我趴在偏房一處隱蔽的房頂上張望匀们。 院中可真熱鬧,春花似錦准给、人聲如沸泄朴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,285評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祖灰。三九已至,卻和暖如春畔规,著一層夾襖步出監(jiān)牢的瞬間局扶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,399評論 1 272
  • 我被黑心中介騙來泰國打工叁扫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留三妈,地道東北人。 一個月前我還...
    沈念sama閱讀 48,837評論 3 376
  • 正文 我出身青樓莫绣,卻偏偏與公主長得像畴蒲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子对室,可洞房花燭夜當晚...
    茶點故事閱讀 45,455評論 2 359

推薦閱讀更多精彩內容

  • 1 序 2016年6月25日夜模燥,帝都,天下著大雨掩宜,拖著行李箱和同學在校門口照了最后一張合照蔫骂,搬離寢室打車去了提前租...
    RichardJieChen閱讀 5,107評論 0 12
  • 搞懂單鏈表常見面試題 Hello 繼上次的 搞懂基本排序算法,這個一星期牺汤,我總結了辽旋,我所學習和思考的單鏈表基礎知識...
    醒著的碼者閱讀 4,591評論 1 45
  • 一、 C/C++程序基礎 面試例題1——分析代碼寫輸出(一般賦值語句的概念和方法)檐迟。 面試例題2—...
    LuckTime閱讀 1,988評論 2 42
  • 鏈表問題是面試過程中經常被問到的一部分戴已,很考查編程功底。最近刷了 LeetCode 上鏈表部分的面試題锅减,我總結了一...
    JohnnyShieh閱讀 4,962評論 0 9
  • 盤算今天時間糖儡,被兒子這件事干擾,最低浪費了一半怔匣。
    雪木912閱讀 88評論 0 0