剑指offer思路整理(个人向)66-

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

1.因为不能用乘法,所以考虑前后遍历,前遍历,第n位就是前n-1位的乘积,后遍历,临时变量存储乘积,然后乘到数组上。

class Solution:
    def constructArr(self, a: List[int]) -> List[int]:
        result = [1]*len(a)
        for i in range(1,len(a)):
            result[i] = result[i-1]*a[i-1]
        temp = 1
        for i in range(len(a)-2, -1, -1):
            temp *= a[i+1]
            result[i] *= temp
        return result

**自我反思:**思路清晰

剑指67.把字符串转换成整数

写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。

说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。

示例 1:

输入: "42"
输出: 42

1.这里涉及一个溢出问题
在这里插入图片描述

遍历字符,判断是否越界,然后每次乘10+,就可以得到结果。最后的int(s) 也可以用 ord(s) - ord(‘0’)来表示,ord返回ASCII数值。

class Solution:
    def strToInt(self, str: str) -> int:
        result, sign, i, length = 0, 1, 0, (len(str))
        int_max = 2**31-1
        int_min = -2**31
        border = 2**31//10
        if str == '': return 0
        while str[i] == ' ':
            i += 1
            if i == length: return 0
        if str[i] == '-': sign = -1
        if str[i] in '+-': i += 1
        for s in str[i:]:
            if not '0'<=s<='9': break
            if result > border or result == border and s>'7':
                return int_max if sign == 1 else int_min
            result = result*10 + int(s)
        return result*sign

**自我反思:**注意数字溢出问题。

剑指68-1. 二叉搜索树的最近公共祖先

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉搜索树: root = [6,2,8,0,4,7,9,null,null,3,5]
在这里插入图片描述
示例 1:

输入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
输出: 6 
解释: 节点 2 和节点 8 的最近公共祖先是 6。

1.写了一个特麻烦的办法,递归遍历所有的结点,每个节点都判断是不是两个结点的祖先,是的话就放进result里,最后return result的最后一个元素。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        self.result = []
        self.p = p
        self.q = q
        def isparent(node1,node2):
            if not node1:
                return False
            if node1 == node2:
                return True
            return isparent(node1.left,node2) or isparent(node1.right,node2)
        
        def recursion(node):
            if not node:
                return
            if isparent(node,self.p) and isparent(node,self.q):
                self.result.append(node)
            if node==self.p or node==self.q:
                return
            recursion(node.left)
            recursion(node.right)
        recursion(root)
        return self.result[-1]

2.这道题的关键点,我的办法没有用上,那就是二叉搜索树。如果当前节点的值大于p和q,就往左子树上查,如果小于p和q,就往右子树上查。当前节点值在p和q之间,则p和q分别在左右子树上,当前节点就是最近公共祖先,return。

class Solution:
    def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
        # 迭代
        # while root:
        #     if root.val > p.val and root.val > q.val:
        #         root = root.left
        #     elif root.val < p.val and root.val < q.val:
        #         root = root.right
        #     else:
        #         break
        # return root

        # 递归
        if root.val > p.val and root.val > q.val:
            return self.lowestCommonAncestor(root.left, p, q)
        if root.val < p.val and root.val < q.val:
            return self.lowestCommonAncestor(root.right, p, q)
        return root

**自我反思:**二叉搜索树的性质要利用好。

剑指68-2. 二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

例如,给定如下二叉树: root = [3,5,1,6,2,0,8,null,null,7,4]
在这里插入图片描述
示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

1.递归解析:
一.终止条件:
1.当越过叶节点,则直接返回 null ;
2.当 root 等于 p,q ,则直接返回 root ;

二.递推工作:
1.开启递归左子节点,返回值记为 left ;
2.开启递归右子节点,返回值记为 right ;

三.返回值: 根据 left 和 right ,可展开为四种情况;
当 left 和 right 同时为空 :说明 root 的左 / 右子树中都不包含 p,q,返回 null ;
当 left 和 right 同时不为空 :说明 p, q 分列在 root 的 异侧 (分别在 左 / 右子树),因此 root 为最近公共祖先,返回 root ;
当 left 为空 ,right 不为空 :p,q 都不在 root 的左子树中,直接返回 right 。具体可分为两种情况:
p,q 其中一个在 root 的 右子树 中,此时 right 指向 p(假设为 p );
p,q 两节点都在 root 的 右子树 中,此时的 right 指向 最近公共祖先节点 ;
当 left 不为空 , right 为空 :与情况 3. 同理;

class Solution:
    def lowestCommonAncestor(self, root: TreeNode, p: TreeNode, q: TreeNode) -> TreeNode:
        if not root or root == p or root == q: return root
        left = self.lowestCommonAncestor(root.left, p, q)
        right = self.lowestCommonAncestor(root.right, p, q)
        if not left: return right
        if not right: return left
        return root

**自我反思:**再理一遍思路

面试题30.包含min函数的栈

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.min();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.min();   --> 返回 -2.
  1. 定义两个stack,第二个stack[-1]存放当前的最小值
class MinStack:
    def __init__(self):
        """
        initialize your data structure here.
        """
        self.stack = []
        self.min_stack = []

    def push(self, x: int) -> None:
        self.stack.append(x)
        if self.min_stack == [] or x<=self.min_stack[-1]:
            self.min_stack.append(x)
    def pop(self) -> None:
        temp = self.stack.pop()
        if self.min_stack[-1] == temp:
            self.min_stack.pop()
    def top(self) -> int:
        return self.stack[-1]
    def min(self) -> int:
        return self.min_stack[-1]

自我反思: 这种O(1)复杂度返回最小值或者最大值的题,一定要熟练。

面试题34. 二叉树中和为某一值的路径

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

示例:

给定如下二叉树,以及目标和 target = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

1.递归,一路记录path,当到达叶子节点,并且和与target相同时,将该条path放入result。

class Solution:
    def pathSum(self, root: TreeNode, target: int) -> List[List[int]]:
        result = []
        path = []
        def recursion(node,tar):
            if not node:return
            path.append(node.val)
            tar += node.val
            if tar == target and not node.left and not node.right:
                result.append(path[:])
            recursion(node.left,tar)
            recursion(node.right,tar)
            path.pop()
        recursion(root,0)
        return result

自我反思: 熟练熟练熟练

面试题59-1. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

示例:

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7] 
解释: 

  滑动窗口的位置                最大值
---------------               -----
[1  3  -1] -3  5  3  6  7       3
 1 [3  -1  -3] 5  3  6  7       3
 1  3 [-1  -3  5] 3  6  7       5
 1  3  -1 [-3  5  3] 6  7       5
 1  3  -1  -3 [5  3  6] 7       6
 1  3  -1  -3  5 [3  6  7]      7
  1. 跟O(1)维护最大值有点像,定义双端队列,利用滑动窗口维护最大值。
    在这里插入图片描述
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        deque = collections.deque()
        res, n = [], len(nums)
        for i, j in zip(range(1 - k, n + 1 - k), range(n)):
            # 删除 deque 中对应的 nums[i-1]
            if i > 0 and deque[0] == nums[i - 1]:
                deque.popleft()
            # 保持 deque 递减
            while deque and deque[-1] < nums[j]:
                deque.pop()
            deque.append(nums[j])
            # 记录窗口最大值
            if i >= 0:
                res.append(deque[0])
        return res

自我反思: 剑指二刷完了,但是很多方面依然感觉掌握的不太好,有的思想还是很难想到,还是需要常回顾。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值