算法(23)-leetcode-剑指offer7


本系列博文为题库刷题笔记,(仅在督促自己刷题)如有不详之处,请参考leetcode官网:https://leetcode-cn.com/problemset/lcof/

59.面试题59-队列的最大值

请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。

若队列为空,pop_front 和 max_value 需要返回 -1

思路1:直接定义一个队列,

import Queue
class MaxQueue(object):

    def __init__(self):
        self.deque=Queue.deque()

    def max_value(self):
        """
        :rtype: int
        """
        return max(self.deque) if self.deque else -1


    def push_back(self, value):
        """
        :type value: int
        :rtype: None
        """
        self.deque.append(value)

    def pop_front(self):
        """
        :rtype: int
        """
        return self.deque.popleft() if self.deque else -1

时间复杂度:插入操作o(1),删除操作o(1),取最大值o(n)

思路2:维护一个辅助双端队列,用于存储最大值,每次插入元素时,将辅助双端队列的尾部小于插入元素的值去除,然后加入该元素。每次弹出队列头部时,比较弹出元素与辅助队列的头是否一致,如果一致,更新双端队列头部( 弹出该头部)

import Queue
class MaxQueue(object):

    def __init__(self):
        self.helper=Queue.deque()   # deque[0] 最大值,deque[-1]最小值
        self.queue=Queue.Queue()

    def max_value(self):
        """
        :rtype: int
        """
        return self.helper[0] if self.helper else -1


    def push_back(self, value):
        """
        :type value: int
        :rtype: None
        """
        while(self.helper and self.helper[-1]<value):
            self.helper.pop()    # 弹出双端队列尾部的元素
        self.helper.append(value) # 在双端队列尾部加入元素
        self.queue.put(value)  # 在队列尾加入元素
        

    def pop_front(self):
        """
        :rtype: int
        """
        if not self.helper:  # 为什么要是helper queue不行
            return -1
        ans=self.queue.get()  # 弹出队列头
        if ans==self.helper[0]:
            self.helper.popleft()  # 双端队列弹出队列头部元素
        return ans
# Your MaxQueue object will be instantiated and called as such:
# obj = MaxQueue()
# param_1 = obj.max_value()
# obj.push_back(value)
# param_3 = obj.pop_front()

时间复杂度:删除o(1),最大o(1),求最大值:均摊时间复杂度o(1),做一个插入操做最多会有n次出队操作,但是每个数字只会出队一次,所以维护n个元素最大值的总出队操作也就是n,即均摊时间复杂度为0(1)。

60.面试题64-求1+2+…+n

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

思路1:求和公式,使用到了乘除法,不可用
1 + 2 + 3 , . . . , n = ( 1 + n ) ∗ n / 2 1+2+3,...,n=(1+n)*n/2 1+2+3,...,n=(1+n)n/2

思路2:迭代要使用循环语句,不可

思路3:递归,递归出口时需要用到判断语句,但是判断语句可以使用逻辑运算符的短路作用,开启递归或结束递归:
n > 1    a n d    s u m N u m s ( n − 1 ) n>1 \ \ and \ \ sumNums(n-1) n>1  and  sumNums(n1)

当n>1 时会开启下一层递归,当n=1,不执行递归,执行之后的操作

class Solution(object):
    def __init__(self):
        self.res=0
    def sumNums(self, n):
        """
        :type n: int
        :rtype: int
        """
        def rec(num):
            num>1 and rec(num-1)
            self.res+=num
        rec(n)
        return self.res

61.面试题65-不用加减乘除做加法

写一个函数,求两个整数之和,要求在函数体内不得使用 “+”、“-”、“*”、“/” 四则运算符号。
思路:不用加减乘除做加法,借助位运算。
经过观察可以发现,两个数字的二进制和,对应位置上的结果运算规律和异或一致,进位位置上的结果和and运算相同。将两数异或 的结果和and 的结果相加,等价于求原来两个数字的和。又出现两数求和操作,重复两数求和求异或。直至进位为0,不存在加法操作。
注意点:python 中的数字以补码的形式存储,python中的数字没有长度,依据题目要求,需要舍去大于32位以上的数字。
补码还原:~(a^x)

class Solution(object):
    def add(self, a, b):
        """
        :type a: int
        :type b: int
        :rtype: int
        """
        x = 0xffffffff
        a, b = a & x, b & x
        while b != 0:
            a, b = (a ^ b), (a & b) << 1 & x
        return a if a <= 0x7fffffff else ~(a ^ x)

62.面试题66-构建乘积数组

给定一个数组 A[0,1,…,n-1],请构建一个数组 B[0,1,…,n-1],其中 B 中的元素 B[i]=A[0]×A[1]×…×A[i-1]×A[i+1]×…×A[n-1]。不能使用除法。
思路:暴力解法43/44时间超出限制

class Solution(object):
    def constructArr(self, a):
        """
        :type a: List[int]
        :rtype: List[int]
        """
        n=len(a)
        b=[1]*n
        for i in range(n):
            for j in range(n):
                if j!=i:
                    b[i]*=a[j]
        return b

思路2:左右乘积数组
l[i]=a[0]*…a[i-1]
r[i]=a[i+1]*…a[n-1]

在这里插入代码片class Solution(object):
    def constructArr(self, a):
        """
        :type a: List[int]
        :rtype: List[int]
        """
        n=len(a)
        b=[1]*n
        left=[1]*n
        right=[1]*n
        for i in range(1,n):
            left[i]=left[i-1]*a[i-1]
        for i in range(n-2,-1,-1):
            right[i]=right[i+1]*a[i+1]
        for i in range(n):
            b[i]=left[i]*right[i]
        return b

63.面试题68-1二叉树搜索树的最近公共祖先

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

思路:一个节点为最近祖先:pq 在该节点的两棵子树/p=root 或者q=root。

首先判断 p 和 q 是否相等,若相等,则直接返回 p 或 q 中的任意一个,程序结束
若不相等,则判断 p 和 q 在向左还是向右的问题上,是否达成了一致
如果 p 和 q 都小于root, 哥俩一致认为向左👈,则 root = root.left
如果 p 和 q 都大于root, 哥俩一致认为向右👉,则 root = root.right
如果 p 和 q 哥俩对下一步的路线出现了分歧,说明 p 和 q 在当前的节点上就要分道扬镳了,当前的 root 是哥俩临别前一起走的最后一站
返回当前 root
迭代:

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

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if root==None:
            return root
        if p.val==q.val:
            return p
        node=root
        while(node):
            if node.val>p.val and node.val>q.val:
                node=node.left
            elif node.val<p.val and node.val<q.val:
                node=node.right
            else:
                return node

递归:

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

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if root.val<p.val and root.val<q.val:
            return self.lowestCommonAncestor(root.right,p,q)
        if root.val>p.val and root.val>q.val:
            return self.lowestCommonAncestor(root.left,p,q)
        return root

64.面试题68-2二叉树的最近公共祖先

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

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

思路1:记录父亲节点
先遍历一遍树,记录每个点的父亲节点。然后去找从p开始去找其父亲节点路径:vis_has,接着从q开始去找其父亲,在vis_hash 中第一次出现的就为最近公共祖先。

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        def dfs(node):
            if node==None:
                return
            if node.left:
                fa_hash[node.left]=node
            if node.right:
                fa_hash[node.right]=node
            dfs(node.left)
            dfs(node.right)
        fa_hash={root:None}
        vis_hash={}
        dfs(root)        
        while (p!=None):
            # print(p)
            vis_hash[p]=True     # 要记得把自己放进去
            p=fa_hash[p]
        while(q!=None):
            if vis_hash.get(q):  # 记得自己也要验证
                return q
            q=fa_hash[q]

思路2:递归 从叶子节点往上递归,找到一个节点,其左右子树中包含p或者q,该节点为最近的父亲节点。

class Solution(object):
    def __init__(self):
        self.res=None
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        def dfs(node,p,q):
            if node==None:
                return False
            left=dfs(node.left,p,q)
            right=dfs(node.right,p,q)
            mid=(p==node or q==node)
            if mid+left+right==2:
                self.res=node
            return mid or left or right
        dfs(root,p,q)
        return self.res

65.面试题67-把字符串转换成数字-自动机

字符串处理相关的题目往往涉及复杂的流程以及条件情况,如果直接上手写程序,一不小心就会写成及其臃肿的代码。

因此,为了有条理的分析每一个输入字符的处理方法。可以使用自动机方法

程序在每一个时刻有一个状态s,每次总序列中输入一个字符串c ,并根据字符串c 转到下一个状态s’ .我们要做的就是简历一个覆盖所有情况的从s 与c 的映射到s‘ 的表格。

用表格来表示自动机:
表格的第一列为状态,第一行为输入,表格体内部为下一个状态。

在这里插入图片描述
编程时,将状态转换表转换进代码里。

题目要求,输出处转换后的数字。因此在s’=in_number 时,更新我们的输入数字,即可得到最终的答案。

INT_MAX = 2 ** 31 - 1
INT_MIN = -2 ** 31

class Automaton:
    def __init__(self):
        self.state = 'start'
        self.sign = 1
        self.ans = 0
        self.table = {
            'start': ['start', 'signed', 'in_number', 'end'],
            'signed': ['end', 'end', 'in_number', 'end'],
            'in_number': ['end', 'end', 'in_number', 'end'],
            'end': ['end', 'end', 'end', 'end'],
        }
        
    def get_col(self, c):
        if c.isspace():
            return 0
        if c == '+' or c == '-':
            return 1
        if c.isdigit():
            return 2
        return 3

    def get(self, c):
        self.state = self.table[self.state][self.get_col(c)]
        if self.state == 'in_number':
            self.ans = self.ans * 10 + int(c)
            self.ans = min(self.ans, INT_MAX) if self.sign == 1 else min(self.ans, -INT_MIN)
        elif self.state == 'signed':
            self.sign = 1 if c == '+' else -1

class Solution:
    def strToInt(self, str):
        automaton = Automaton()
        for c in str:
            automaton.get(c)
        return automaton.sign * automaton.ans

66.面试题20-表示数值的字符串-自动机

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100"、“5e2”、"-123"、“3.1416”、“0123"都表示数值,但"12e”、“1a3.14”、“1.2.3”、“±5”、"-1E-16"及"12e+5.4"都不是。

自动机解题:
1.画状态转移图
2.列状态转移表
3.确定输入字符串对应的列
4.迭代所有的输入,进行状态转移,如果遇到不合理状态就返回

class Solution(object):
    def isNumber(self, s):
        """
        :type s: str
        :rtype: bool
        """
        if not s:
            return False
        transTable=[
            [1,2,7,-1,-1,0],
            [-1,2,7,-1,-1,-1],
            [-1,2,3,4,-1,9],
            [-1,3,-1,4,-1,9],
            [6,5,-1,-1,-1,-1],
            [-1,5,-1,-1,-1,9],
            [-1,5,-1,-1,-1,-1],
            [-1,8,-1,-1,-1,-1],
            [-1,8,-1,4,-1,9],
            [-1,-1,-1,-1,-1,9]
        ]
        cols={
            "sign":0,
            "number":1,
            ".":2,
            "exp":3,
            "other":4,
            "blank":5
        }
        def get_col(c):
            if c.isdigit():
                return "number"
            elif c in {"+","-"}:
                return "sign"
            elif c==".":
                return "."
            elif c in {"e","E"}:
                return "exp"
            elif c==" ":
                return "blank"
            else:
                return "other"
        legal_state={2,3,5,8,9}
        state=0
        for c in s:
            state=transTable[state][cols[get_col(c)]]
            if state==-1:
                return False
        return True if state in legal_state else False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值