LeetCode-题目详解(六):链表【链表的中间节点:左半链表的结尾节点】【slow = head | fast = head | while fast and fast.next】

这篇博客介绍了如何在链表中进行各种操作,包括插入排序、删除节点、合并排序链表以及找到链表的中间节点。详细探讨了不同的方法,如原地插入排序、使用快慢指针找链表环和中间节点等,并提供了不同时间复杂度的解决方案。此外,还讨论了删除排序链表中的重复元素、合并两个有序链表以及删除链表的倒数第N个节点等问题。
摘要由CSDN通过智能技术生成

这里写目录标题

排序

147-对链表进行插入排序

对链表进行插入排序。
在这里插入图片描述

插入排序的动画演示如上。从第一个元素开始,该链表可以被认为已经部分排序(用黑色表示)。

每次迭代时,从输入数据中移除一个元素(用红色表示),并原地将其插入到已排好序的链表中。

插入排序算法:

  1. 插入排序是迭代的,每次只移动一个元素,直到所有元素可以形成一个有序的输出列表。
  2. 每次迭代中,插入排序只从输入数据中移除一个待排序的元素,找到它在序列中适当的位置,并将其插入。
  3. 重复直到所有输入数据插入完为止。

示例 1:

输入: 4->2->1->3
输出: 1->2->3->4

示例 2:

输入: -1->5->3->4->0
输出: -1->0->3->4->5

方法一:原地插入排序(执行用时: 144 ms)

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
   
public:
    ListNode* insertionSortList(ListNode* head) {
   
        auto dummy = new ListNode();
        dummy->next = head;
        auto lastSorted = head;
        auto curr = head->next;

        while(curr){
   
            auto nxt = curr->next;

            // 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
            if(lastSorted->val <= curr->val){
   
                lastSorted = lastSorted->next;
            // 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
            }else{
     // lastSorted.val > curr.val
                auto prev = dummy;
                while(prev->next->val < curr->val){
    // 查询插入点
                    prev = prev->next;
                }
                curr->next = prev->next;    // 将“待处理节点”curr的下一个节点为插入点的下一个节点    
                prev->next = curr;          // 插入点的前一个节点连接到“待处理节点”curr
                lastSorted->next = nxt;     // 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
            }
            curr = nxt;                     // 开始堆下一个节点进行插入排序
        }
        return dummy->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        lastSorted = head
        curr = head.next

        while curr:
            nxt = curr.next
            
            # 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
            if lastSorted.val <= curr.val:
                lastSorted = lastSorted.next
            
            # 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
            else:   # lastSorted.val > curr.val
                prev = dummy
                while prev.next.val < curr.val: # 查询插入点
                    prev = prev.next
                curr.next = prev.next   # 将“待处理节点”curr的下一个节点为插入点的下一个节点    
                prev.next = curr        # 插入点的前一个节点连接到“待处理节点”curr
                lastSorted.next = nxt   # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
            
            curr = nxt  # 开始堆下一个节点进行插入排序

        return dummy.next
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        if not head:
            return head
        
        dummyHead = ListNode(0)
        dummyHead.next = head

        lastSorted = head
        curr = lastSorted.next

        while curr:
            # 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
            if lastSorted.val <= curr.val:
                lastSorted = lastSorted.next
            
            # 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
            if lastSorted.val > curr.val:
                prev = dummyHead
                while prev.next.val <= curr.val:    # 查询插入点
                    prev = prev.next
                lastSorted.next = curr.next # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点
                curr.next = prev.next   # 将“待处理节点”curr的下一个节点为插入点的下一个节点
                prev.next = curr    # 插入点的前一个节点连接到“待处理节点”curr
            

            curr = lastSorted.next
        
        return dummyHead.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        lastSorted = head
        curr = head.next

        while curr:
            nxt = curr.next
            
            # 如果已排序的链表部分的尾结点的值小于当前节点的值,则直接将尾结点指针向后移动一位
            if lastSorted.val <= curr.val:
                lastSorted = lastSorted.next
            
            # 如果链表的已排序部分的尾结点的值大于当前节点的值,则需要将当前节点插入到已排序部分中的合适位置,插入完毕后,尾结点指针也向后移动了一位
            if lastSorted.val > curr.val:
                prev = dummy
                temp = prev.next # 此处不能用temp = head, 因为由于后面元素插入排序的原因, 最初的head节点有可能已经不是头节点了
                while temp.val < curr.val: # 查询插入点
                    prev = prev.next
                    temp = temp.next
                curr.next = prev.next   # 将“待处理节点”curr的下一个节点为插入点的下一个节点    
                prev.next = curr        # 插入点的前一个节点连接到“待处理节点”curr
                lastSorted.next = nxt   # 将已排序的链表部分的尾结点链接到“待处理节点”curr的下一个节点,使得整个链表重新连接起来
            
            curr = nxt  # 开始堆下一个节点进行插入排序

        return dummy.next

方法二(错误重复操作,导致时间复杂度增加;执行用时: 1060 ms):

if lastSorted.val < curr.val 条件里没有将curr指针移动到next,导致curr停留在原来的位置,导致重复插入排序;

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def insertionSortList(self, head: ListNode) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        lastSorted = head
        curr = head.next

        while curr:
            if lastSorted.val < curr.val:
                lastSorted = lastSorted.next
                # curr = curr.next # 如果没有此步骤,会导致重复进行插入排序
            else:
                prev = dummy
                while prev.next.val < curr.val:
                    prev = prev.next

                nxt = curr.next
                curr.next = prev.next
                prev.next = curr
                lastSorted.next = nxt
                curr = nxt

        return dummy.next

方法三:开辟新的链表(执行用时: 1336 ms)

148-排序链表

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。

示例 1:

在这里插入图片描述

输入:head = [4,2,1,3]
输出:[1,2,3,4]

示例 2:
在这里插入图片描述

输入:head = [-1,5,3,4,0]
输出:[-1,0,3,4,5]

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目在范围 [ 0 , 5 ∗ 1 0 4 ] [0, 5 * 10^4] [0,5104]
  • − 1 0 5 < = N o d e . v a l < = 1 0 5 -10^5 <= Node.val <= 10^5 105<=Node.val<=105

进阶:你可以在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序吗?


方法一:最小堆

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def sortList(self, head: ListNode) -> ListNode:
        heap = []
        curr = head
        while curr:
            heapq.heappush(heap, curr.val)
            curr = curr.next
        
        dummy = ListNode()
        prev = dummy
        while heap:
            node = ListNode(heapq.heappop(heap))
            prev.next = node
            prev = prev.next

        return dummy.next

方法二:归并排序

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    # 归并排序
    def sortList(self, head: ListNode) -> ListNode:
        if not head or not head.next: 
            return head
        
        left_end = self.find_left_end(head)
        new_head = left_end.next 
        left_end.next = None

        left = self.sortList(head)
        right = self.sortList(new_head)
        
        return self.merged(left, right)
    
    # 快慢指针查找链表中点(左半链表的结尾节点)
    def find_left_end(self, head):
        if head is None or head.next is None: 
            return head
        
        slow,fast = head, head
        
        while fast and fast.next and fast.next.next:
            slow=slow.next
            fast=fast.next.next
        
        return slow
    
    # 合并有序链表
    def merged(self, left, right):
        dummy = ListNode()
        prev = dummy
        while left and right:
            if left.val < right.val: 
                prev.next = left
                left = left.next
            else: 
                prev.next = right
                right = right.next
            prev = prev.next
        
        prev.next = left if left else right
        
        return dummy.next

删除

剑指 Offer 18. 删除链表的节点

给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。

返回删除后的链表的头节点。

注意:此题对比原题有改动

示例 1:

输入: head = [4,5,1,9], val = 5
输出: [4,1,9]
解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.

示例 2:

输入: head = [4,5,1,9], val = 1
输出: [4,5,9]
解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.

说明:

  • 题目保证链表中节点的值互不相同
  • 若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
   
public:
    ListNode* deleteNode(ListNode* head, int val) {
   
        ListNode* dummy = new ListNode();
        dummy->next = head;
        auto prev = dummy;
        auto curr = head;

        while(curr){
   
            auto nxt = curr->next;
            if(curr->val == val){
   
                prev->next = nxt;
            }
            prev = prev->next;
            curr = nxt;
        }

        return dummy->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None
class Solution:
    def deleteNode(self, head: ListNode, val: int) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        prev = dummy
        curr = head

        while curr:
            nxt = curr.next
            if curr.val == val:
                prev.next = nxt
            prev = prev.next
            curr = nxt

        return dummy.next

83-删除排序链表中的重复元素

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除所有重复的元素,使每个元素 只出现一次 。

返回同样按升序排列的结果链表。

示例 1:
在这里插入图片描述

输入:head = [1,1,2]
输出:[1,2]

示例 2:
在这里插入图片描述

输入:head = [1,1,2,3,3]
输出:[1,2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

方法一:一次遍历

由于给定的链表是排好序的,因此重复的元素在链表中出现的位置是连续的,因此我们只需要对链表进行一次遍历,就可以删除重复的元素。

具体地,我们从指针 cur 指向链表的头节点,随后开始对链表进行遍历。如果当前 cur 与 cur.next 对应的元素相同,那么我们就将cur.next 从链表中移除;否则说明链表中已经不存在其它与 cur 对应的元素相同的节点,因此可以将cur 指向 cur.next。

当遍历完整个链表之后,我们返回链表的头节点即可。

细节:当我们遍历到链表的最后一个节点时,cur.next 为空节点,如果不加以判断,访问 cur.next 对应的元素会产生运行错误。因此我们只需要遍历到链表的最后一个节点,而不需要遍历完整个链表。

class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if not head:
            return head

        cur = head
        while cur.next:
            if cur.val == cur.next.val:
                cur.next = cur.next.next
            else:
                cur = cur.next

        return head
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
   
public:
    ListNode* deleteDuplicates(ListNode* head) {
   
        if(!head){
   
            return head;
        }
        auto curr = head;
        while(curr->next){
   
            if(curr->val == curr->next->val){
   
                curr->next = curr->next->next;
            }else{
   
                curr = curr->next;
            }
        }

        return head;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        
        dummy = ListNode()
        dummy.next = head
        prev = dummy
        curr = head

        while curr:
            # 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个重复节点位置
            while curr.next and curr.val == curr.next.val:
                curr = curr.next 

            # 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
            # 则将prev.next指向最后一个重复节点curr的下一个节点,进入下一个循环
            if prev.next != curr:
                prev.next = curr
            # 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
            else: 
                prev = prev.next
                curr = curr.next


        return dummy.next

82-删除排序链表中的重复元素 II

存在一个按升序排列的链表,给你这个链表的头节点 head ,请你删除链表中所有存在数字重复情况的节点,只保留原始链表中 没有重复出现 的数字。

返回同样按升序排列的结果链表。

示例 1:
在这里插入图片描述

输入:head = [1,2,3,3,4,4,5]
输出:[1,2,5]

示例 2:
在这里插入图片描述

输入:head = [1,1,1,2,3]
输出:[2,3]

提示:

  • 链表中节点数目在范围 [0, 300] 内
  • -100 <= Node.val <= 100
  • 题目数据保证链表已经按升序排列

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
   
public:
    ListNode* deleteDuplicates(ListNode* head) {
   
        auto dummy = new ListNode();
        dummy->next = head;
        auto prev = dummy;
        auto curr = head;

        while(curr){
   
            // 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个节点位置
            while(curr->next && curr->val == curr->next->val){
   
                curr = curr->next;
            }
            // 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
            // 则将prev.next指向最后一个重复节点curr的下一个节点【相当于跳过了剩余的最后一个重复节点】,curr移动到下一个节点位置
            if(prev->next != curr){
   
                prev->next = curr->next;
                curr = curr->next;
            }
            // 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
            else{
   
                prev = prev->next;
                curr = curr->next;
            }
        }

        return dummy->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution(object):
    def deleteDuplicates(self, head):

        dummy = ListNode()
        dummy.next = head
        prev = dummy
        curr = head
        
        while curr:
            # 如果有重复节点,则跳过当前的重复节点,使得curr指向当前重复元素的最后一个节点位置
            while curr.next and curr.val == curr.next.val:
                curr = curr.next
            
            # 如果经过上一步的while操作,prev的next不再指向原来指向的curr,则说明curr节点有重复元素,
            # 则将prev.next指向最后一个重复节点curr的下一个节点【相当于跳过了剩余的最后一个重复节点】,curr移动到下一个节点位置
            if prev.next != curr:
                prev.next = curr.next
                curr = curr.next
            # 如果经过上一步的while操作,prev的next依旧指向原来指向的curr,则说明curr节点没有重复元素,则将prev、curr分别向后移动一位,分析下一个元素
            else:
                prev = prev.next
                curr = curr.next

        return dummy.next

面试题 02.03-删除中间节点

若链表中的某个节点,既不是链表头节点,也不是链表尾节点,则称其为该链表的「中间节点」。

假定已知链表的某一个中间节点,请实现一种算法,将该节点从链表中删除。

例如,传入节点 c(位于单向链表 a->b->c->d->e->f 中),将其删除后,剩余链表为 a->b->d->e->f

示例:

输入:节点 5 (位于单向链表 4->5->1->9 中)
输出:不返回任何数据,从链表中删除传入的节点 5,使链表变为 4->1->9

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
   
public:
    void deleteNode(ListNode* node) {
   
        node->val = node->next->val;
        node->next = node->next->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def deleteNode(self, node):
        """
        :type node: ListNode
        :rtype: void Do not return anything, modify node in-place instead.
        """
        node.val = node.next.val
        node.next = node.next.next

19-删除链表的倒数第 N 个结点【剑指 Offer II 021. 删除链表的倒数第 n 个结点】

给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。

进阶:你能尝试使用一趟扫描实现吗?

示例 1:
在这里插入图片描述

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

示例 2:

输入:head = [1], n = 1
输出:[]

示例 3:

输入:head = [1,2], n = 1
输出:[1]

提示:

  • 链表中结点的数目为 sz
  • 1 <= sz <= 30
  • 0 <= Node.val <= 100
  • 1 <= n <= sz

方法一:多指针

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
   
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
   
        auto dummy = new ListNode();
        dummy->next = head;
        auto prev = dummy;
        auto slow = head;
        auto fast = head;

        for(int i = 0; i < n; i++){
   
            fast = fast->next;
        }

        while(fast){
   
            fast = fast->next;
            slow = slow->next;
            prev = prev->next;
        }

        prev->next = slow->next;

        return dummy->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        prev = dummy
        slow = head
        fast = head

        for _ in range(n):
            fast = fast.next
        
        # 将fast指针移动到链表尾节点,此时slow位于倒数第n个节点,prev位于倒数第n个节点的前一个节点
        while fast:
            fast = fast.next
            slow = slow.next
            prev = prev.next
        
        prev.next = slow.next

        return dummy.next
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def removeNthFromEnd(self, head: ListNode, n: int) -> ListNode:
        dummy = ListNode()
        dummy.next = head
        slow = head
        fast = head
        prev = dummy
        
        for _ in range(n - 1):
            fast = fast.next
        
        while fast and fast.next:
            prev = prev.next
            slow = slow.next
            fast = fast.next
     
        prev.next = slow.next

        return dummy.next

合并

21-合并两个有序链表 【剑指 Offer 25. 合并两个排序的链表】

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 1:
在这里插入图片描述

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

参考归并排序中的归并步骤

方法一:迭代法

在这里插入图片描述

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
   
public:
    ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
   
        auto dummy = new ListNode();
        auto prev = dummy;

        auto curr01 = l1;
        auto curr02 = l2;

        while(curr01 && curr02){
   
            if(curr01->val < curr02->val){
   
                prev->next = curr01;
                prev = prev->next;
                curr01 = curr01->next;
            }else{
   
                prev->next = curr02;
                prev = prev->next;
                curr02 = curr02->next;
            }
        }
        while(curr01){
   
            prev->next = curr01;
            prev = prev->next;
            curr01 = curr01->next;
        }
        while(curr02){
   
            prev->next = curr02;
            prev = prev->next;
            curr02 = curr02->next;
        }

        return dummy->next;
    }
};
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值