Loading

刷题笔记 - 腾讯50题(下)

接上篇:刷题笔记 - 腾讯50题(上)

From: 🐧 腾讯精选练习 50 题 - 力扣(LeetCode),按通过率降序排列


23. Merge k Sorted Lists

  • 多指针。找出lists中val最小的node,把它拼入新链表,然后它的next替换它,重复这个过程:

    class Solution:
        def mergeKLists(self, lists: List[Optional[ListNode]]) -> Optional[ListNode]:
            new_head = None
            new_tail = None
            lists = [head for head in lists if head]
            while lists:
                min_node_index = lists.index(min(lists, key=lambda node: node.val))
                new_head = lists[min_node_index] if not new_head else new_head
                if not new_tail:
                    new_tail = lists[min_node_index]
                else:
                    new_tail.next = lists[min_node_index]
                    new_tail = lists[min_node_index]
                if lists[min_node_index].next:
                    lists[min_node_index] = lists[min_node_index].next
                else:
                    lists.pop(min_node_index)
            return new_head
    
  • 多指针代码中,“找出lists中val最小的node”这一步可以改用优先队列实现,从而将这一步的时间复杂度O(k)(k为lists中的头节点数量)降低到O(2logk)(优先队列插入和弹出的时间复杂度都是O(logn),而输入数据中各链表的每个节点都需要经历一次优先队列的插入和弹出),此时算法的总体时间复杂度可以从O(nk) (n为输入数据中的链表长度之和)降低到O(n2logk)

    附:python中优先队列的实现方式:

    import heapq
    
    class PriorityQueue:
        def __init__(self, comparator):
            self._heap = []
            self._comparator = comparator
            self._index = 0  # 当队列中的元素具备相同优先级时,按FIFO顺序弹出
    
        def push(self, data):
            # 当heapq维护的堆中的元素是一个元组时,其优先顺序将由元组中的对象按从前往后的顺序依次比较得到
            # 这里维护的_index可以在_comparator得到相同优先级时,起到优先级补充判断的作用,避免heapq直接对data进行比较
            heap_item = (self._comparator(data), self._index, data)
            heapq.heappush(self._heap, heap_item)
            self._index += 1
    
        def pop(self):
            return heapq.heappop(self._heap)[-1] if self._heap else None
    

    测试用例:

    class Node:
        def __init__(self, val, next_node=None):
            self.val = val
            self.next = next_node
    
    queue = PriorityQueue(lambda node: node.val)
    queue.push(Node(2))
    queue.push(Node(1))
    queue.push(Node(1))
    queue.push(Node(3))
    while True:
        node = queue.pop()
        if node:
            print(node.val)
        else:
            break
    # 期望输出:1 1 2 3
    

142. Linked List Cycle II

  • 哈希比对结点id,时间复杂度:O(n),空间复杂度:O(n)

    class Solution:
        def detectCycle(self, head: Optional[ListNode]) -> Optional[ListNode]:
            nodes = set()
            while head:
                if id(head) in nodes:
                    return head
                else:
                    nodes.add(id(head))
                    head = head.next
            else:
                return None
    
  • 双指针

    • 两个指针同时从head出发,一个指针每次走2步,我们称它为快指针,另一个指针每次走1步,我们称它为慢指针
      • 如果链表无环,那么快指针会在漫游过程中先碰到None,此时返回None即可
      • 如果链表有环,那么快慢指针一定会相遇(因为两指针的间距在每次移动时都会+1,当这个间距等于链表环长度的倍数时,二者就会相遇)
        • 我们假设链表中成环结的数目为\(b\),不在环中的结点数为\(a\)。同时记快指针走过的步数为\(f\),慢指针走过的步数为\(s\)。这样当快慢指针相遇时,快指针走过的步数为:\(f = s+nb\)\(n\)为快指针在链表环中额外漫游的圈数),又由于快慢指针的步数满足:\(f=2s\),因此,我们可以得到以下关系:\(s=nb\)\(f=2nb\),即:当两指针相遇时,快指针在链表环中绕的圈数是慢指针的2倍
        • 那么,如何在关系式中引入\(a\)呢?我们知道,如果一个指针在链表中走过的步数为\(k\),那么当它经过链表环的入口时,其步数关系一定满足:\(k = a+mb\),这里的\(m\)是该指针在链表中绕的圈数。又由于我们前面得到了\(s=nb\),因此只要让慢指针再往前走\(a\)步,它的步数就满足\(k=a+mb\),此时它指向的结点就是链表环的入口
        • 又由于\(a\)是链表不在环中的结点数,因此我们让快指针在两指针第一次相遇后重新指向链表的头节点,这次它每次只走一步,这意味着当它走了\(a\)步后,它会指向链表环的入口,而此时慢指针也在相遇后走了\(a\)步,因此它也在链表环的入口,两指针第二次相遇,我们返回此时二者指向的结点即可
      • 综上,该方法的时间复杂度为O(n),空间复杂度为O(1)

9. Palindrome Number

  • 转成字符串,然后比较。时间复杂度O(n),空间复杂度O(n)

    class Solution:
        def isPalindrome(self, x: int) -> bool:
            x = str(x)
            com_i = int(len(x)/2)
            return x[:com_i] == x[::-1][:com_i]
    

217. Contains Duplicate

  • 哈希

    class Solution:
        def containsDuplicate(self, nums: List[int]) -> bool:
            pool = set()
            for i in nums:
                if i not in pool:
                    pool.add(i)
                else:
                    return True
            else:
                return False
    

53. Maximum Subarray

  • 动态规划

    • 定义状态:如果把状态定义为“前i个元素中的最大子序列之和”,那么后面的递推关系就不好判断了。因此,我们可以把状态定义为“以第i个元素结尾的最大子序列之和”,这样状态既能从小到大覆盖所有可能性,又容易与之前的状态间建立递推关系
      • 最后,我们只需要比较以每个元素结尾的最大子序列之和,取其中的最大值返回即可
    • 递推关系:dp[i] = max(dp[i-1]+nums[i], nums[i])
    • 边界条件:dp[1] = nums[1] (注意编程时用的是从0开始的索引,与此处不同)
    class Solution:
        def maxSubArray(self, nums: List[int]) -> int:
            max_sum = nums[0]  # 用来获取dp_i遍历过程中取到的最大值
            dp_i = nums[0]
            for item in nums[1:]:
                dp_i = max(dp_i+item, item)
                if dp_i > max_sum:
                    max_sum = dp_i
            return max_sum
    

26. Remove Duplicates from Sorted Array

  • 双指针:慢指针用来维护去重序列,快指针用来遍历数组:

    class Solution:
        def removeDuplicates(self, nums: List[int]) -> int:
            i, j = 0, 1
            while j < len(nums):
                if nums[i] == nums[j]:  # ij元素相同,则j向前走
                    j += 1
                else:  # ij元素不同,则i向前走,并把j指向的元素赋值给i,然后j继续向前
                    i += 1
                    nums[i] = nums[j]
                    j += 1
            return i+1  # 去重序列的长度等于索引+1
    

70. Climbing Stairs

  • 动态规划

    class Solution:
        def climbStairs(self, n: int) -> int:
            dp = [1, 2]
            if n <= 2:
                return dp[n-1]
            for i in range(2, n):
                # dp.append(dp[i-1] + dp[i-2])  # 空间上可以进一步优化↓
                dp[1], dp[0] = dp[0] + dp[1], dp[1]
            return dp[-1]
    

146. LRU Cache

  • 注意题意中LRU key的含义是:当前最久没有被使用的key。它是一个时间上的概念,而不是一个使用次数上的概念

  • 我们可以用一个list来记录各key的使用顺序,此时get方法的时间复杂度是O(n),put方法的时间复杂度是O(n),二者中时间复杂度最大的操作都是list.remove(key)

    class LRUCache:
    
        def __init__(self, capacity: int):
            self.cache = dict()
            self.used_time = list()
            self.max_cap = capacity
    
        def get(self, key: int) -> int:
            if key in self.used_time:
                self.used_time.remove(key)
                self.used_time = [key] + self.used_time
            return self.cache.get(key, -1)
    
        def put(self, key: int, value: int) -> None:
            if key in self.cache:
                self.used_time.remove(key)
            elif len(self.cache) >= self.max_cap:
                lru_key = self.used_time[-1]
                self.cache.pop(lru_key)
                self.used_time = self.used_time[:-1]
            self.used_time = [key] + self.used_time
            self.cache[key] = value
    
  • 我们可以引入双向链表来优化get和put的时间复杂度,具体来说:

    • 我们用一个双向链表来存储key的使用顺序以及每个key对应的value值,其中靠近头结点的node中的key是最近被使用的,靠近尾结点的node中的key则是最近未被使用的。我们同时维护一个字典,字典的key就是输入数据的key,而字典的value则是双向链表中的node
    • 在执行get方法时,字典查找元素的时间复杂度是O(1),而更新使用顺序则可以分解为两个操作:1. 在双向链表中删除指定节点,2. 在双向链表的头部插入新节点,这两个操作的时间复杂度都是O(1)。put方法同理。
    • PS:以下代码在部分测试用例上还是会出错,调试双向链表实在太痛苦了,受不了了,先不调了Orz。
    class Node:
        def __init__(self, key, val, next_n=None, prev_n=None):
            self.key = key
            self.val = val
            self.next = next_n
            self.prev = prev_n
    
    
    class LRUCache:
        def __init__(self, capacity: int):
            self.cache = dict()
            self.max_cap = capacity
            self.head = None
            self.tail = None
        
        def _update_head(self, node):
            # 处理链表前,先检查当前结点是否是头结点
            if self.head and self.head.key != node.key:
                # 从双向链表中移除node
                if node.prev:
                    node.prev.next = node.next
                if node.next:
                    node.next.prev = node.prev
            # 检查node是否是尾结点,如果是,且node之前还有其他结点,则要更新尾结点
            if self.tail and self.tail.key == node.key and node.prev is not None:
                self.tail = node.prev
            # 更新头结点
            if self.head:
                node.next = self.head
                self.head.prev = node
            self.head = node
            self.head.prev = None
            
        def _pop_tail(self):
            if len(self.cache) >= self.max_cap:
                lru_key = self.tail.key
                old_tail = self.cache.pop(lru_key)
                self.tail = old_tail.prev
                if self.tail:
                    self.tail.next = None
                del old_tail
    
        def get(self, key: int) -> int:
            if key in self.cache:
                node = self.cache[key]
                self._update_head(node)
                return node.val
            else:
                return -1
    
        def put(self, key: int, value: int) -> None:
            if key in self.cache:  # 更新旧结点
                node = self.cache[key]
                node.val = value
            else:  # 新增结点
                self._pop_tail()
                node = Node(key, value)
                self.cache[key] = node
                if not self.tail:
                    self.tail = node
            self._update_head(node)
    

88. Merge Sorted Array

  • 三指针,从后往前比较。时间复杂度:O(m+n),空间复杂度:O(1)

    class Solution:
        def merge(self, nums1: List[int], m: int, nums2: List[int], n: int) -> None:
            i1, i2, ir = m-1, n-1, len(nums1)-1
            while i1 >=0 and i2 >=0:
                print(nums1[i1], nums2[i2])
                if nums1[i1] > nums2[i2]:
                    nums1[ir] = nums1[i1]
                    i1 -= 1
                else:
                    nums1[ir] = nums2[i2]
                    i2 -= 1
                ir -= 1
            if i2 >= 0:
                nums1[:i2+1] = nums2[:i2+1]
    

141. Linked List Cycle

  • 快慢指针,时间复杂度O(n),空间复杂度O(1):

    class Solution:
        def hasCycle(self, head: Optional[ListNode]) -> bool:
            if head and head.next:
                fast, slow = head, head
                while fast:
                    if fast.next and fast.next.next:
                        fast = fast.next.next
                    else:
                        return False
                    slow = slow.next
                    if id(fast) == id(slow):
                        return True
            else:
                return F
    

231. Power of Two

  • 不断除2,追本溯源:

    class Solution:
        def isPowerOfTwo(self, n: int) -> bool:
            while n >= 1:
                if n == 1:
                    return True
                n /= 2
            else:
                return False
    
  • 也可以从进制位的角度来思考,一个数如果是以k为底的指数,那么它一定是k进制计数法下的幂次数(个/十/百/千):

    class Solution:
        def isPowerOfTwo(self, n: int) -> bool:
            n_binary = bin(n)[2:]
            # 判断n_binary是否是:'1','10','100...'等形式
            if n_binary[0] == '1' and n_binary[1:].replace('0','') == '':
                return True
            else:
                return False
    

54. Spiral Matrix

  • 画个图,找到横纵坐标在移动时的变化规律:

    class Solution:
        def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
            m, n = len(matrix), len(matrix[0])
            res = list()
            num_items = m * n
            hd, vd = 1, 1  # Horizontal/Vertical Direction
            i, j = 0, -1  # 初始指针坐标
            m -= 1
            while len(res) < num_items:
                for _ in range(n):  # 水平方向移动
                    j += hd
                    res.append(matrix[i][j])
                n -= 1  # 下次水平方移动步数-1
                hd *= -1  # 水平方向掉头
                for _ in range(m):  # 垂直方向移动
                    i += vd
                    res.append(matrix[i][j])
                m -= 1  # 下次垂直方向移动步数-1
                vd *= -1  # 垂直方向掉头
            return res
    

124. Binary Tree Maximum Path Sum

  • 后序遍历整颗树,在遍历时,针对每个结点,其可能涉及到的“路径”只有以下6种情况:

    1. 根左右 -> 路径从左子树上来,经过当前节点,再沿右子树下去
    2. 根左 -> 路径从左子树上来,经过当前节点,向上走去
    3. 根右 -> 路径从右子树上来,经过当前节点,向上走去
    4. 左 -> 路径只涉及到左子树,没有经过当前节点
    5. 根 -> 路径只涉及当前节点,没有经过左右子树
    6. 右 -> 路径只涉及到右子树,没有经过当前节点

    以上情况中,只有根左、根右和根这三种情况涉及到向上走的路径,其他三种情况的路径都局限在当前节点之下。考虑到我们是在后序遍历整颗树,这意味着当前节点之下的所有情况我们都遍历过了。所以我们把需要向上走的路径累加的和称为max_sum_up,不需要向上走的路径累加的和称为max_sum_down。在后序遍历时,我们一边用“打擂台”的方式检索max_sum_down,一边累加max_sum_up,供后续遍历的结点使用。这样到最后,我们比较一下根结点的max_sum_up和全局中的max_sum_down,即可找到整棵树中的最大路径和了。

    # Definition for a binary tree node.
    # class TreeNode:
    #     def __init__(self, val=0, left=None, right=None):
    #         self.val = val
    #         self.left = left
    #         self.right = right
    class Solution:
        def scan(self, node):
            """
            scan()方法有两个作用:
              1. 搜索当前结点下,不向上叠加的最大路径和(经过当前节点向下,或不经过根节点)
              2. 返回当前结点下,向上累加的最大路径和(经过或仅由当前节点向上)
            """
            if not node:
                return -1001  # 题设中结点val的最小值是-1000
            left_max_sum = self.scan(node.left)
            right_max_sum = self.scan(node.right)
            self.max_sum_down = max(
                self.max_sum_down, 
                node.val + left_max_sum + right_max_sum,
                left_max_sum, 
                right_max_sum,
            )
            max_sum_up = max(
                node.val,
                node.val + left_max_sum, 
                node.val + right_max_sum,
            )
            return max_sum_up
        
        def maxPathSum(self, root: Optional[TreeNode]) -> int:
            self.max_sum_down = -1001  # 题设中结点val的最小值是-1000
            max_sum_up = self.scan(root)
            return max(max_sum_up, self.max_sum_down)
    
    

16. 3Sum Closest

  • 感觉题目复杂度达到O(\(n^2\))以上时,可以考虑先引入排序。因为排序的时间复杂度是O(nlogn),此时做排序一般是有益无害的。

  • 此题暴力检索的时间复杂度是O(\(n^3\)),我们可先对数组排序,然后遍历排序后的数组。遍历时针对每个nums[i],在数组首尾各设置一个指针,然后计算三者之和:如果和比目标值大,就向前移动尾部指针,反之,就向后移动首部指针。在遍历时,记录每个nums[i]在双指针移动过程中取到的最接近目标值的三数和,遍历完成后,就得到了整个数组最接近目标值的三数和:

    class Solution:
        def threeSumClosest(self, nums: List[int], target: int) -> int:
            nums.sort()
            res = sum(nums[:3])
            for i in range(1,len(nums)-1):
                j, k = 0, len(nums)-1
                while j < i < k:
                    temp_sum = nums[i] + nums[j] + nums[k]
                    if abs(temp_sum - target) < abs(res - target):
                        res = temp_sum
                    if temp_sum < target:
                        j += 1
                    elif temp_sum > target:
                        k -= 1
                    else:
                        return target
            return res
    

43. Multiply Strings

  • 偷懒:

    class Solution:
        def multiply(self, num1: str, num2: str) -> str:
            return str(int(num1)*int(num2))
    
  • 一般解法:竖式乘法

    /**
     *               9   9   9
     *         ×     6   7   8
     *  ----------------------
     *              72  72  72
     *          63  63  63
     *      54  54  54
     *  ----------------------
     *      54 117 189 135  72
     *  ----------------------
     *      54 117 189 142   2
     *  -----------------------
     *      54 117 203   2   2
     *  -----------------------
     *      54 137   3   2   2
     *  -----------------------
     *      67   7   3   2   2
     *  -----------------------
     *   6   7   7   3   2   2
     */
    

20. Valid Parentheses

  • 维护一个栈记录左括号,检查每个右括号是否按顺序与栈中的左括号对齐:

    class Solution:
        def isValid(self, s: str) -> bool:
            stack = list()
            bracket_map = {
                '(':')',
                '{':'}',
                '[':']',
            }
            for c in s:
                if c in bracket_map.keys():
                    stack.append(c)
                else:
                    if stack and bracket_map[stack[-1]] == c:
                        stack.pop()
                    else:
                        return False
            if not stack:
                return True
            else:
                return False
    

33. Search in Rotated Sorted Array

  • 利用数组循环排序的特性,按顺序沿一个方向寻找:

    class Solution:
        def search(self, nums: List[int], target: int) -> int:
            if len(nums) == 1:
                return 0 if target == nums[0] else -1
            i = 0
            monotonicity = set()
            while len(monotonicity) < 2:
                previous = nums[i % len(nums)]
                if target > nums[i % len(nums)]:
                    i += 1
                elif target < nums[i % len(nums)]:
                    i -= 1
                else:
                    return i % len(nums)
                current = nums[i % len(nums)]
                monotonicity.add(current>previous)
            else:
                return -1
    

14. Longest Common Prefix

  • 多指针,同时利用zip方法来加快多指针的比较速度:

    class Solution:
        def longestCommonPrefix(self, strs: List[str]) -> str:
            if not strs:
                return ''
            i = 0
            for c in zip(*strs):
                if len(set(c)) != 1:
                    break
                else:
                    i += 1
            return strs[0][:i] if i != 0 else ''
    

2. Add Two Numbers

  • 链表版的“竖式加法”:

    class Solution:
        def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
            carry = 0
            res_head = None
            res_tail = None
            while l1 or l2 or carry:
                if l1:
                    num1 = l1.val
                    l1 = l1.next
                else:
                    num1 = 0
                if l2:
                    num2 = l2.val
                    l2 = l2.next
                else:
                    num2 = 0
                num_res = num1 + num2 + carry
                if num_res >= 10:
                    num_res -= 10
                    carry = 1
                else:
                    carry = 0
                node = ListNode(num_res)
                if not res_head:
                    res_head = node
                if not res_tail:
                    res_tail = node
                else:
                    res_tail.next = node
                    res_tail = node
            return res_head
    

4. Median of Two Sorted Arrays

  • 一个相对复杂的二分搜索:

    • 先找到中位数在合并数组中的索引,然后再根据这个索引找出中位数在两个分离数组中的位置

      • 我们可以以依次比较两个数组中各元素的大小,不断排除较小的数,直到排除的数据数量达到要求,这样下一组比较元素中较小的数就是目标中位数了

        • 但这样时间复杂度就是(m+n)/2了,不符合题意
      • 要实现log(m+n)的时间复杂度,就需要用二分的方法来寻找两个分离数组中的中位数

        • 在复杂度(m+n)/2的算法中,我们每次只排除了一个数,一共排除(m+n)/2次,进而找到中位数。但实际上,假如我们要找第k小的数,那么我们每次可以排除k/2个数,具体方法如下:

          • 比较两个数组中的第k/2个元素,如果哪个小,就表明该数组的前 k/2 个元素都不是第 k 小的元素,可以排除。依次类推,排除掉k-1个数后,就找到了第k小的数

          更一般的情况:

          假设有A、B两个数组: A[1] ,A[2] ,A[3],A[k/2] ... ,B[1],B[2],B[3],B[k/2] ... ,如果 A[k/2]<B[k/2] ,那么A[1],A[2],A[3],A[k/2]都不可能是第 k 小的数字。

          A 数组中比 A[k/2] 小的数有 k/2-1 个,B 数组中,B[k/2] 比 A[k/2] 小,假设 B[k/2] 前边的数字都比 A[k/2] 小,也只有 k/2-1 个,所以比 A[k/2] 小的数字最多有 k/1-1+k/2-1=k-2个,所以 A[k/2] 最多是第 k-1 小的数。而比 A[k/2] 小的数更不可能是第 k 小的数了,所以可以把它们排除。

          From:windliang

    class Solution:
        @staticmethod
        def compare_and_pop_smaller(nums1, nums2):
            if not nums1:
                return nums1, nums2[1:], nums2[0]
            elif not nums2:
                return nums1[1:], nums2, nums1[0]
            else:
                if nums1[0] < nums2[0]:
                    return nums1[1:], nums2, nums1[0]
                else:
                    return nums1, nums2[1:], nums2[0]
        
        def findMedianSortedArrays(self, nums1: List[int], nums2: List[int]) -> float:
            total_length = len(nums1) + len(nums2)
            if total_length % 2 == 0:
                num_to_exclude = int(total_length/2) - 1
            else:
                num_to_exclude = int(total_length/2)
            while num_to_exclude > 1:
                if not nums1:
                    nums2 = nums2[num_to_exclude:]
                    num_to_exclude = 0
                    break
                elif not nums2:
                    nums1 = nums1[num_to_exclude:]
                    num_to_exclude = 0
                    break
                else:
                    k = min(int(num_to_exclude / 2), len(nums1), len(nums2))
                    if nums1[k-1] < nums2[k-1]:
                        nums1 = nums1[k:]
                    else:
                        nums2 = nums2[k:]
                    num_to_exclude -= k
            if num_to_exclude > 0:
                nums1, nums2, _ = self.compare_and_pop_smaller(nums1, nums2)
            nums1, nums2, median = self.compare_and_pop_smaller(nums1, nums2)
            if total_length % 2 == 0:
                nums1, nums2, median2 = self.compare_and_pop_smaller(nums1, nums2)
                return (median + median2)/2
            else:
                return median
    

61. Rotate List

  • 关键点1:将链表右旋K次,等价于将倒数K个元素置于链表头部(如果K大于链表长度,取余即可)

  • 关键点2:记链表长度为m,将倒数K个元素置于链表头部,等价于将前m-K个元素置于链表后部(因为单链表从前往后遍历比较容易,从后往前遍历比较麻烦)

    class Solution:
        def rotateRight(self, head: Optional[ListNode], k: int) -> Optional[ListNode]:
            if (head is None) or (head.next is None):
                return head
            cur = head
            length = 1
            while cur.next:
                cur = cur.next
                length += 1
            else:
                tail = cur
            k %= length
            k = length - k
            cur = head
            while k > 1:
                cur = cur.next
                k -= 1
            else:
                tail.next = head
                head = cur.next
                cur.next = None
            return head
    

5. Longest Palindromic Substring

  • 暴力搜索:

    class Solution:
        def longestPalindrome(self, s: str) -> str:
            max_len = 0
            res = ''
            for i in range(len(s)):
                for j in range(i+1, len(s)+1):
                    if len(s[i:]) <= max_len:
                        break
                    if s[i:j] == s[i:j][::-1]:
                        if len(s[i:j]) > max_len:
                            res = s[i:j]
                            max_len = len(s[i:j])
            return res
    

15. 3Sum

  • 暴力搜索

    • 执行超时

      class Solution:
          def threeSum(self, nums: List[int]) -> List[List[int]]:
              res = set()
              for i in range(len(nums)-2):
                  for j in range(i+1, len(nums)-1):
                      for k in range(j+1, len(nums)):
                          if nums[i]+nums[j]+nums[k]==0:
                              indexes = tuple({i,j,k})
                              if indexes not in res:
                                  res.add(indexes)
              distinct_results = set()
              final_results = list()
              for i,j,k in list(res):
                  item = [nums[i], nums[j], nums[k]]
                  if tuple(sorted(item)) not in distinct_results:
                      final_results.append(item)
                      distinct_results.add(tuple(sorted(item)))
              return final_results
      
  • 排序+双指针

    class Solution:
        def threeSum(self, nums):
            res = list()
            nums.sort()
            for i in range(len(nums)-2):
                if nums[i] > 0:  # 避免无意义搜索
                    break
                if i > 0 and nums[i] == nums[i-1]:  # 避免重复搜索
                    continue
                j = i + 1
                k = len(nums) - 1
                three_sum = nums[i] + nums[j] + nums[k]
                while True:
                    if three_sum == 0:
                        res.append([nums[i], nums[j], nums[k]])
                        if j < k-2:
                            j += 1
                            while j < k-1 and nums[j] == nums[j-1]:  # 避免重复搜索
                                j += 1
                            k -= 1
                            while j < k-1 and nums[k] == nums[k+1]:  # 避免重复搜索
                                k -= 1
                        else:
                            break
                    elif three_sum < 0:  # sum偏小,则移动左指针
                        j += 1
                        while j < k-1 and nums[j] == nums[j-1]:  # 避免重复搜索
                            j += 1
                    else:  # sum偏大,则移动右指针
                        k -= 1
                        while j < k-1 and nums[k] == nums[k+1]:  # 避免重复搜索
                            k -= 1
                    if j >= k:
                        break
                    three_sum = nums[i] + nums[j] + nums[k]
            return res
    

7. Reverse Integer

转字符串处理,注意处理各种边界值:

class Solution:
    def reverse(self, x: int) -> int:
        x = str(x)
        if x[0] == '-':
            x = x[1:]
            negative = True
        else:
            negative = False
        while len(x) > 1 and x[-1] == '0':
            x = x[:-1]
        res = x[::-1]
        if negative:
            res = '-' + res
        num = int(res)
        if num < -2147483648 or num > 2147483647:
            return 0
        else:
            return num

8. String to Integer (atoi)

考察编程熟练度,把该写的逻辑写出来就好了:

class Solution:
    def myAtoi(self, s: str) -> int:
        negative = None
        while len(s) > 0 and s[0] == ' ':
            s = s[1:]
        if len(s) == 0:
            return 0
        if s[0] == '-':
            negative = True
            s = s[1:]
        elif s[0] == '+':
            negative = False
            s = s[1:]
        if len(s) == 0 or s[0] not in "0123456789":
            return 0
        else:
            nums = ''
            while len(s) > 0 and s[0] in "0123456789":
                nums += s[0]
                s = s[1:]
            if nums:
                res = -int(nums) if negative else int(nums)
                if res < -2147483648:
                    return -2147483648
                elif res > 2147483647:
                    return 2147483647
                else:
                    return res
            else:
                return 0
posted @ 2023-06-22 14:20  云野Winfield  阅读(28)  评论(0编辑  收藏  举报