LeetCode-剑指Offer刷题记录

这个博客记录leetcode-剑指offer中遇到问题的题目

剑指 Offer 04. 二维数组中的查找

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false。
限制:
0 <= n <= 1000
0 <= m <= 1000

class Solution:
    def findNumberIn2DArray(self, matrix: List[List[int]], target: int) -> bool:
        if matrix == []:
            return False
        row = 0
        col = len(matrix[0]) - 1
        n = len(matrix)
        m = len(matrix[0])
        while -1 < row < n and -1 < col < m:
            if matrix[row][col] == target:
                return True
            elif matrix[row][col] > target:
                col -= 1
            else:
                row += 1
        return False

从右上角开始,左边的比它小,下边的比它大。

剑指 Offer 07. 重建二叉树

输入某二叉树的前序遍历和中序遍历的结果,请重建该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。
例如,给出
前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:
3
/
9 20
/
15 7

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
        if inorder == []:
            return None
        elif len(inorder) == 1: 
            return TreeNode(preorder[0])
        elif len(inorder) == 2:
            root = TreeNode(preorder[0])
            if preorder == inorder:
                root.right = TreeNode(preorder[-1])
            else:
                root.left = TreeNode(preorder[-1])
            return root
        else:
            root = TreeNode(preorder[0])
            ind = inorder.index(preorder[0])
            left_in = inorder[:ind]
            right_in = inorder[ind+1:]
            left_pre = preorder[1:1+len(left_in)]
            right_pre = preorder[-len(right_in):]
            root.left = self.buildTree(left_pre,left_in) if left_in != [] else None
            root.right = self.buildTree(right_pre,right_in) if right_in != [] else None
            return root

在前序遍历中寻找左右子树对应节点时,可以利用之前左右子树中序遍历节点长度生成。
leetoce解题方案

剑指 Offer 13. 机器人的运动范围

地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?

示例 1:
输入:m = 2, n = 3, k = 1
输出:3
示例 2:
输入:m = 3, n = 1, k = 0
输出:1
提示:
1 <= n,m <= 100
0 <= k <= 20

class Solution:
    def getnum(self,x):
        tmp = 0 
        while x > 0:
            tmp += (x%10)
            x = x // 10
        return tmp
    def movingCount(self, m: int, n: int, k: int) -> int:
        list_res = set([(0,0)])
        for i in range(m):
            for j in range(n):
                if ((i - 1, j) in list_res or (i, j - 1) in list_res) and self.getnum(i) + self.getnum(j) <= k:
                    list_res.add((i, j))
        return len(list_res)

不要把简单的问题复杂化,由[0,0]开始,依次计算就可以,不要过度追求递归之类的。

剑指 Offer 16. 数值的整数次方

实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。

示例 1:
输入: 2.00000, 10
输出: 1024.00000
示例 2:
输入: 2.10000, 3
输出: 9.26100
示例 3:
输入: 2.00000, -2
输出: 0.25000
解释: 2-2 = 1/22 = 1/4 = 0.25

class Solution:
    def myPow(self, x: float, n: int) -> float:
        if n == 0:
            return 1
        elif x == 0:
            return 0
        res = 1
        n_abs = abs(n)
        while n_abs > 0:
            if n_abs % 2 == 1:
                res = res*x
            x = x*x
            n_abs = n_abs//2
        if n > 0 :
            return res
        else:
            return 1/res

算法分为两个知识点:
1.十进制和二进制之间的转换:
n = 1b1 + 2b2 + 4*b3 + … +2^(m-1)*b_m
2.x的n次方展开
xn = x1b1+2b2+4b3+…+2**(m-1)bm
= x1*b1*x2*b2x4*b3…*x2**(m-1)*bm

剑指 Offer 12. 矩阵中的路径(重点题目)

请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
[[“a”,“b”,“c”,“e”],
[“s”,“f”,“c”,“s”],
[“a”,“d”,“e”,“e”]]
但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

示例 1:
输入:board = [[“A”,“B”,“C”,“E”],[“S”,“F”,“C”,“S”],[“A”,“D”,“E”,“E”]], word = “ABCCED”
输出:true
示例 2:
输入:board = [[“a”,“b”],[“c”,“d”]], word = “abcd”
输出:false

class Solution:
    def exist(self, board: List[List[str]], word: str) -> bool:
        def dfs(i, j, k):
            if not 0 <= i < len(board) or not 0 <= j < len(board[0]) or board[i][j] != word[k]:
                return False
            if k == len(word) - 1: 
                return True
            board[i][j] = ''
            res = dfs(i + 1, j, k + 1) or dfs(i - 1, j, k + 1) or dfs(i, j + 1, k + 1) or dfs(i, j - 1, k + 1)
            board[i][j] = word[k]
            return res

        for i in range(len(board)):
            for j in range(len(board[0])):
                # 起点
                if dfs(i, j, 0): 
                    return True
        return False

递归
今天看的两个题都是这种里面嵌套了一个函数的实现方法

剑指 Offer 33. 二叉搜索树的后序遍历序列

输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。

参考以下这颗二叉搜索树:
5
/
2 6
/
1 3
示例 1:
输入: [1,6,3,2,5]
输出: false
示例 2:
输入: [1,3,2,6,5]
输出: true

class Solution:
    def verifyPostorder(self, postorder: [int]) -> bool:
        # 二叉搜索树
        # 后序遍历
        if postorder == []:
            return True
        root = postorder[-1]
        left = postorder[:-1]
        right = []
        for i in range(0,len(postorder)-1):
            if postorder[i] > root:
                left = postorder[:i]
                right = postorder[i:-1]
                break
        # 左边肯定是符合要求的,只检查右子树即可
        for i in right:
            if i < root:
                return False
        return self.verifyPostorder(left) and self.verifyPostorder(right)

这道题目里面有两个知识点,1.后序遍历;2.二叉搜索树左节点<根节点<右节点

剑指 Offer 34. 二叉树中和为某一值的路径

输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。

示例:
给定如下二叉树,以及目标和 sum = 22,

          5
         / \
        4   8
       /   / \
      11  13  4
     /  \    / \
    7    2  5   1

返回:
[
[5,4,11,2],
[5,8,4,5]
]

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None
class Solution:
    def pathSum(self, root: TreeNode, sum: int) -> List[List[int]]:
        if root == None:
            return []
        if root.left == None and root.right == None:
            res = [[root.val]] if root.val == sum else []
            return res
        else:
            left_res = self.pathSum(root.left,sum-root.val)
            right_res = self.pathSum(root.right,sum-root.val)
            res = []
            for r in left_res+right_res:
                r = [root.val] + r
                res.append(r)
            return res        

递归最原始,最朴素的写法和思想

剑指 Offer 31. 栈的压入、弹出序列

输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。

class Solution:
    def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
        # 压入 弹出 先进后出
        storehouse = []
        while pushed != [] and popped != []:
            # 入栈
            storehouse.append(pushed[0])
            pushed = pushed[1:]
            while storehouse!= [] and storehouse[-1] == popped[0]:
                # 出栈
                storehouse = storehouse[:-1]
                popped = popped[1:]
        if storehouse == [] and pushed == [] and popped == []:
            return True
        else:
            return False

剑指 Offer 45. 把数组排成最小的数

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 1:
输入: [10,2]
输出: “102”
示例 2:
输入: [3,30,34,5,9]
输出: “3033459”
提示:
0 < nums.length <= 100

class Solution:
    def minNumber(self, nums: List[int]) -> str:
        def join_list(list_nums):
            str_nums = [str(n) for n in list_nums]
            return int(''.join(str_nums))
        # min
        for x in range(int(0.5*len(nums))):
            i = x
            j = len(nums)-1-x
            while i < len(nums)-1-x:
                tmp = nums[:]
                tmp[i],tmp[i+1] = tmp[i+1],tmp[i]
                if join_list(tmp) < join_list(nums):
                    nums[i],nums[i+1] = nums[i+1],nums[i]
                tmp = nums[:]
                tmp[j-1],tmp[j] = tmp[j],tmp[j-1]
                if join_list(tmp) < join_list(nums):
                    nums[j-1],nums[j] = nums[j],nums[j-1]
                """
                if nums[i] > nums[i+1]:
                    nums[i],nums[i+1] = nums[i+1],nums[i]
                if nums[j-1] > nums[j]:
                    nums[j-1],nums[j] = nums[j],nums[j-1]
                """
                i += 1
                j -= 1
        nums_str = [str(n) for n in nums] 
        return ''.join(nums_str)

其实就是一个排序算法,只不过目标不一样!!!

剑指 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

class Solution:
    def singleNumbers(self, nums: List[int]) -> List[int]:
        # ^异或符号, &与符号, << 左移一位 
        ret = functools.reduce(lambda x, y: x ^ y, nums)
        # 所有数据的异或结果,相同数据的异或结果是0
        div = 1
        # 一开始应该是只有一位
        while div & ret == 0:
            div <<= 1
            # 左移一位
        # 找到不为0的第一位
        a, b = 0, 0
        for n in nums:
            # 分组,只要是异或为1,必定是不一样的值
            if n & div:
                # 不为0的第一位对于数值是1
                a ^= n
            else:
                # 不为0的第一位对于数值是0
                b ^= n
        return [a, b]

剑指 Offer 64. 求1+2+…+n

求 1+2+…+n ,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。

示例 1:
输入: n = 3
输出: 6
示例 2:
输入: n = 9
输出: 45
限制:
1 <= n <= 10000

class Solution:
    def __init__(self):
        self.res = 0
    def sumNums(self, n: int) -> int:
        (n>1) and self.sumNums(n-1)
        self.res += n
        return self.res

常见的逻辑运算符有三种,即 “与 &&&& ”,“或 ||∣∣ ”,“非 !! ” ;而其有重要的短路效应,如下所示:

if(A && B)  
// 若 A 为 false ,则 B 的判断不会执行(即短路),直接判定 A && B 为 false
if(A || B) 
// 若 A 为 true ,则 B 的判断不会执行(即短路),直接判定 A || B 为 true

本题需要实现 “当 n = 1时终止递归” 的需求,可通过短路效应实现。

n > 1 && sumNums(n - 1)
 // 当 n = 1 时 n > 1 不成立 ,此时 “短路” ,终止后续递归

剑指 Offer 60. n个骰子的点数

把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。
你需要用一个浮点数数组返回答案,其中第 i 个元素代表这 n 个骰子所能掷出的点数集合中第 i 小的那个的概率。

示例 1:
输入: 1
输出: [0.16667,0.16667,0.16667,0.16667,0.16667,0.16667]
示例 2:
输入: 2
输出: [0.02778,0.05556,0.08333,0.11111,0.13889,0.16667,0.13889,0.11111,0.08333,0.05556,0.02778]

class Solution:
    def dicesProbability(self, n: int) -> List[float]:
        # n个骰子的话,最大的数是n,最小的数是6*nn
        dp = [[0 for i in range(6*n+1)]for j in range(1+n)]
        # dp这里,行数就是骰子的个数,列数就是n个骰子时,最大数,这里这样写的好处是和原始数据对上了
        dp[1][1:7] = [1]*6
        for i in range(2,n+1):
            for j in range(i,6*i+1):
            	# n个骰子时的数字变化范围
                for k in range(1,7):
                	# 骰子有六个面
                    dp[i][j] += dp[i-1][j-k] 
        x = dp[n][n:6*n+1]
        y = sum(x)
        x = [xx/y for xx in x]
        return x

剑指 Offer 66. 构建乘积数组

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B[i] 的值是数组 A 中除了下标 i 以外的元素的积, 即 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。

示例:
输入: [1,2,3,4,5]
输出: [120,60,40,30,24]
提示:
所有元素乘积之和不会溢出 32 位整数
a.length <= 100000

class Solution:
    def constructArr(self, a: List[int]) -> List[int]:
        """
        if len(a) <= 1:
            return a 
        elif len(a) == 2:
            return [a[1],a[0]]
        else:
            tmp = self.constructArr(a[:-1])
            last = 1
            for t in range(len(tmp)):
                tmp[t] = tmp[t]*a[-1]
                last *= a[t]
            return tmp + [last]
        """
        res = [1 for i in range(len(a))]
        for i in range(1,len(a)):
            res[i] = a[i-1]*res[i-1]
        res1 = [1 for i in range(len(a))]
        for i in range(len(a)-2,-1,-1):
            res1[i] = a[i+1]*res1[i+1]
            res[i] = res1[i]*res[i]
        return res

第一种方法会超时,第二种方法是,从上到下计算一次,从右到左再计算一次。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值