[剑指Offer]55~57

[剑指Offer]55~57

学习使用工具

剑指Offer http://itmyhome.com/sword-means-offer/sword-means-offer.pdf

LeetCode的剑指Offer题库 https://leetcode.cn/problemset/all/

剑指 Offer 55 - II. 平衡二叉树

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

示例 1:

给定二叉树 [3,9,20,null,null,15,7]

    3
   / \
  9  20
    /  \
   15   7

返回 true
示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4

返回 false

限制:

  • 0 <= 树的结点个数 <= 10000

解法:

先写一个用于计算当前节点深度的函数辅助递归。主函数递归,如果当前节点为空,返回True;如果当前节点深度满足平衡,继续对左右子树进行递归判定;如果不满足,返回False。

class Solution:
    def maxDepth(self, root: TreeNode) -> int:
        if not root:
            return 0
        deep = 1
        deep += max(self.maxDepth(root.left), self.maxDepth(root.right))
        return deep

    def isBalanced(self, root: TreeNode) -> bool:
        if not root:
            return True

        if abs(self.maxDepth(root.left) - self.maxDepth(root.right)) < 2:
            return self.isBalanced(root.left) and self.isBalanced(root.right)
        else:
            return False

剑指 Offer 56 - I. 数组中数字出现的次数

一个整型数组 nums 里除两个数字之外,其他数字都出现了两次。请写程序找出这两个只出现一次的数字。要求时间复杂度是O(n),空间复杂度是O(1)。

示例 1:

输入:nums = [4,1,4,6]
输出:[1,6] 或 [6,1]

示例 2:

输入:nums = [1,2,10,4,1,4,3,3]
输出:[2,10] 或 [10,2]

限制:

  • 2 <= nums.length <= 10000

解法:

想了半天怎么把时间复杂度压进O(n),然后又想了半天怎么把空间复杂度压进O(1),一看答案竟然是用异或……

考虑异或操作的性质:对于两个操作数的每一位,相同结果为 0,不同结果为 1。那么在计算过程中,成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字。

那么这一方法如何扩展到找出两个出现一次的数字呢?如果我们可以把所有数字分成两组,使得:

  • 两个只出现一次的数字在不同的组中;
  • 相同的数字会被分到相同的组中。

那么对两个组分别进行异或操作,即可得到答案的两个数字。这是解决这个问题的关键。

分组方法:

记这两个只出现了一次的数字为a和b,那么所有数字异或的结果就等于a和b异或的结果,记为x。将x写成二进制形式\(x_kx_{k-1}……x_2x_1x_0\),其中\(x_i∈{0,1}\)。我们任选一个不为0的\(x_i\)按照第i位给原来的序列分组,如果该位为0就分到第一组,否则就分到第二组。

def singleNumbers(self, nums: List[int]) -> List[int]:
        ret = functools.reduce(lambda x, y: x ^ y, nums)
        div = 1
        while div & ret == 0:
            div <<= 1
        a, b = 0, 0
        for n in nums:
            if n & div:
                a ^= n
            else:
                b ^= n
        return [a, b]

剑指 Offer 56 - II. 数组中数字出现的次数 II

在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。

示例 1:

输入:nums = [3,4,3,3]
输出:4

示例 2:

输入:nums = [9,1,7,9,7,9,7]
输出:1

限制:

  • 1 <= nums.length <= 10000
  • 1 <= nums[i] < 2^31

解法:

不考虑空间复杂度当然是乱做,但是考虑时间复杂度O(n)和空间复杂度O(1)的话依然要使用位运算。考虑数字的二进制形式,对于出现三次的数字,各 二进制位 出现的次数都是 333 的倍数。 因此,统计所有数字的各二进制位中 1 的出现次数,并对 3 求余,结果则为只出现一次的数字。

Picture1.png

使用与运算 ,可获取二进制数字 num 的最右一位,配合无符号右移操作 ,可获取 num所有位置的值。建立一个长度为 32 的数组 counts,通过以上方法可记录所有数字的各二进制位的1的出现次数。将 counts各元素对 3 求余,则结果为 “只出现一次的数字” 的各二进制位。利用 左移操作和或运算,可将 counts 数组中各二进位的值恢复,复原后返回即可。

def singleNumber(self, nums: List[int]) -> int:
        counts = [0] * 32
        for num in nums:
            for j in range(32):
                counts[j] += num & 1
                num >>= 1
        res, m = 0, 3
        for i in range(32):
            res <<= 1
            res |= counts[31 - i] % m
        return res if counts[31] % m == 0 else ~(res ^ 0xffffffff)

剑指 Offer 57. 和为s的两个数字

输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,则输出任意一对即可。

示例 1:

输入:nums = [2,7,11,15], target = 9
输出:[2,7] 或者 [7,2]

示例 2:

输入:nums = [10,26,30,31,47,60], target = 40
输出:[10,30] 或者 [30,10]

限制:

  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^6

解法:

梦开始的地方。双指针各自从头尾开始遍历即可。

def twoSum(self, nums: List[int], target: int) -> List[int]:
        i, j = 0, len(nums) - 1
        ans = nums[i] + nums[j]
        while not ans == target:
            if ans > target:
                j -= 1
            else:
                i += 1
            ans = nums[i] + nums[j]

        return [nums[i], nums[j]]
posted @ 2023-03-06 14:47  无机呱子  阅读(8)  评论(0编辑  收藏  举报