數(shù)據(jù)結(jié)構(gòu)與算法-鏈表

簡(jiǎn)介

鏈表是一種物理存儲(chǔ)單元上非連續(xù)依疼、非順序的存儲(chǔ)結(jié)構(gòu)數(shù)據(jù)元素的邏輯順序是通過鏈表中的指針鏈接次序?qū)崿F(xiàn)的而芥。

數(shù)組和鏈表

上一篇文章寫了數(shù)組的一些特性律罢,我們這里就拿數(shù)組跟鏈表做為比較來學(xué)習(xí)一些鏈表這個(gè)數(shù)據(jù)結(jié)構(gòu)吧。


內(nèi)存結(jié)構(gòu).png

數(shù)組和鏈表在內(nèi)存上的區(qū)別就跟示意圖一樣棍丐,上一篇文章寫了件蚕,數(shù)組的一個(gè)特點(diǎn)就是內(nèi)存的連續(xù)性旅薄,而鏈表不是,鏈表不要求所有數(shù)據(jù)都連續(xù)存儲(chǔ),而是用一個(gè)“指針”將他們串在一起辨宠。

單鏈表
單鏈表

單鏈表是最簡(jiǎn)單的一種鏈表,從圖中可以看出答姥,鏈表第一個(gè)節(jié)點(diǎn)(頭結(jié)點(diǎn))記錄著鏈表的起始地址莫鸭,用它能遍歷整個(gè)鏈表潦匈,尾節(jié)點(diǎn)指針指向一個(gè)空地址null,說明鏈表結(jié)束了赚导。
和數(shù)組一樣茬缩,鏈表也有查找刪除插入等操作。上一篇中寫了數(shù)組的刪除插入操作是很高復(fù)雜度的操作吼旧,每次刪除插入都有搬移數(shù)據(jù)凰锡,復(fù)雜度為O(n),而鏈表卻不用圈暗,因?yàn)殒湵硎怯谩爸羔槨眮泶鎯?chǔ)下一節(jié)點(diǎn)的地址的掂为,所以無需數(shù)據(jù)搬移,插入操作只要將前一節(jié)點(diǎn)指針指向待插入數(shù)據(jù)员串,將待插入數(shù)據(jù)的指針指向原有節(jié)點(diǎn)的下一節(jié)點(diǎn)勇哗,就完成了插入操作,刪除也一樣昵济,復(fù)雜度為O(1)智绸。


鏈表插入刪除操作.png

然而鏈表也并非是完美的,相對(duì)于數(shù)組访忿,因?yàn)閿?shù)組內(nèi)存的連續(xù)性瞧栗,隨意數(shù)組的隨機(jī)訪問就可以做到O(1)的復(fù)雜度,而鏈表無法根據(jù)內(nèi)存地址的偏移來計(jì)算出某個(gè)節(jié)點(diǎn)的地址海铆,所需想要隨機(jī)訪問一個(gè)鏈表中的數(shù)據(jù)迹恐,需要遍歷整個(gè)鏈表來找到要訪問的節(jié)點(diǎn),其算法復(fù)雜度是為O(n)卧斟。

#單鏈表的java實(shí)現(xiàn) SingleList.java
public class SingleList {
    private Node head=null;

    public SingleList(){
        head=new Node(null);
        head.next=null;
    }

    public int length(){
        int length=0;
        Node temp=head;
        while (temp.next!=null){
            length++;
            temp=temp.next;
        }
        return length;
    }

    public void addNode(Node node){
        Node temp=head;
        while (temp.next!=null){
            temp=temp.next;
        }
        temp.next=node;
    }
    public void addNode(Node node,int index) throws Exception {
        if (index<0||index>length()+1){
            throw  new Exception("不合法index殴边!");
        }
        int currentIndex=0;
        Node temp=head;
        while (temp.next != null) {
            if (index== currentIndex++){
                node.next=temp.next;
                temp.next=node;
                return;
            }
            temp=temp.next;
        }
    }

    public void delNode(Node node){
        Node temp=head;
        while (temp.next != null) {
            if (temp.data==node.data){
                node.next=temp.next;
                temp.next=node;
                return;
            }
            temp=temp.next;
        }
    }

    public void delNode(int index) throws Exception {
        if (index<1||index>length()+1){
            throw new Exception("不合法index!");
        }
        Node temp=head;
        int currentIndex=0;
        while (temp.next != null) {
            if (index==currentIndex++){
                temp.next=temp.next.next;
                return;
            }
            temp=temp.next;
        }
    }
    public Node getNode(int index) throws Exception {
        if (index < 1 || index > length() + 1) {
            throw new Exception("不合法index!");
        }
        Node temp=head;
        int currentIndex=0;
        while (temp.next!=null){
            if (index == currentIndex++) {
                return temp;
            }
            temp=temp.next;
        }
        return null;
    }

    public void show(){
        Node temp=head;
        while (temp.next != null) {
            temp=temp.next;
            System.out.println(temp.data+";");
        }
    }


}

class Node{
    public Object data;
    public Node next;
    public Node(Object data){
        this.data=data;
    }
}

#測(cè)試 main.java
 public static void main(String[] args) {
        SingleList singleList=new SingleList();
        singleList.addNode(new Node(1));
        singleList.addNode(new Node(2));
        System.out.println("singleList Length="+singleList.length());
        singleList.show();
        try {
            singleList.addNode(new Node(0),0);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("after addNode by index singleList Length="+singleList.length());
        singleList.show();
        try {
            singleList.delNode(2);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("after delNode by index singleList Length="+singleList.length());
        singleList.show();
        singleList.delNode(new Node(1));
        try {
            System.out.println("node 2="+singleList.getNode(2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
循環(huán)鏈表

循環(huán)鏈表也比較簡(jiǎn)單,就是在單鏈表的尾節(jié)點(diǎn)將原本指向null 的指針變?yōu)橹赶蝾^結(jié)點(diǎn)珍语。然后實(shí)現(xiàn)代碼也是跟單鏈表差不多锤岸,只是有幾個(gè)地方需要注意一下。

#CircularList.java 
public class CircularList {
    private Node head=null;

    public CircularList(){
        head=new Node(-1);
        head.next=head;
    }

    public int length(){
        int length=0;
        Node temp=head;
        while (temp.next!=head){
            length++;
            temp=temp.next;
        }
        return length;
    }

    public void addNode(Node node){
        if (head.next==head){
            head.next=node;
            node.next=head;
        }else {
            Node temp=head;
            while (temp.next!=head){
                temp=temp.next;
            }
            temp.next=node;
            node.next=head;
        }
    }
    public void addNode(Node node,int index) throws Exception {
        if (index<0||index>length()+1){
            throw  new Exception("不合法index板乙!");
        }
        int currentIndex=0;
        Node temp=head;
        while (temp.next != head) {
            if (index== currentIndex++){
                node.next=temp.next;
                temp.next=node;
                return;
            }
            temp=temp.next;
        }
    }

    public void delNode(Node node){
        Node temp=head;
        while (temp.next.data != head.data) {
            if (temp.next.data==node.data){
                temp.next=temp.next.next;
                System.out.println("del:"+node.data);
                return;
            }
            temp=temp.next;
        }
    }

    public void delNode(int index) throws Exception {
        if (index<1||index>length()+1){
            throw new Exception("不合法index!");
        }
        Node temp=head;
        int currentIndex=0;
        while (temp.next != head) {
            if (index==currentIndex++){
                temp.next=temp.next.next;
                return;
            }
            temp=temp.next;
        }
    }
    public Node getNode(int index) throws Exception {
        if (index < 1 || index > length() + 1) {
            throw new Exception("不合法index!");
        }
        Node temp=head;
        int currentIndex=0;
        while (temp.next!=head){
            if (index == currentIndex++) {
                return temp;
            }
            temp=temp.next;
        }
        return null;
    }

    public void show(){
        Node temp=head;
        while (temp.next != head) {
            temp=temp.next;
            System.out.println(temp.data+"->"+temp.next.data);
        }
    }
}

循環(huán)鏈表實(shí)現(xiàn)完了是偷,我們接下來用它來實(shí)現(xiàn)一個(gè)循環(huán)鏈表的經(jīng)典問題“約瑟夫環(huán)”,這個(gè)問題的數(shù)學(xué)描述是這樣的:已知n個(gè)人(以編號(hào)1募逞,2蛋铆,3...n分別表示)圍坐在一張圓桌周圍。從編號(hào)為k的人開始報(bào)數(shù)放接,數(shù)到m的那個(gè)人出列刺啦;他的下一個(gè)人又從1開始報(bào)數(shù),數(shù)到m的那個(gè)人又出列纠脾;依此規(guī)律重復(fù)下去玛瘸,直到圓桌周圍的人全部出列蜕青。

# jusephus.java                     
    public static void main(String[] args) {
        jusephus(30,5,3);
    }

    private static void jusephus(int n,int k,int m){
        CircularList circularList=new CircularList();
        for (int i = 0; i < n; i++) {
            circularList.addNode(new Node(i));
        }

        int count=m;
        Node startNode=null;
        try {
            startNode=circularList.getNode(k);
        } catch (Exception e) {
            e.printStackTrace();
        }
        while (circularList.length()>0){
            if (--count>0){
                startNode=startNode.next;
            }
            if (count==0){
                System.out.println("out:"+startNode.data+" m="+m+" c l="+circularList.length());
                circularList.delNode(startNode);
                startNode=startNode.next;
                count=m;
            }
        }
    }

雙向鏈表

雙向鏈表顧名思義不僅有一個(gè)指向下一個(gè)節(jié)點(diǎn)的指針,還有一個(gè)指向前一個(gè)節(jié)點(diǎn)的指針捧韵。


雙向鏈表.png

從圖中可以看出市咆,雙向鏈表會(huì)比單鏈表多一個(gè)字節(jié)的內(nèi)存空間來存儲(chǔ)前驅(qū)節(jié)點(diǎn),所謂有得必有失再来,那么雙向鏈表比單鏈表多了什么特性呢?
由于多了一個(gè)前向指針磷瘤,雙向鏈表支持O(1)時(shí)間復(fù)雜度查找前驅(qū)節(jié)點(diǎn)芒篷,在某些情況下雙向鏈表的插入刪除操作比單鏈表高效;對(duì)于單鏈表采缚,前面已經(jīng)寫了针炉,他的插入、刪除操作的時(shí)間復(fù)雜度都是O(1)扳抽,那么還有比這更高效的篡帕?這就要先看下實(shí)際情況下刪除操作所包含的情況了:
很多情況下我們刪除一個(gè)節(jié)點(diǎn)都是只知道某個(gè)值,然后要?jiǎng)h除這個(gè)值所在節(jié)點(diǎn)贸呢,那么這個(gè)時(shí)候想要找到這個(gè)節(jié)點(diǎn)镰烧,就需要遍歷整個(gè)鏈表,從頭遍歷一次找到節(jié)點(diǎn)然后再執(zhí)行刪除操作楞陷,這時(shí)候復(fù)雜度就退化到了O(n)怔鳖,這種情況下無論是單鏈表還是雙向鏈表都沒有差別;還有一種情況是我們已經(jīng)知道要?jiǎng)h除的是一個(gè)節(jié)點(diǎn)固蛾,刪除這個(gè)節(jié)點(diǎn)之前我們需要得到所要?jiǎng)h除的節(jié)點(diǎn)的前向節(jié)點(diǎn)和后續(xù)節(jié)點(diǎn)结执,對(duì)于單鏈表,我們想要知道前向節(jié)點(diǎn)就不得不重新變量所有的節(jié)點(diǎn)以找到它艾凯,這個(gè)時(shí)候献幔,雙向鏈表的優(yōu)勢(shì)就體現(xiàn)出來了,因?yàn)樗粌H存儲(chǔ)了后續(xù)節(jié)點(diǎn)的指針趾诗,還存儲(chǔ)了前向節(jié)點(diǎn)的指針蜡感,所以這種情況下,雙向鏈表的刪除操作無需遍歷找到前向節(jié)點(diǎn)沧竟,此時(shí)铸敏,刪除節(jié)點(diǎn)的時(shí)間復(fù)雜度就僅為O(1)。同理悟泵,插入操作時(shí)雙向鏈表也比單鏈表會(huì)高效杈笔。

總結(jié)

鏈表雖然相較于數(shù)組會(huì)更方便,但是因?yàn)槊總€(gè)節(jié)點(diǎn)要多存儲(chǔ)一個(gè)或者兩個(gè)指針數(shù)據(jù)糕非,所以在內(nèi)存耗費(fèi)上會(huì)比數(shù)組消耗更大蒙具,所以具體使用數(shù)組還是鏈表還得根據(jù)實(shí)際情況來決定球榆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市禁筏,隨后出現(xiàn)的幾起案子持钉,更是在濱河造成了極大的恐慌,老刑警劉巖篱昔,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件每强,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡州刽,警方通過查閱死者的電腦和手機(jī)空执,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穗椅,“玉大人辨绊,你說我怎么就攤上這事∑ケ恚” “怎么了门坷?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)袍镀。 經(jīng)常有香客問我默蚌,道長(zhǎng),這世上最難降的妖魔是什么流椒? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任敏簿,我火速辦了婚禮,結(jié)果婚禮上宣虾,老公的妹妹穿的比我還像新娘惯裕。我一直安慰自己,他們只是感情好绣硝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布蜻势。 她就那樣靜靜地躺著,像睡著了一般鹉胖。 火紅的嫁衣襯著肌膚如雪握玛。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天甫菠,我揣著相機(jī)與錄音挠铲,去河邊找鬼。 笑死寂诱,一個(gè)胖子當(dāng)著我的面吹牛拂苹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痰洒,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼瓢棒,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼浴韭!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起脯宿,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤念颈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后连霉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榴芳,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年窘面,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了翠语。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡财边,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出点骑,到底是詐尸還是另有隱情酣难,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布黑滴,位于F島的核電站憨募,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏袁辈。R本人自食惡果不足惜菜谣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望晚缩。 院中可真熱鬧尾膊,春花似錦、人聲如沸荞彼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)鸣皂。三九已至抓谴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間寞缝,已是汗流浹背癌压。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荆陆,地道東北人滩届。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像慎宾,于是被迫代替她去往敵國(guó)和親丐吓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子浅悉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350