LeetCode 專(zhuān)題:?jiǎn)捂湵?/h2>

知識(shí)點(diǎn)總結(jié)

1、鏈表問(wèn)題只要涉及到頭結(jié)點(diǎn)的操作的,一般都會(huì)用到設(shè)置虛擬頭結(jié)點(diǎn)這個(gè)技巧;

2、鏈表中的問(wèn)題,很多可以歸結(jié)為“穿針引線”,“穿針引線”要寫(xiě)正確的一個(gè)重要技巧就是“畫(huà)圖”,“畫(huà)圖”會(huì)使自己的思路更加清晰,這一點(diǎn)是非常重要的;

3、鏈表中可以遞歸處理的問(wèn)題有:(1)合并有序鏈表;(2)刪除鏈表中的結(jié)點(diǎn);(3)反轉(zhuǎn)鏈表;(4)兩兩交換鏈表中的結(jié)點(diǎn)。

4、有的時(shí)候,鏈表的操作,有循環(huán)或者判斷的,一定要記得看一看循環(huán)或者判斷的循環(huán)體或者判斷邏輯,就能檢查出自己的代碼是否寫(xiě)得正確,例如 LeetCode 第 148 題,使用“歸并排序”將單鏈表進(jìn)行排序和“單鏈表找中點(diǎn)”。

下面這句話不一定對(duì),暫且先放在這里:鏈表的動(dòng)態(tài)特性,很適合用來(lái)實(shí)現(xiàn)棧。數(shù)組適合在尾部操作,要擴(kuò)容、縮容。鏈表適合在頭部操作,每次都要 new 一個(gè)內(nèi)存空間。所以,其實(shí)不論是數(shù)組實(shí)現(xiàn)還是鏈表實(shí)現(xiàn),都沒(méi)有本質(zhì)區(qū)別。

反轉(zhuǎn)鏈表

LeetCode 第 206 題:反轉(zhuǎn)鏈表

LeetCode 第 92 題:反轉(zhuǎn)從位置 m 到 n 的鏈表,k 個(gè)組進(jìn)行一次反轉(zhuǎn)

穿針引線。

LeetCode 第 86 題:分割鏈表

不難,不過(guò)一直搞不清楚這個(gè)問(wèn)題的意思。

LeetCode 第 2 題、第 445 題:鏈表求和

LeetCode 第 2 題:兩數(shù)相加

要求:給出兩個(gè) 非空 的鏈表用來(lái)表示兩個(gè)非負(fù)的整數(shù)。其中,它們各自的位數(shù)是按照 逆序 的方式存儲(chǔ)的,并且它們的每個(gè)節(jié)點(diǎn)只能存儲(chǔ) 一位 數(shù)字。

如果,我們將這兩個(gè)數(shù)相加起來(lái),則會(huì)返回一個(gè)新的鏈表來(lái)表示它們的和。

您可以假設(shè)除了數(shù)字 0 之外,這兩個(gè)數(shù)都不會(huì)以 0 開(kāi)頭。

分析:這道題其實(shí)不難,主要考察了代碼的編寫(xiě)能力。

1、首先應(yīng)該考慮到邊界情況:

if l1 is None:
    return l2

if l2 is None:
    return l1

2、鏈表問(wèn)題一般都會(huì)設(shè)置虛擬頭結(jié)點(diǎn);

3、進(jìn)位問(wèn)題有模板寫(xiě)法:模 10 得到位數(shù),再除以 10 得到進(jìn)位;

4、最后考慮要不要進(jìn)位 1。

# Definition for singly-linked list.
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution:
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if l1 is None:
            return l2

        if l2 is None:
            return l1

        dummy_node = ListNode(-1)
        cur_node = dummy_node
        s = 0

        # 只要二者之一非空,就加下去
        while l1 or l2:
            if l1:
                s += l1.val
                l1 = l1.next
            if l2:
                s += l2.val
                l2 = l2.next
            cur_node.next = ListNode(s % 10)
            s //= 10
            cur_node = cur_node.next
        if s == 1:
            cur_node.next = ListNode(1)
        return dummy_node.next

問(wèn)題并不難,但是編碼上要注意。轉(zhuǎn)換成數(shù)字不是不可以,但是鏈表很長(zhǎng)的時(shí)候,就有越界的風(fēng)險(xiǎn)。

思考為什么要使用虛擬頭結(jié)點(diǎn),計(jì)算 carry 是一個(gè)常見(jiàn)的關(guān)于進(jìn)位的操作。第 445 題稍微復(fù)雜,要借助棧完成。

LeetCode 第 445 題:兩數(shù)相加 II

傳送門(mén):445. 兩數(shù)相加 II

刪除鏈表中等于給定值 val 的所有結(jié)點(diǎn)

LeetCode 第 203 題:刪除鏈表中等于給定值 val 的所有結(jié)點(diǎn)

一個(gè)對(duì)于鏈表常用的操作就是設(shè)置虛擬頭結(jié)點(diǎn)。

LeetCode 第 203 題:移除鏈表元素

傳送門(mén):移除鏈表元素

只要涉及跟鏈表頭結(jié)點(diǎn)有關(guān)的,都要設(shè)計(jì)虛擬頭結(jié)點(diǎn)來(lái)避免繁瑣的討論。

LeetCode 第 82 題

傳送門(mén):刪除排序鏈表中的重復(fù)元素 II。

LeetCode 第 83 題

傳送門(mén):刪除排序鏈表中的重復(fù)元素

和 82 題完全不同。

分析:這道題給出兩種解法。
相同之處:

  • 都使用了當(dāng)前指針和當(dāng)前指針的下一指針,一共兩個(gè)指針來(lái)幫助我們完成邏輯。
  • 當(dāng)前指針的下一指針和"當(dāng)前指針"作判斷,如果一樣,說(shuō)明應(yīng)該丟棄"當(dāng)前指針的下一指針",如果一樣,說(shuō)明應(yīng)該保留"當(dāng)前指針的下一指針"。
    不同之處在于如何保留:
  • 發(fā)現(xiàn)一樣的元素的時(shí)候,馬上把 next 指針后移;
  • 發(fā)現(xiàn)一樣的元素的時(shí)候,馬上把 current 指針挪到 next 指針的下一位。

關(guān)鍵點(diǎn):

  • 保證在遍歷過(guò)程中保持循環(huán)不變量的定義:current 總是指向第 1 次出現(xiàn)該數(shù)組的位置,next 用于遍歷,會(huì)走過(guò)所有的元素。

解法1:

提示:要考慮到一些特殊情況,才能寫(xiě)好邏輯。

public class Solution2 {

    /**
     * 從一個(gè)有序的鏈表中刪除重復(fù)的元素
     * 思路:從后面往前面看,有重復(fù)就刪除
     *
     * @param head
     * @return
     */
    public ListNode deleteDuplicates(ListNode head) {
        if(head==null){
            return null;
        }
        ListNode cur = head;
        ListNode next = cur.next;

        while (next!=null){
            if(next.val == cur.val){ //  1,1,1,1,2,3
                // next 后移一位
                next = next.next;
            }else { // 1,2,3
                cur.next = next; // 把 cur 的指針指向下一位
                cur = next; // 移動(dòng)指針
                next = next.next;
            }
        }
        // 這一行是一個(gè)要小心的陷阱
        cur.next = null;
        return head;
    }

    public static void main(String[] args) {
        ListNode head1 = createListNode(new int[]{1,1,2,2,2,2,3,3,3,3});
        Solution2 solution2 = new Solution2();
        ListNode head2 = solution2.deleteDuplicates(head1);
        printLinkedList(head2);

    }


    public static ListNode createListNode(int[] nums) {
        if (nums.length == 0) {
            return null;
        }

        ListNode head = new ListNode(nums[0]);
        ListNode curNode = head;

        for (int i = 1; i < nums.length; i++) {
            curNode.next = new ListNode(nums[i]);
            curNode = curNode.next;
        }
        return head;
    }


    // 超級(jí)簡(jiǎn)單的一個(gè)工具方法
    public static void printLinkedList(ListNode head) {
        ListNode curNode = head;
        while (curNode != null) {
            System.out.printf("%s\t", curNode.val);
            curNode = curNode.next;
        }
        System.out.printf("null");
    }
}

解法2

public class Solution3 {
    public ListNode deleteDuplicates(ListNode head) {
        if (head == null) {
            return null;
        }
        ListNode cur = head;
        ListNode next = cur.next;
        while (next != null) {
            if (next.val == cur.val) { // 1,1,2
                // 因?yàn)?next 不被使用了,所以 cur 的下一個(gè)就指向 next 的下一個(gè)
                cur.next = next.next;
                // 此時(shí)不能移動(dòng) cur,因?yàn)?cur 一定是第 1 個(gè)出現(xiàn)的元素,注意這里保持循環(huán)不變量的定義
                next = next.next;
            } else { // 1,2,3
                cur = next;
                next = next.next;
            }
        }
        return head;
    }

    public static void main(String[] args) {
        ListNode head1 = createListNode(new int[]{1, 1, 2, 2, 2, 2, 3, 3, 3, 3});
        Solution3 solution3 = new Solution3();
        ListNode head2 = solution3.deleteDuplicates(head1);
        printLinkedList(head2);
    }

    public static ListNode createListNode(int[] nums) {
        if (nums.length == 0) {
            return null;
        }
        ListNode head = new ListNode(nums[0]);
        ListNode curNode = head;
        for (int i = 1; i < nums.length; i++) {
            curNode.next = new ListNode(nums[i]);
            curNode = curNode.next;
        }
        return head;
    }

    // 超級(jí)簡(jiǎn)單的一個(gè)工具方法
    public static void printLinkedList(ListNode head) {
        ListNode curNode = head;
        while (curNode != null) {
            System.out.printf("%s\t", curNode.val);
            curNode = curNode.next;
        }
        System.out.printf("null");
    }
}

LeetCode 第 328 題:奇數(shù)(Odd)偶數(shù)(Even)鏈表

傳送門(mén): https://leetcode.com/problems/odd-even-linked-list/description/

思路:在遍歷的過(guò)程中,標(biāo)記奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn),把奇數(shù)節(jié)點(diǎn)和偶數(shù)節(jié)點(diǎn)分開(kāi)。最后把奇數(shù)節(jié)點(diǎn)的最后一個(gè)節(jié)點(diǎn)指向偶數(shù)節(jié)點(diǎn)的開(kāi)始節(jié)點(diǎn),具體細(xì)節(jié)請(qǐng)見(jiàn)代碼。

我的解答:

class ListNode {
    int val;
    ListNode next;

    ListNode(int x) {
        val = x;
    }
}

public class Solution {

    public ListNode oddEvenList(ListNode head) {
        if (head == null || head.next == null) {
            return head;
        }
        ListNode oddHead = head;
        ListNode evenHead = oddHead.next;
        ListNode oddNode = oddHead;
        ListNode evenNode = evenHead;
        ListNode currentNode = evenHead.next;
        boolean isodd = true;
        while (currentNode != null) {
            if (isodd) {
                oddNode.next = currentNode;
                oddNode = currentNode;
            } else {
                evenNode.next = currentNode;
                evenNode = currentNode;
            }
            isodd = !isodd;
            currentNode = currentNode.next;
        }
        isodd = !isodd;
        if (isodd) {
            oddNode.next = evenHead;
            evenNode.next = null;
        } else {
            oddNode.next = evenHead;
        }
        return oddHead;
    }


    public static void main(String[] args) {
        ListNode node1 = createListNode(new int[]{1, 2, 3, 4, 5});
        Solution solution = new Solution();
        ListNode result1 = solution.oddEvenList(node1);
        printLinkedList(result1);

        System.out.println("------");


        ListNode node2 = createListNode(new int[]{1, 2, 3, 4});
        ListNode result2 = solution.oddEvenList(node2);
        printLinkedList(result2);

        System.out.println("------");


        ListNode node3 = createListNode(new int[]{1, 2});
        ListNode result3 = solution.oddEvenList(node3);
        printLinkedList(result3);
    }

    public static ListNode createListNode(int[] nums) {
        if (nums.length == 0) {
            return null;
        }
        ListNode head = new ListNode(nums[0]);
        ListNode curNode = head;
        for (int i = 1; i < nums.length; i++) {
            curNode.next = new ListNode(nums[i]);
            curNode = curNode.next;
        }
        return head;
    }

    // 超級(jí)簡(jiǎn)單的一個(gè)工具方法
    public static void printLinkedList(ListNode head) {
        ListNode curNode = head;
        while (curNode != null) {
            System.out.printf("%s\t", curNode.val);
            curNode = curNode.next;
        }
        System.out.printf("null");
    }
}

網(wǎng)友的解答:http://blog.csdn.net/guicaisa/article/details/50557475
顯然,網(wǎng)友的解法會(huì)更簡(jiǎn)潔一些:
根據(jù)網(wǎng)友的解答自己寫(xiě)了一遍:

public class Solution2 {

    public ListNode oddEvenList(ListNode head) {
        if (head == null) {
            return head;
        }
        ListNode oddNode = head;
        ListNode evenNode = head.next;
        ListNode evenHead = evenNode;
        while (evenNode != null && evenNode.next != null) {
            oddNode.next = evenNode.next;
            oddNode = oddNode.next;
            evenNode.next = oddNode.next;
            evenNode = evenNode.next;
        }
        oddNode.next = evenHead;
        return head;
    }


    public static void main(String[] args) {
        ListNode node1 = createListNode(new int[]{1, 2, 3, 4, 5});
        Solution2 solution = new Solution2();
        ListNode result1 = solution.oddEvenList(node1);
        printLinkedList(result1);

        System.out.println("------");


        ListNode node2 = createListNode(new int[]{1, 2, 3, 4});
        ListNode result2 = solution.oddEvenList(node2);
        printLinkedList(result2);

        System.out.println("------");


        ListNode node3 = createListNode(new int[]{1, 2});
        ListNode result3 = solution.oddEvenList(node3);
        printLinkedList(result3);
    }

    public static ListNode createListNode(int[] nums) {
        if (nums.length == 0) {
            return null;
        }
        ListNode head = new ListNode(nums[0]);
        ListNode curNode = head;
        for (int i = 1; i < nums.length; i++) {
            curNode.next = new ListNode(nums[i]);
            curNode = curNode.next;
        }
        return head;
    }

    // 超級(jí)簡(jiǎn)單的一個(gè)工具方法
    public static void printLinkedList(ListNode head) {
        ListNode curNode = head;
        while (curNode != null) {
            System.out.printf("%s\t", curNode.val);
            curNode = curNode.next;
        }
        System.out.printf("null");
    }
}

LeetCode 第 147 題:?jiǎn)捂湵淼牟迦肱判?/h3>

重刷

LeetCode 第 148 題:?jiǎn)捂湵淼呐判?,使用歸并排序

LeetCode 第 24 題:兩兩交換鏈表中的結(jié)點(diǎn)

LeetCode 第 25 題:k 個(gè)一組交換鏈表

重刷

LeetCode 第 138 題:復(fù)制帶隨機(jī)指針的鏈表

LeetCode 第 109 題:有序鏈表轉(zhuǎn)換二叉搜索樹(shù)

英文網(wǎng)址:109. Convert Sorted List to Binary Search Tree ,中文網(wǎng)址:109. 有序鏈表轉(zhuǎn)換二叉搜索樹(shù) 。

LeetCode 第 237 題:刪除鏈表中的結(jié)點(diǎn)

要求:請(qǐng)編寫(xiě)一個(gè)函數(shù),使其可以刪除某個(gè)鏈表中給定的(非末尾的)結(jié)點(diǎn),您將只被給予要求被刪除的結(jié)點(diǎn)。

LeetCode 第 21 題:合并兩個(gè)有序鏈表

LeetCode 第 21 題:合并兩個(gè)有序鏈表

使用遞歸解決穿針引線,單雙轉(zhuǎn)換

穿針引線:

image-20190111135807481

遞歸寫(xiě)法:首先先寫(xiě)好遞歸終止條件。

image-20190111135825172

LeetCode 第 23 題:合并 K 個(gè)排序鏈表

英文網(wǎng)址:23. Merge k Sorted Lists ,中文網(wǎng)址:23. 合并K個(gè)排序鏈表 。

分析:歸并多個(gè)有序鏈表(多路歸并排序),要用到優(yōu)先隊(duì)列

LeetCode 第 19 題:刪除鏈表的倒數(shù)第 N 個(gè)結(jié)點(diǎn)

LeetCode 第 61 題:旋轉(zhuǎn)鏈表

LeetCode 第 143 題:重排鏈表

LeetCode 第 234 題:回文鏈表

LeetCode 第 141 題:找出鏈表中是否有環(huán)

LeetCode 第 142 題:找出鏈表的入口結(jié)點(diǎn)

LeetCode 第 114 題:二叉樹(shù)展開(kāi)為鏈表

要求:給定一個(gè)二叉樹(shù),原地將它展開(kāi)為鏈表。

例如,給定二叉樹(shù)

    1
   / \
  2   5
 / \   \
3   4   6

將其展開(kāi)為:

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

Java 代碼:后序遍歷

class Solution {
    public void flatten(TreeNode root) {
        if(root == null){
            return ;
        }
        
        flatten(root.left);
        flatten(root.right);
        
        if(root.left != null){
            TreeNode right = root.right;//記錄右結(jié)點(diǎn)
            root.right = root.left;
            root.left = null;//將左結(jié)點(diǎn)置空
            TreeNode node = root.right;//到左結(jié)點(diǎn)的最后一個(gè)結(jié)點(diǎn)
            while(node.right != null){
                node = node.right;
            }
            node.right = right; 
        }
    }
}

鏈表的中間結(jié)點(diǎn)

LeetCode 第 876 題:?jiǎn)捂湵淼囊粋€(gè)常見(jiàn)操作,設(shè)置快慢指針。


LeetCode 第 61 題:鏈表向右旋轉(zhuǎn)

傳送門(mén):61. 旋轉(zhuǎn)鏈表

給定一個(gè)鏈表,旋轉(zhuǎn)鏈表,將鏈表每個(gè)節(jié)點(diǎn)向右移動(dòng) k 個(gè)位置,其中 k 是非負(fù)數(shù)。

示例 1:

輸入: 1->2->3->4->5->NULL, k = 2
輸出: 4->5->1->2->3->NULL
解釋:
向右旋轉(zhuǎn) 1 步: 5->1->2->3->4->NULL
向右旋轉(zhuǎn) 2 步: 4->5->1->2->3->NULL

示例 2:

輸入: 0->1->2->NULL, k = 4
輸出: 2->0->1->NULL
解釋:
向右旋轉(zhuǎn) 1 步: 2->0->1->NULL
向右旋轉(zhuǎn) 2 步: 1->2->0->NULL
向右旋轉(zhuǎn) 3 步: 0->1->2->NULL
向右旋轉(zhuǎn) 4 步: 2->0->1->NULL

Python 代碼:

# Definition for singly-linked list.
class ListNode(object):
    def __init__(self, x):
        self.val = x
        self.next = None


class Solution(object):
    def rotateRight(self, head, k):
        """
        :type head: ListNode
        :type k: int
        :rtype: ListNode
        """
        if head is None or k <= 0:
            return head

        # 先看鏈表有多少元素
        node = head
        # 先數(shù)這個(gè)鏈表的長(zhǎng)度
        counter = 1
        while node.next:
            node = node.next
            counter += 1

        node.next = head
        k = k % counter
        node = head
        # counter - k - 1 可以取一些極端的例子找到規(guī)律
        for _ in range(counter - k - 1):
            node = node.next
        new_head = node.next
        node.next = None
        return new_head

(本節(jié)完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容