亚洲视频二区_亚洲欧洲日本天天堂在线观看_日韩一区二区在线观看_中文字幕不卡一区

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.430618.com 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線(xiàn)咨詢(xún)客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

文章簡(jiǎn)述

在本次的文章中,按照個(gè)人的刷題計(jì)劃,會(huì)分享關(guān)于鏈表的 3 道簡(jiǎn)單級(jí)別的算法題(可是依然感覺(jué)不簡(jiǎn)單)

但是不要緊,從本篇文章開(kāi)始分享的算法題個(gè)人都會(huì)把關(guān)于這道題的全部代碼寫(xiě)出來(lái),并用debug的形式,分解每一步來(lái)整理出來(lái)。

通過(guò)還原題目場(chǎng)景,用 debug 調(diào)試的方式去分析,印象更加深刻些。

本篇文章中共有 3 道題目。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

一,合并兩個(gè)有序鏈表

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

1.1 題目分析

看到這道題的時(shí)候,第一反應(yīng)就是先將兩個(gè)鏈表合并,然后再排序。嗯。。。不用想,絕對(duì)的暴力寫(xiě)法。

或者是循環(huán)兩個(gè)鏈表,然后兩兩相比較,就像:

for(){
  for(){
    if(){}
  }
}

好吧,其實(shí)這道題精華在于可以使用遞歸,這個(gè)。。。來(lái)個(gè)草圖簡(jiǎn)單描述下。

第一步:

兩個(gè)鏈表的首節(jié)點(diǎn)進(jìn)行比較

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

兩個(gè)節(jié)點(diǎn)相等,則使 L2 鏈表【1】,和 L1 鏈表的【2】進(jìn)行比較

注意:

L1節(jié)點(diǎn)【1】和L2節(jié)點(diǎn)【1】比較完成后,需要修改1.next指針,以指向它的下個(gè)節(jié)點(diǎn)。

第二步:

現(xiàn)在我們獲取到了 L2 鏈表【1】,那它的 next 指向誰(shuí)?也就是 L2 鏈表【1】去和 L1 鏈表的【2】進(jìn)行比較。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

比較完成后,L2 鏈表【1】的 next 就指向了 L1 鏈表【2】,接著以此類(lèi)推。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

L2 鏈表【3】去和 L1 鏈表【4】比較。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

最后 L1 鏈表【4】和 L2 鏈表【4】比較。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

全部比較完成后,整個(gè)鏈表就已經(jīng)排序完成了。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

遞歸的方式就在于,兩兩節(jié)點(diǎn)進(jìn)行比較,當(dāng)有一個(gè)鏈表為null時(shí),表示其中一個(gè)鏈表已經(jīng)遍歷完成,那就需要終止遞歸,并將比較結(jié)果進(jìn)行返回。

可能只是單純的畫(huà)圖并不好理解,下面用代碼 debug 的方式去分析,還請(qǐng)耐心往下看。

 

1.2 代碼分析

按照題意需要先創(chuàng)建 2 個(gè)單鏈表,具體的創(chuàng)建方式可以參考本人的第一篇文章《手寫(xiě)單鏈表基礎(chǔ)之增,刪,查!附贈(zèng)一道鏈表題》。不多說(shuō),先初始化節(jié)點(diǎn)對(duì)象。

class ListNode {
    /**
     * 數(shù)據(jù)域
     */
    int val;
    /**
     * 下一個(gè)節(jié)點(diǎn)指針
     */
    ListNode next;

    ListNode() {
    }
    ListNode(int val) {
        this.val = val;
    }
    ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
    @Override
    public String toString() {
        return "ListNode{" +
                "val=" + val +
                '}';
    }
}

定義新增鏈表方法。

 public ListNode add(ListNode node) {
        // 1,定義輔助指針
        ListNode temp = nodeHead;
        // 2,首先判斷當(dāng)前節(jié)點(diǎn)是否為最后節(jié)點(diǎn)
        if (null == temp.next) {
            // 當(dāng)前節(jié)點(diǎn)為最后節(jié)點(diǎn)
            temp.next = node;
            return nodeHead;
        }
        // 3,循環(huán)遍歷節(jié)點(diǎn),如果當(dāng)前節(jié)點(diǎn)的下個(gè)節(jié)點(diǎn)不為空,表示還有后續(xù)節(jié)點(diǎn)
        while (null != temp.next) {
            // 否則將指針后移
            temp = temp.next;
        }
        // 4,遍歷結(jié)束,將最后節(jié)點(diǎn)的指針指向新添加的節(jié)點(diǎn)
        temp.next = node;
        return nodeHead.next;
    }

接著創(chuàng)建 2 個(gè)鏈表。

 /**
         * 定義L1鏈表
         */
        ListNode l11 = new ListNode(1);
        ListNode l12 = new ListNode(2);
        ListNode l13 = new ListNode(4);

        MergeLinkedList l1 = new MergeLinkedList();
        l1.add(l11);
        l1.add(l12);
        /**
         * 返回新增完的L1鏈表
         */
        ListNode add1 = l1.add(l13);

        /**
         * 定義L2鏈表
         */
        ListNode l21 = new ListNode(1);
        ListNode l22 = new ListNode(3);
        ListNode l23 = new ListNode(4);

        MergeLinkedList l2 = new MergeLinkedList();
        l2.add(l21);
        l2.add(l22);
        /**
         * 返回L2鏈表
         */
        ListNode add2 = l2.add(l23);

我們先把上述圖中使用遞歸的代碼貼出來(lái)。

 public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        /**
         * 如果L1鏈表為null,終止遞歸
         */
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        /**
         * 按照?qǐng)D中的描述,兩兩比較鏈表的節(jié)點(diǎn)
         */
        if (l1.val <= l2.val) {
            /**
             * L1的節(jié)點(diǎn)比L2的小,按照?qǐng)D中就是需要比較L1鏈表的下個(gè)節(jié)點(diǎn)
             * l1.next 就是指當(dāng)比較出節(jié)點(diǎn)大小后,需要修改指針的指引,將整個(gè)鏈表全部串聯(lián)起來(lái)
             */
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            /**
             * 同理,與上個(gè)if判斷一樣
             */
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

1.3 debug 調(diào)試

1.3.1 L1 鏈表已經(jīng)創(chuàng)建完成,同理 L2 也被創(chuàng)建完成。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

1.3.2 比較兩個(gè)鏈表的首節(jié)點(diǎn)

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

注意:

l1.next是指向遞歸方法的,也就是上圖中我們描述的l1鏈表【1】指向了l2鏈表【1】,但是L2鏈表【1】又指向誰(shuí)?開(kāi)始進(jìn)入遞歸

1.3.3 如上圖,開(kāi)始比較 L2 鏈表【1】與 L1 鏈表的【2】

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

1.3.4 比較 L1 鏈表【2】與 L2 鏈表【3】

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

1.3.5 后面操作是一樣的,下面就直接展示最后兩個(gè)節(jié)點(diǎn)比較

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

這里已經(jīng)到最后兩個(gè)節(jié)點(diǎn)的比較。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

這個(gè)時(shí)候 L1 鏈表先遍歷完成,需要終止遞歸,返回 L2 鏈表。為什么返回 L2 鏈表?直接看圖。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

因?yàn)樽詈笠徊奖容^的是 L1 鏈表【4】和 L2 鏈表【4】,也就是說(shuō) L2 鏈表【4】是最后的節(jié)點(diǎn),如果返回 L1 鏈表,那 L2 鏈表【4】就會(huì)被丟棄,可參考上面圖解的最后一張。

重點(diǎn)來(lái)了!!!

重點(diǎn)來(lái)了!!!

重點(diǎn)來(lái)了!!!

L1 鏈表已經(jīng)遍歷完成,開(kāi)始觸發(fā)遞歸將比較的結(jié)果逐次返回。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

這是不是我們最后 L1 鏈表【4】和 L2 鏈表【4】比較的那一步,是不是很明顯,l1.next 指向了 l1 的節(jié)點(diǎn)【4】,而 L1 節(jié)點(diǎn)也就是它最后的節(jié)點(diǎn)【4】,和我們那上面圖解中最后的結(jié)論一樣。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

再接著執(zhí)行下一步返回。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

L2 鏈表的【3】指向了 L1 鏈表的【4】

同理,按照之前遞歸的結(jié)果以此返回就可以了,那我們來(lái)看下最終的排序結(jié)果。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

二,刪除排序鏈表中的重復(fù)元素

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

2.1 題目分析

初次看這道題好像挺簡(jiǎn)單的,這不就是個(gè)人之前寫(xiě)的第一篇文章里面,刪除鏈表節(jié)點(diǎn)嗎!

仔細(xì)審題其實(shí)這道題要更簡(jiǎn)單些,因?yàn)轭}中已說(shuō)明是一個(gè)排序鏈表,因此我們只需要將當(dāng)前節(jié)點(diǎn)與下一個(gè)節(jié)點(diǎn)進(jìn)行比較,如果相等則直接修改 next 指針即可。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

2.1 代碼分析

同樣是鏈表的定義,與上面第一題中的創(chuàng)建是一樣的,只不過(guò)我們是需要再重新創(chuàng)建一個(gè)單鏈表。

				ListNode l1 = new ListNode(1);
        ListNode l2 = new ListNode(1);
        ListNode l3 = new ListNode(3);
        ListNode l4 = new ListNode(4);
        ListNode l5 = new ListNode(4);
        ListNode l6 = new ListNode(5);

        NodeFun nodeFun = new NodeFun();
        nodeFun.add(l1);
        nodeFun.add(l2);
        nodeFun.add(l3);
        nodeFun.add(l4);
        nodeFun.add(l5);
        ListNode listNode = nodeFun.add(l6);

創(chuàng)建完成后,接著看去重復(fù)的代碼。

public ListNode deleteDuplicates(ListNode head) {
        /**
         * 定義輔助指針
         */
        ListNode temp = head;
        /**
         * 判斷當(dāng)前節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)不能為空,因?yàn)槭切枰獙?dāng)前節(jié)點(diǎn)和下一個(gè)節(jié)點(diǎn)進(jìn)行比較的
         */
        while (temp != null && temp.next != null) {
            /**
             * 如果節(jié)點(diǎn)值相同
             */
            if (temp.val == temp.next.val) {
                /**
                 * 表示當(dāng)前節(jié)點(diǎn)與下一個(gè)節(jié)點(diǎn)的值相同,則移動(dòng)指針
                 */
                temp.next = temp.next.next;
            } else {
                /**
                 * 必須移動(dòng)指針,否則會(huì)產(chǎn)生死循環(huán)
                 */
                temp = temp.next;
            }
        }
        return head;
    }
}

2.2 debug 調(diào)試

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

2.2.1 按照初始化的鏈表,應(yīng)該是首節(jié)點(diǎn)【1】和第二個(gè)節(jié)點(diǎn)【1】進(jìn)行比較。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

不用說(shuō)兩個(gè)節(jié)點(diǎn)是相等的,那下一步進(jìn)入 if 判斷,就是修改指針的指向。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

此時(shí)第二個(gè)節(jié)點(diǎn)【1】已經(jīng)沒(méi)有被 next 指引了,就會(huì)被 GC 回收掉。

2.2.2 下一步就是節(jié)點(diǎn)【1】和節(jié)點(diǎn)【3】進(jìn)行比較

兩個(gè)節(jié)點(diǎn)不相等,進(jìn)入 else 將輔助指針移動(dòng)到下個(gè)節(jié)點(diǎn)。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

那么剩下的節(jié)點(diǎn)判斷也都是一樣的,我們最后看下打印的結(jié)果。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

三,環(huán)形鏈表

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

3.1 題目分析

如果這個(gè)鏈表里面有環(huán),其中一個(gè)節(jié)點(diǎn)必然是被指針指引了一次或者多次(如果有多個(gè)環(huán)的話(huà))。因此個(gè)人當(dāng)時(shí)簡(jiǎn)單的做法就是遍歷鏈表,把遍歷過(guò)的節(jié)點(diǎn)對(duì)象保存到 HashSet 中,每遍歷下一個(gè)節(jié)點(diǎn)時(shí)去 HashSet 中比對(duì),存在就表示有環(huán)。

而這道題沒(méi)有設(shè)置過(guò)多的要求,只要有環(huán)返回 boolean 就好。

還有一種巧妙的寫(xiě)法,使用快慢指針的思想。

這種方式大致意思就是說(shuō),快慢指針比作龜兔賽跑,兔子跑的快,如果存在環(huán)那么兔子就會(huì)比烏龜先跑進(jìn)環(huán)中。那么它們就會(huì)在某個(gè)節(jié)點(diǎn)上相遇,相遇了也就說(shuō)明鏈表是有環(huán)的。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

那么,你們問(wèn)題是不是來(lái)了?這不公平啊,【兔子】本來(lái)就比【烏龜】跑的快,那咋兔子還先跑了。

試想,如果它倆都在一個(gè)節(jié)點(diǎn)上跑,那它們從開(kāi)始不就是相遇了,因?yàn)槲覀兾覀兪窃O(shè)定如果在一個(gè)節(jié)點(diǎn)上相遇,表示鏈表是有環(huán)的。所以,這不是“不打自招“了!

比賽開(kāi)始,這【兔子大哥】有點(diǎn)猛啊,一下跑兩個(gè)節(jié)點(diǎn)。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 


開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

果然,有情人終成眷屬,它們相遇了。

3.2 代碼分析

這次創(chuàng)建鏈表的時(shí)候,就不能單純是個(gè)單鏈表了,還得加個(gè)環(huán)。

 

 		ListNode l1 = new ListNode(3);
        ListNode l2 = new ListNode(2);
        ListNode l3 = new ListNode(0);
        ListNode l4 = new ListNode(-4);

        /**
         * 給主角加個(gè)環(huán)
         */
        l4.next = l2;

        NodeFun nodeFun = new NodeFun();
        nodeFun.add(l1);
        nodeFun.add(l2);
        nodeFun.add(l3);
        ListNode listNode = nodeFun.add(l4);

那就一起來(lái)找環(huán)吧。

public boolean hasCycle(ListNode head) {
        ListNode temp = head;
        if(null == head){
            // 為空表示沒(méi)有環(huán)
            return false;
        }
        // 1,set集合保存遍歷過(guò)的節(jié)點(diǎn),如果新的節(jié)點(diǎn)已經(jīng)在set中,表示存在環(huán)
        // 2,使用快慢指針的思想
        // 定義慢指針
        ListNode slow = head;
        // 定義快指針
        ListNode fast = head.next;
        // 循環(huán),只要2個(gè)指針不重合,就一直循環(huán)
        while(slow != fast){
            // 如果2個(gè)指針都到達(dá)尾節(jié)點(diǎn),表示沒(méi)有環(huán)
            if(fast == null || fast.next == null){
                return false;
            }
            // 否則就移動(dòng)指針
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }

3.3 debug 調(diào)試

所以,尷尬的事情來(lái)了,這玩意 debug 不了啊。如果存在環(huán),那么 while 循環(huán)是不會(huì)進(jìn)來(lái)的。

那就直接看下結(jié)果吧。

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

如果把環(huán)去掉就是?

開(kāi)啟算法之路,還原題目,用debug調(diào)試搞懂每一道題

 

那還用猜?沒(méi)有光環(huán)了肯定。。。

作者:奮進(jìn)的小樣

原文鏈接:https://www.cnblogs.com/fenjyang/p/14426665.html

分享到:
標(biāo)簽:算法
用戶(hù)無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定