献芹奏曝-Python面试题-算法-链表篇

上一篇:献芹奏曝-Python面试题 

      开篇的话:本文目的是收集和归纳力扣上的算法题,希望用python语言,竭我所能做到思路最清奇、代码最简洁、方法最广泛、性能最高效,了解常见题目,找到最利于记忆的答案,更加从容的应对面试。希望广思集益,共同进步。

链表篇

  1. 237. 删除链表中的节点(难度系数✯)

    # 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
    View Code

    运行结果:1:耗时超过36%。2:内存超过78% 

    知识点/技巧: 把下一位赋值给当前

  2. 19. 删除链表的倒数第 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: Optional[ListNode], n: int) -> Optional[ListNode]:
            # 1:通过for循环计算列表长度。TODO:可以单独封装成方法
            _len = 0
            _len_head = head
            while _len_head:
                _len += 1
                _len_head = _len_head.next
            pre = head
            # 删除头结点
            if _len-n == 0:            
                return head.next
            # 找到前一个结点 【1,2,3,4,5】 n=2
            for i in range(_len-n-1):
                pre = pre.next
            # 让前一个结点的next指向要删除结点的next
            pre.next = pre.next.next
            return head
    
    
           
    方法一

    运行结果:1:耗时超过55%。2:内存超过93% 

    知识点/技巧: 通过for循环,计算列表长度。列表长度-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: Optional[ListNode], n: int) -> Optional[ListNode]:
            slow = head
            fast = head
            # 快的先走n步
            for i in range(n):
                fast = fast.next
            if not fast:
                # fast 走到末尾了,表示删除首节点
                return head.next        
            while fast.next:
                fast = fast.next
                slow = slow.next
            # fast比slow快n步,最后一次fast.next条件成立,fast走到了倒数第二个结点,slow也走到了倒数N+1个结点
            slow.next = slow.next.next
            return head
            
    方法二-双指针

    运行结果:1:耗时超过93%。2:内存超过90% 

    知识点/技巧: 通过双指针,一个在前,一个在后。前的到头,后面的到达目标位置

    # 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: Optional[ListNode], n: int) -> Optional[ListNode]:
            pos = self.length(head,n)
            if pos == n:
                return head.next
            return head
    
        def length(self, head: Optional[ListNode], n: int):
            if head is None:
                return 0
            pos = self.length(head.next,n)+1
            if pos == n+1:
                head.next = head.next.next
            return pos
            
    方法三-递归

    运行结果:1:耗时超过8%。2:内存超过93% 

    知识点/技巧:递归

  3. 206. 反转链表(难度系数✯)

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    from copy import deepcopy
    class Solution:
        def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:        
            if not head or not head.next:
                return head
            val_list = []
            # 暴力反转,先把所有的值放到列表中,再把列表中的值倒着重写组装成列表
            while head.next:
                cur_head = deepcopy(head)
                cur_head.next = None
                val_list.append(cur_head)
                head = head.next
            new_head = head
            for item in reversed(val_list):
                head.next = item
                head = head.next        
            return new_head  
    暴力反转

    运行结果:1:超时

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    from copy import deepcopy
    class Solution:
        def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:        
    
            new_head = None
            while head:
                # temp = head.next # 先获取到下一个节点,把它做head节点,从而实现不断while循环
                # head.next = new_head # 实现反转 
                # new_head = head
                # head = temp 
                head.next,new_head,head =new_head, head,head.next              
            return new_head 
    双链表求解 一行代码

    运行结果:1:耗时超过66%。2:内存超过76% 

    知识点/技巧:把原链表的结点一个个摘掉,每次摘掉的链表都让他成为新的链表的头结点,然后更新新链表

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    from copy import deepcopy
    class Solution:
        def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:        
            if not head or not head.next:
                return head
            reverse = self.reverseList(head.next)
            head.next.next = head
            head.next = None
            return reverse
    递归调用

    运行结果:1:耗时超过99.06%。2:内存超过8.89% 

    知识点/技巧:递归调用之后head.next节点就会成为reverse节点的尾结点,我们可以直接让head.next.next = head;

  4. 21. 合并两个有序链表(难度系数✯)
    # 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, list1: Optional[ListNode], list2: Optional[ListNode]) -> Optional[ListNode]:
            dummy = ListNode()
            move = dummy
            while list1 and list2:
                if list1.val < list2.val:
                    move.next = list1
                    list1 = list1.next
                else:
                    move.next = list2
                    list2 = list2.next
                # 每次比完,移动一位
                move = move.next   
            move.next = list1 if list1 else list2
            return dummy.next
    方法一

    运行结果:1:耗时超过42%。2:内存超过49% 

    知识点/技巧:取最小数据添加到当前节点后面

  5. 141. 环形链表 (难度系数✯)
    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            if not head:
                return False
            fast = head
            slow = head
            while fast and fast.next:
                slow = slow.next
                fast = fast.next.next
                if slow == fast:
                    return True            
            else:
                # 有尽头,所以不为环
                return False
    快慢指针

    运行结果:1:耗时超过76%。2:内存超过71% 

    知识点/技巧:慢指针针每次走一步,快指针每次走两步,如果相遇就说明有环,如果有一个为空说明没有环。类似钟表上的分针与秒针

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            i=0
            while head and head.next and i<10**4+1 :
                head = head.next
                i += 1       
            else:
                return i>10**4
    暴力破解

    运行结果:1:耗时超过31%。2:内存超过31% 

    知识点/技巧:根据题意,设置10**4为边界。

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, x):
    #         self.val = x
    #         self.next = None
    
    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            set_result = set()
            while head:
                if head in set_result:
                    return True
                else:
                    set_result.add(head)  
                head = head.next   
            else:
                return False
    set集合

    运行结果:1:耗时超过90%。2:内存超过80% 

    知识点/技巧:利用set集合判断,其实list也可以。 

  6. 2. 两数相加(难度系数

    # Definition for singly-linked list.
    # class ListNode:
    #     def __init__(self, val=0, next=None):
    #         self.val = val
    #         self.next = next
    class Solution:
        """
        1:如何实现一步一步往下走? l1 = l1.next
        2:如何实现返回是从head返回?定义两个 head 和 move 变量,
        """
        is_add_one = False # 定义全局变量,确认是否需要进位 +1
        def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            if not l1:
                return l2
            if not l2:
                return l1        
            head = move = ListNode()        
            while l1 or l2: 
                sum_val = self.get_sum_val(l1,l2)      
                move.next = ListNode(sum_val)
                move = move.next
                if l1:
                    l1 = l1.next 
                else:
                    l1 = None
                if l2:
                    l2 = l2.next  
                else:
                    l2 = None
            # 最后别忘了考虑是否需要补位
            if self.is_add_one:
                move.next = ListNode(1)
                move = move.next
            return head.next
    
        def get_sum_val(self,n1,n2):
            """求和的时候别忘了考虑上一步是否有进位,当前位是否有进位"""
            sum_val = 0
            if n1 and n1.val:
                sum_val += n1.val
            if n2 and n2.val:
                sum_val += n2.val
            if self.is_add_one:
                sum_val += 1
            if sum_val >= 10:
                self.is_add_one = True
                return sum_val-10
            else:
                self.is_add_one = False
                return sum_val
    方法一

    运行结果:1:耗时超过31%。2:内存超过100% 

    知识点/技巧:1:注意进位

    # 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: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            """
            1:如何实现一步一步往下走? l1 = l1.next
            2:如何实现返回是从head返回?定义两个 head 和 move 变量,
            """
            # 定义链表用于返回
            head = move = ListNode()
            # 定义变量
            tmp = val = 0
    
            while tmp or l1 or l2:
                val = tmp # 如果上次计算有进位, tmp为1
                if l1:
                    val += l1.val
                    l1 = l1.next
                if l2:
                    val += l2.val
                    l2 = l2.next
                tmp = val // 10 
                val = val % 10 
    
                move.next = ListNode(val)
                move = move.next
    
            return head.next
    方法二

    运行结果:1:耗时超过72.72%。2:内存超过96.94% 

    知识点/技巧:1:巧用商与余数,代码简洁

  7.  

 

posted @ 2022-09-03 15:36  逍遥小天狼  阅读(38)  评论(0编辑  收藏  举报