LeetCode 常见链表问题 (python实现)

链表

链表问题很多可以采用递归进行操作。递归理清思路的时候,主要考虑问题最简单的情况。当只有一个节点或两个节点的时候,问题是如何解决的。理清递归退出的条件,可以更加清晰代码的作用。

1. 找出两个链表的交点 LeetCode 160 Easy

问题的核心思想是:两个链表被分成三个部分,a、b、c. 如果他们有共同的交点那么一定有公式 a + c + b = b + c + a a+c+b = b+c+a a+c+b=b+c+a,因此当a链表循环到末尾时,跳转到b链表上。那么他们一定会同时到达同一个节点上去。

如果不存在共同链c,那么有 a + b = b + a a+b=b+a a+b=b+a,他们会同时到达对方的末尾None处,因此也会退出循环。

如果题目只是判断是否相交不需要给出相交节点的值,那么还有如下思路:

  • 将A链表的尾巴连接到B链表的表头,如果B链表存在循环,那么相交
  • 直接将A、B两个链表循环到末尾,如果末尾节点相同,那么相交
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, x):
#         self.val = x
#         self.next = None

class Solution:
    def getIntersectionNode(self, headA: ListNode, headB: ListNode) -> ListNode:
        la, lb = headA, headB
        if la == None or lb == None: return None
        while la != lb:
            la = la.next if la != None else headB
            lb = lb.next if lb != None else headA
        return la
//java代码
public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        ListNode l1 = headA, l2 = headB;
        if (l1 == null || l2 == null) return null;
        while (l1 != l2) {
            l1 = (l1 != null) ? l1.next : headB;
            l2 = (l2 != null) ? l2.next : headA;
        }
        return l2;
    }
}

2. 链表反转 LeetCode 206 Easy

  • 递归(从只有两个节点的最简单情形入手思考)
  • 头插法(需要创建一个全新的链表,空间复杂度 O ( n ) O(n) O(n)
  • 直接指针修改(内存空间消耗小,空间复杂度 O ( 1 ) O(1) O(1)

头插法实现较为简单,这里给出递归实现的python代码。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
    	# 递归的出口
        if head == None or head.next == None: return head
        
        newHead = self.reverseList(head.next)
        # 考虑只有2个节点的情形,就可以快速理解递归的思想(node1 -> node2 -> null)
        head.next.next = head
        head.next = None
        
        return newHead
// 头插法:Java代码
class Solution {
    public ListNode reverseList(ListNode head) {
        if (head == null) return null;
        // 创建一个全新的表头,使用头插法将节点插入次表头
        ListNode resHead = new ListNode(0, null);
        while (head != null) {
            ListNode tmp = new ListNode(head.val);
            tmp.next = resHead.next;
            resHead.next = tmp;
            head = head.next;
        }
        // 返回头结点的下一个节点
        return resHead.next;
    }
}

3. 归并两个有序链表 LeetCode 21 Easy

  • 递归
  • 依次合并
# 依次合并   56ms, 13.9MB
# 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) -> ListNode:
        headNew = ListNode()
        headP = headNew
        while l1 and l2:
            if l1.val < l2.val:
                headP.next = l1
                l1 = l1.next
                headP = headP.next
            else:
                headP.next = l2
                l2 = l2.next
                headP = headP.next
        if l1:
            headP.next = l1
        if l2:
            headP.next = l2
            
        return headNew.next
      
# 递归
class Solution:
    def mergeTwoLists(self, l1: ListNode, l2: ListNode) -> ListNode:
        if l1 == None: return l2
        if l2 == None: return l1
        if l1.val < l2.val:
            l1.next = self.mergeTwoLists(l1.next, l2)
            return l1
        else:
            l2.next = self.mergeTwoLists(l1, l2.next)
            return l2

4. 有序链表删除重复节点 LeetCode 19 Medium

  • 链表循环删除节点
  • 递归(递归思想需要把问题简化:如果当前链表只包含两个节点,那么进行比较,返回删除节点,否则进行递归)
# while循环删除前后相同节点
# 44ms, 13.9MB
# 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:
        if head == None or head.next == None: return head
        headP, headPP = head, head.next
        
        while headPP.next:
            if headP.val == headPP.val:
                headP.next = headPP.next
            else:
                headP = headPP
                
        headPP = headPP.next
        
        if headP.val == headPP.val:
            headP.next = None
        return head
      
# 递归删除
# 80ms, 14.1MB
class Solution:
    def deleteDuplicates(self, head: ListNode) -> ListNode:
        if head == None or head.next == None: return head
        head.next = self.deleteDuplicates(head.next)
        return head.next if head.val == head.next.val else head

5. 删除链表的倒数第n个节点 LeetCode 19 Medium

首先明确n <= len(LinkList),利用滑动窗口的思想,构建一个长度为n的窗口,两个指针一前一后的滑动行进。right指针指到结尾的时候,left指针位置就是应该删除的n的位置。

# 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:
        right = head
        left = head
        
        while n > 0:
            right = right.next
            n -= 1
        if right == None:
            return head.next
        while right.next != None:
            right = right.next
            left = left.next
        left.next = left.next.next
        return head

6. 交换链表的相邻两个节点 LeetCode 24 Medium

题目要求:不能修改节点val的值,O(1)的空间复杂度。

思路:创建一个新的头结点,用新的头结点不断修改其后面两个节点的顺序,并后移两位,知道循环到链的末尾。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: ListNode) -> ListNode:
        resHead = res = ListNode(0, head)

        while res.next != None and res.next.next != None:
            p = res.next
            pp = p.next
            next = pp.next
            
            p.next = next
            pp.next = p
            res.next = pp
            
            res = pp.next
        return resHead.next

7. 链表求和 leetCode 445 Medium

题目给出提示:可不可以不修改链表的结构,换句话说就是不使用逆转链表的方法,直接计算结果。如果不能翻转链表,那么一定需要其他的数据结构来记录某些信息。这里使用的是栈。

方法一:逆序链表,然后遍历链表用普通的加法规则

方法二:使用栈存储两个链表中的值,栈的FILO(First In Last Out)特性,刚好可以把链表中的值倒序。

注意: 这里两个链表可以采用一个循环完成,只要一个链表没有到达结尾,循环即不停止。直接按照之前累加方法即可,循环开始部分添加两个if,判断空结果。

# 方法一
# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
# 下面代码新链表构成部分需要优化,可以直接生成新的表头,而不需要从第二个位置开始构建新的链表。
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        l1 = self.reverse(l1)
        l2 = self.reverse(l2)
        total = 0
        resHead = ListNode(0, None)
        
        while l1 != None or l2 != None:
            if l1 != None:
                total += l1.val
                l1 = l1.next
            if l2 != None:
                total += l2.val
                l2 = l2.next
            
            if total > 9:
                tmp = ListNode(total % 10, None)
                total //= 10
                if resHead.next:
                    tmp.next = resHead.next
                    resHead.next = tmp
                else:
                    resHead.next = tmp
            
            else:
                tmp = ListNode(total, None)
                total = 0
                if resHead.next:
                    tmp.next = resHead.next
                    resHead.next = tmp
                else:
                    resHead.next = tmp
        if total != 0:
            tmp = ListNode(total, None)
            tmp.next = resHead.next
            resHead.next = tmp
                
        return resHead.next
            

    def reverse(self, head):
        if head == None or head.next == None: return head
        
        newHead = self.reverse(head.next)
        head.next.next = head
        head.next = None
        
        return newHead
# 方法二
class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        stackL1 = []
        stackL2 = []
        
        while l1:
            stackL1.append(l1.val)
            l1 = l1.next
        while l2:
            stackL2.append(l2.val)
            l2 = l2.next
        
        total = 0
        resFirst = ListNode(0, None)
        while len(stackL1) > 0 or len(stackL2) > 0:
            if len(stackL1) > 0:
                total += stackL1.pop()
            if len(stackL2) > 0:
                total += stackL2.pop()
            
            resFirst.val = total % 10
            total //= 10
            tmp = ListNode(total, resFirst)
            resFirst = tmp
        return resFirst.next if resFirst.val == 0 else resFirst

8. 回文链表

有多种思路

  1. 逆序构建一个相同的链表,进行比较,空间复杂度 O ( n ) O(n) O(n)
  2. 原来链表找到中间节点,前半段,后半段比较,空间复杂度 O ( 1 ) O(1) O(1)

Note: 难点在于理清三个指针逆序链表的顺序。剩下的判断就容易多了,详细描述可参考博主Hustudent20080101

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def isPalindrome(self, head: ListNode) -> bool:
        if head == None: return True
        length = 0
        point = head
        while point:
            length += 1
            point = point.next
        if length == 1:
            return True
        
        if length % 2 == 0:    # 123 456   leaael
            pre = length // 2
            revert = length // 2
        else:
            pre = length // 2 + 1    # 123 4 567
            revert = length // 2
            
        
        point_head = head
        point_prev = None
        for i in range(pre):
            point_head = point_head.next
            
        
        while point_head != None:
            point_next = point_head.next
            point_head.next = point_prev
            point_prev = point_head
            point_head = point_next
            
        while point_prev != None:
            if head.val != point_prev.val:
                return False
            head = head.next
            point_prev = point_prev.next
            
        return True

9. 分隔链表

本题理解题意之后,难度不大。主要是两个指针prev, head在链表上不停移动,构造结果的过程。如果能想到生成ans数组,那么本题就很好理解了。

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def splitListToParts(self, root: ListNode, k: int) -> List[ListNode]:
        total_len = 0
        head = root
        while head:
            total_len += 1
            head = head.next
            
        ans = [None for _ in range(k)]
        
        l, r = total_len // k, total_len % k
        
        prev, head = None, root
        
        for i in range(k):
            ans[i] = head
            for j in range(l + (1 if r > 0 else 0)):
                prev, head = head, head.next
            if prev: prev.next = None
            r -= 1
            
        return ans

10. 链表元素按奇偶顺序重新构造

改题目是较简单的链表操作。

创建两个用来单独存储奇数位偶数位的空链表,遍历原始链表,当遍历到奇数位置时,添加到奇数位链表中,当遍历到偶数位置时,添加到偶数位置上。最会将两个链表整合,返回。

class Solution:
    def oddEvenList(self, head: ListNode) -> ListNode:
        point_odd = odd = ListNode(0, None)
        point_even = even = ListNode(0, None)
        
        i = 1
        while head != None:
            if i % 2 == 1:
                point_odd.next = head
                point_odd = point_odd.next
                head = head.next
                i += 1
            else:
                point_even.next = head
                point_even = point_even.next
                head = head.next
                i += 1
        point_even.next = None
        point_odd.next = even.next
        
        return odd.next
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值