献芹奏曝-Python面试题-算法-链表篇
上一篇:献芹奏曝-Python面试题
开篇的话:本文目的是收集和归纳力扣上的算法题,希望用python语言,竭我所能做到思路最清奇、代码最简洁、方法最广泛、性能最高效,了解常见题目,找到最利于记忆的答案,更加从容的应对面试。希望广思集益,共同进步。
链表篇
-
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
运行结果:1:耗时超过36%。2:内存超过78%
知识点/技巧: 把下一位赋值给当前
-
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%
知识点/技巧:递归
-
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;
- 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%
知识点/技巧:取最小数据添加到当前节点后面
- 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
运行结果:1:耗时超过90%。2:内存超过80%
知识点/技巧:利用set集合判断,其实list也可以。
-
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:巧用商与余数,代码简洁
-