簡單鏈表的合并、刪除并巍、交點目木、是否有環(huán)

一、將兩個有序鏈表合并為一個新的有序鏈表并返回

方法一:迭代
首先懊渡,我們設(shè)定一個哨兵節(jié)點 "prehead" 刽射,這可以在最后讓我們比較容易地返回合并后的鏈表。我們維護(hù)一個 prev 指針剃执,我們需要做的是調(diào)整它的 next 指針誓禁。然后,我們重復(fù)以下過程肾档,直到 l1 或者 l2 指向了 null :如果 l1 當(dāng)前位置的值小于等于 l2 摹恰,我們就把 l1 的值接在 prev 節(jié)點的后面同時將 l1 指針往后移一個。否則怒见,我們對 l2 做同樣的操作俗慈。不管我們將哪一個元素接在了后面,我們都把 prev 向后移一個元素遣耍。
在循環(huán)終止的時候闺阱, l1 和 l2 至多有一個是非空的。由于輸入的兩個鏈表都是有序的舵变,所以不管哪個鏈表是非空的酣溃,它包含的所有元素都比前面已經(jīng)合并鏈表中的所有元素都要大。這意味著我們只需要簡單地將非空鏈表接在合并鏈表的后面纪隙,并返回合并鏈表赊豌。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode preHead = new ListNode(-1);
        ListNode prev = preHead;
        while(l1 != null && l2 != null) {
            if(l1.val <= l2.val) {
                prev.next = l1;
                l1 = l1.next;
            } else {
                prev.next = l2;
                l2 = l2.next;
            }
            prev = prev.next;
        }
        prev.next = l1 == null ? l2 : l1;
        return preHead.next;
    }
}

鏈接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-lian-biao-by-leetcode/

方法二:遞歸

  • 終止條件:兩條鏈表分別名為 l1 和 l2,當(dāng) l1 為空或 l2 為空時結(jié)束
  • 返回值:每一層調(diào)用都返回排序好的鏈表頭
  • 本級遞歸內(nèi)容:如果 l1 的 val 值更小绵咱,則將 l1.next 與排序好的鏈表頭相接碘饼,l2 同理
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        if (l1 == null) {
            return l2;
        }
        if (l2 == null) {
            return l1;
        }
        if (l1.val <= l2.val) {
            l1.next = mergeTwoLists(l1.next, l2);
            return l1;
        } else {
            l2.next = mergeTwoLists(l1, l2.next);
            return l2;
        }
    }
}

鏈接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/hua-jie-suan-fa-21-he-bing-liang-ge-you-xu-lian-bi/

二、給定一個排序鏈表,刪除所有重復(fù)的元素派昧,使得每個元素只出現(xiàn)一次黔姜。
// 由于輸入的列表已排序,因此我們可以通過將結(jié)點的值與它之后的結(jié)點進(jìn)行比較來確定它是否為重復(fù)結(jié)點蒂萎。如果它是重復(fù)的秆吵,我們更改當(dāng)前結(jié)點的 next 指針,以便它跳過下一個結(jié)點并直接指向下一個結(jié)點之后的結(jié)點
class Solution {
    public ListNode deleteDuplicates(ListNode head) {
        ListNode current = head;
        while(current != null && current.next != null) {
            if(current.val == current.next.val) {
                current.next = current.next.next;
            } else {
                current = current.next;
            }
        }
        return head;
    }
}
三五慈、給定一個鏈表纳寂,判斷鏈表中是否有環(huán)。

方法一:哈希表
我們遍歷所有結(jié)點并在哈希表中存儲每個結(jié)點泻拦。如果當(dāng)前結(jié)點為空結(jié)點 null(即已檢測到鏈表尾部的下一個結(jié)點)毙芜,那么我們已經(jīng)遍歷完整個鏈表,并且該鏈表不是環(huán)形鏈表争拐。如果當(dāng)前結(jié)點的引用已經(jīng)存在于哈希表中腋粥,那么返回 true(即該鏈表為環(huán)形鏈表)
方法二:快慢指針
首先創(chuàng)建兩個不同速度的節(jié)點,如果兩個節(jié)點相同時架曹,則代表該鏈表是環(huán)形鏈表隘冲;
若不相同時,判斷快指針當(dāng)前節(jié)點/下一個節(jié)點是否到達(dá)終點(即null)绑雄,若為null則不是環(huán)形鏈表展辞,否則移動節(jié)點

class Solution {
    public boolean hasCycle(ListNode head) {
        /// 1、哈希表
        // Set<ListNode> map = new HashSet<>();
        // while(head != null) {
        //     if(map.contains(head)) {
        //         return true;
        //     } else {
        //         map.add(head);
        //     }
        //     head = head.next;
        // } 
        // return false;

        /// 2万牺、快慢指針
        ListNode slow = head, fast = head.next;
        while (slow != fast) {
            if(fast == null || fast.next == null) {
                return false;
            }
            slow = slow.next;
            fast = fast.next.next;
        }
        return true;
    }
}
四罗珍、找到兩個單鏈表相交的起始節(jié)點。

方法一脚粟、暴力遍歷
對鏈表A中的每一個結(jié)點 a 覆旱,遍歷整個鏈表 B 并檢查鏈表 B 中是否存在結(jié)點和 a 相同。

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /// 暴力遍歷
        while(headA != null) {
            ListNode temp = headB;
            while(temp != null) {
                if(headA == temp) {
                    return headA;
                } else {
                    temp = temp.next;
                }
            }
            headA = headA.next;
        }
        return headA;
    }
}

方法二珊楼、哈希表
遍歷鏈表 A 并將每個結(jié)點存儲在哈希表中通殃。然后檢查鏈表 B 中的每一個結(jié)點 b 是否在哈希表中。若在厕宗,則 b為相交結(jié)點

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /// 哈希表
        Set<ListNode> set = new HashSet<>();
        while(headA != null || headA.next != null) {
            if(headB == null || headB.next == null) return null;
            if(set.contains(headB)) {
                return headB;
            } else {
                set.add(headA);
            }
            headA = headA.next;
            headB = headB.next;
        }
        return headA;
    }
}

方法三、雙指針

  • 創(chuàng)建兩個指針 pA 和 pB堕担,分別初始化為鏈表 A 和 B 的頭結(jié)點已慢。然后讓它們向后逐結(jié)點遍歷。
  • 當(dāng) pA 到達(dá)鏈表的尾部時霹购,將它重定位到鏈表 B 的頭結(jié)點; 類似的佑惠,當(dāng) pB 到達(dá)鏈表的尾部時,將它重定位到鏈表 A 的頭結(jié)點。
  • 若在某一時刻 pA 和 pB 相遇膜楷,則 pA/pB 為相交結(jié)點旭咽。
  • 想弄清楚為什么這樣可行, 可以考慮以下兩個鏈表: A={1,3,5,7,9,11} 和 B={2,4,9,11},相交于結(jié)點 9赌厅。 由于 B.length (=4) < A.length (=6)穷绵,pB 比 pA 少經(jīng)過 2 個結(jié)點,會先到達(dá)尾部特愿。將 pB 重定向到 A 的頭結(jié)點仲墨,pApA 重定向到 B 的頭結(jié)點后,pB 要比 pA 多走 2 個結(jié)點揍障。因此目养,它們會同時到達(dá)交點。
  • 如果兩個鏈表存在相交毒嫡,它們末尾的結(jié)點必然相同癌蚁。因此當(dāng) pA/pB 到達(dá)鏈表結(jié)尾時,記錄下鏈表 A/B 對應(yīng)的元素兜畸。若最后元素不相同努释,則兩個鏈表不相交。
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        /// 雙指針
        ListNode pA = headA, pB = headB;
        while(pA != pB) {
            pA = pA == null ? headB : pA.next;
            pB = pB == null ? headA : pB.next;
        }
        return pA;
    }
}
五膳叨、刪除鏈表中等于給定值 val 的所有節(jié)點洽洁。

示例:
輸入: 1->2->6->3->4->5->6, val = 6
輸出: 1->2->3->4->5
這里需要注意的是:如果刪除的節(jié)點位于鏈表的頭部時 ,我們使用哨兵節(jié)點來解決該問題

  • 初始化哨兵節(jié)點為 ListNode(0) 且設(shè)置 sentinel.next = head菲嘴。
  • 初始化兩個指針 curr 和 prev 指向當(dāng)前節(jié)點和前繼節(jié)點饿自。
  • 當(dāng) curr != null:
  • 比較當(dāng)前節(jié)點和要刪除的節(jié)點:
  • 若當(dāng)前節(jié)點就是要刪除的節(jié)點:則 prev.next = curr.next。
  • 否則設(shè) prve = curr龄坪。
  • 遍歷下一個元素:curr = curr.next昭雌。
  • 返回 sentinel.next。
    :哨兵節(jié)點廣泛應(yīng)用于樹和鏈表中健田,如偽頭烛卧、偽尾、標(biāo)記等妓局,它們是純功能的总放,通常不保存任何數(shù)據(jù),其主要目的是使鏈表標(biāo)準(zhǔn)化好爬,如使鏈表永不為空局雄、永不無頭、簡化插入和刪除存炮。
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        ListNode sentinel = new ListNode(0); // 哨兵節(jié)點炬搭,虛擬增加一個頭結(jié)點
        sentinel.next = head;
        
        ListNode prev = sentinel, curr = head;
        while (curr != null) {
            if (curr.val == val) {
                prev.next = curr.next;
            } else {
                prev = curr;
            } 
            curr = curr.next;
        }
        return sentinel.next;
    }
}
六蜈漓、翻轉(zhuǎn)鏈表
class Solution {
    public ListNode reverseList(ListNode head) {
        // 1、 迭代
        // ListNode preHead = null;
        // ListNode prev = head;
        // while(prev != null) {
        //     ListNode temp = prev.next;
        //     prev.next = preHead;
        //     preHead = prev;
        //     prev = temp;
        // }
        // return preHead;

        /// 2宫盔、 遞歸
        if (head == null || head.next == null) return head;
        ListNode temp = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return temp;
    }
}
七融虽、請判斷一個鏈表是否為回文鏈表。

先翻轉(zhuǎn)鏈表 再比較

class Solution {
    public boolean isPalindrome(ListNode head) {
        if (head == null || head.next == null) {
            return true;
        }
        ListNode slow = head, fast = head, prev = null, prepre = null;
        while(fast != null && fast.next != null) {
            /// 移動指針
            prev = slow;
            slow = slow.next;
            fast = fast.next.next;
            /// 翻轉(zhuǎn)前半部鏈表
            prev.next = prepre;
            prepre = prev;

        }
        if(fast != null) { slow = slow.next;} // 鏈表為奇數(shù)灼芭,需要去除中間節(jié)點
        // 遍歷 對比
        while (prev != null && slow != null) {
            if (slow.val != prev.val) {
                return false;
            }
            slow = slow.next;
            prev = prev.next;
        }
        return true;
    }
}
八有额、請編寫一個函數(shù),使其可以刪除某個鏈表中給定的(非末尾)節(jié)點

從鏈表里刪除一個節(jié)點 node 的最常見方法是修改之前節(jié)點的 next 指針姿鸿,使其指向之后的節(jié)點谆吴。
在這里我們無法訪問我們想要刪除的節(jié)點 之前 的節(jié)點,我們始終不能修改該節(jié)點的 next 指針苛预。但是我們可以將想要刪除的節(jié)點的值替換為它后面節(jié)點中的值句狼,然后刪除它之后的節(jié)點。
因為我們知道要刪除的節(jié)點不是列表的末尾热某,所以我們可以保證這種方法是可行的腻菇。

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}
九、給定一個帶有頭結(jié)點 head 的非空單鏈表昔馋,返回鏈表的中間結(jié)點筹吐。如果有兩個中間結(jié)點,則返回第二個中間結(jié)點秘遏。

使用雙指針丘薛,通過快慢指針找到中間點

class Solution {
    public ListNode middleNode(ListNode head) {
        ListNode slow = head;
        ListNode fast = head;
        // fast != null && fast.next != null 用于找到第二個中間節(jié)點
        // fast.next != null && fast.next.next != null 用于找到第一個中間節(jié)點
        while( fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
        return slow;
    }
}
十、給你一個單鏈表的引用結(jié)點 head邦危。鏈表中每個結(jié)點的值不是 0 就是 1洋侨。已知此鏈表是一個整數(shù)數(shù)字的二進(jìn)制表示形式。

請你返回該鏈表所表示數(shù)字的 十進(jìn)制值 倦蚪。

class Solution {
    public int getDecimalValue(ListNode head) {
        int sum = 0;
        while (head != null) {
            sum = sum * 2 + head.val;
            head = head.next;
        }
        return sum;
    }
}

注: 以上鏈表問題皆來源于https://leetcode-cn.com/problems/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末希坚,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子陵且,更是在濱河造成了極大的恐慌裁僧,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慕购,死亡現(xiàn)場離奇詭異聊疲,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)沪悲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進(jìn)店門售睹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人可训,你說我怎么就攤上這事昌妹。” “怎么了握截?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵飞崖,是天一觀的道長。 經(jīng)常有香客問我谨胞,道長固歪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任胯努,我火速辦了婚禮牢裳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘叶沛。我一直安慰自己蒲讯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布灰署。 她就那樣靜靜地躺著判帮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溉箕。 梳的紋絲不亂的頭發(fā)上晦墙,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機(jī)與錄音肴茄,去河邊找鬼晌畅。 笑死,一個胖子當(dāng)著我的面吹牛寡痰,可吹牛的內(nèi)容都是我干的抗楔。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼酷窥!你這毒婦竟也來了酝碳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤反粥,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后疲迂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體才顿,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年尤蒿,在試婚紗的時候發(fā)現(xiàn)自己被綠了郑气。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡腰池,死狀恐怖尾组,靈堂內(nèi)的尸體忽然破棺而出忙芒,到底是詐尸還是另有隱情,我是刑警寧澤讳侨,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布呵萨,位于F島的核電站,受9級特大地震影響跨跨,放射性物質(zhì)發(fā)生泄漏潮峦。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一勇婴、第九天 我趴在偏房一處隱蔽的房頂上張望忱嘹。 院中可真熱鬧,春花似錦耕渴、人聲如沸拘悦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽窄做。三九已至,卻和暖如春慰技,著一層夾襖步出監(jiān)牢的瞬間椭盏,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工吻商, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留掏颊,地道東北人。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓艾帐,卻偏偏與公主長得像乌叶,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子柒爸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容

  • 搞懂單鏈表常見面試題 Hello 繼上次的 搞懂基本排序算法准浴,這個一星期,我總結(jié)了捎稚,我所學(xué)習(xí)和思考的單鏈表基礎(chǔ)知識...
    醒著的碼者閱讀 4,579評論 1 45
  • LeetCode-鏈表 鏈表(Linked List)是一種常見的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)乐横,是一種線性表,但是并不會按線性的順...
    raincoffee閱讀 1,177評論 0 6
  • 轉(zhuǎn)載請注明出處:http://www.reibang.com/p/c65d9d753c31 在上一篇博客《數(shù)據(jù)結(jié)構(gòu)...
    Alent閱讀 3,500評論 4 74
  • (一)LeetCode206.反轉(zhuǎn)鏈表 題目描述: 反轉(zhuǎn)一個單鏈表。 代碼實現(xiàn) (二)LeetCode160. 相...
    Jarily閱讀 1,404評論 0 5
  • 上帝之眼条霜,代表著上帝監(jiān)視人類的法眼催什, 上帝之手,代表著上帝監(jiān)管人類的規(guī)矩宰睡, 擁有了上帝之眼和上帝之手蒲凶, 是否就可以...
    陟卓閱讀 464評論 2 15