算法(18)-leetcode-剑指offer2


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

编程语言为python

11.面试题13-机器人的运动范围-广度优先搜索

地上有一个m行n列的方格,从坐标[0,0]到坐标[m-1,n-1].一个机器人从坐标[0,0]开始移动,它每一次可以向左右上下移动一格,机器人不能进入行列坐标位数之和大于k的格子(k=18,机器人能够进入方格[35,37],因为3+5+3+7=18)。请问机器人能够达到多少个格子。–需要从左上角开始的连续的区域。当一个方向不可以走时,设置一个flag,如果当flag==3时,停止向下遍历。

广度优先搜索:将不能走的格子堪称障碍物,这是一道搜索题,可以使用深度优先或广度优先来解决。本题隐含条件要求从左上角到右下角的联通集。(k=1,[10,0]是满足可走条件,但是机器人首先到不了[9,0].)广度优先遍历先将(0,0)放入队列,判读是否可达,可达就将它右边和下边的格子(下一步备选可达点)坐标[0,1] 和[1,0]放入到队列中,不断判断队列备选可达的点是否可达。直至队列为空。(如果一个点不可达,他的右边下边就不能从这个点出发达到,但可能可以从其他点出发得到)

class Solution(object):
    def movingCount(self, m, n, k):
        """
        :type m: int
        :type n: int
        :type k: int
        :rtype: int
        """
        def is_available(i,j):
            sum_ij=0
            # 取出一个数字每位上的数
            while(i>0):
                mod=i%10
                sum_ij+=mod
                i=(i-mod)/10
            while(j):
                mod=j%10
                sum_ij+=mod
                j=(j-mod)/10
            if sum_ij<=k:
                return True
            else:
                return False
        res=0
        que=[(0,0)]
        visted_dit={}
        while(que):
            i,j=que.pop(0)
            if 0<=i<m and 0<=j<n and is_available(i,j):
                if not visted_dit.get((i,j)): # 一些已经走过的点需要标记,避免重复遍历
                    visted_dit[(i,j)]=True
                    res+=1
                    que.append((i+1,j))
                    que.append((i,j+1))   
        return res

12.面试题14-1-剪绳子

给你一根长度为n的绳子,请把绳子坚称整数长度的m段(n,m都是整数),每段绳子的长度记为k[0],k[1],…,k[m-1]。请问k[0]k[1]…k[m-1]可能的最大乘积是多少。(2 <= n <= 58)
完全背包问题:绳子的长度是背包容量,每次剪掉一段,让乘积最大嘛。
dp[i]表示长为i的绳子至少切了一次的最大乘积,则dp[i]的更新表达式为:dp[i]=max(dp[i],dp[j]*(i-j),j*(i-j))
dp[j]与j的区别:dp[j]至少剪过一次,j表示一次没剪。

class Solution(object):
    def cuttingRope(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp=[0]*(n+1)
        dp[1]=1
        for i in range(2,n+1): 
            for j in range(1,i):
                # print("i:",i,"j:",j,"i-j:",i-j,"dp[j]:",dp[j],dp[j]*(i-j),j*(i-j))
                dp[i]=max(dp[i],dp[j]*(i-j),j*(i-j))
        print dp
        return dp[-1]

13.面试题14-2-剪绳子2

本题与上一题题目一致只是输入数据n的范围变大了,可能回造成大数越界的问题。之前的dp可能回造成超时或出错。
(2 <= n <= 1000)
和上一题写的一毛一样,勉强过了,击败24.73%用户。

class Solution(object):
    def cuttingRope(self, n):
        """
        :type n: int
        :rtype: int
        """
        dp=[0]*(n+1)
        dp[1]=1
        for i in range(2,n+1): 
            for j in range(1,i):
                # print("i:",i,"j:",j,"i-j:",i-j,"dp[j]:",dp[j],dp[j]*(i-j),j*(i-j))
                dp[i]=max(dp[i],dp[j]*(i-j),j*(i-j))
        return dp[-1]%(10**9+7)

基于数学推到的方案
算术几何均值不等式:
n 1 + n 2 + . . . + n a a > = n 1 n 2 . . . n a a \frac{n_1+n_2+...+n_a}{a}>=\sqrt[a]{n_1n_2...n_a} an1+n2+...+na>=an1n2...na
a个数字的算术平均值,大于等于,这a个数的几何平均值。
a=2时:
n 1 + n 2 2 > = n 1 n 2 2 \frac{n_1+n_2}{2}>=\sqrt[2]{n_1n_2} 2n1+n2>=2n1n2
右边取得最大值的当且仅当 n 1 = n 2 = , . . . , n a n_1=n_2=,...,n_a n1=n2=,...,na
即可得到推论1:将绳子 以相等的长度 等分多段,得到的乘积最大。

将绳子按照长度x分成a段, n = a x n=ax n=ax,乘积最大为 x a x^a xa(x和a都是变量).因为 a = n x a=\frac{n}{x} a=xn,即最大值为:
x a = x n x = [ x 1 x ] n x^a=x^{\frac{n}{x}}=[x^{\frac{1}{x}}]^n xa=xxn=[xx1]n
问题转换为求上式子的最大值,对上式求导让其为0,求得极大值点为e。因为切分长度必须为整数,比较x=2,x=3的大小。x=3时 x 1 x x^{\frac{1}{x}} xx1更大。
所以推论2为:将绳子尽量分为长度为3的多个等分段时,乘积最大。

综上:
推论1说的是需要将绳子绳子分为k段,这k段的长度是等分时这个k段的乘积最大。
推论2说的是每段的长度最好是3,对应的等分方式乘积最大。

n的取整依据能不能被3整除,可以分为( n = 3 a + b n=3a+b n=3a+b)-确定的方法有贪心选择的性质:
b=0:最大乘积 3 a 3^a 3a
b=1:前面切出的a段3中拿出一段来,和1凑成长度为4子段,切成22=4>13
b=2:最大值为 3 a ∗ 2 3^a*2 3a2

class Solution(object):
    def cuttingRope(self, n):
        """
        :type n: int
        :rtype: int
        """
        if n<=3:
            return n-1       # 初始条件n=2 res=1; n=3,res = 2
        a,b=n//3,n%3
        res=0
        if b==0:
            res=3**a
        elif b==1:
            res=3**(a-1)*4
        else:
            res=3**(a)*2
        return res%(10**9+7)

14.面试题16-二进制中1的个数-布莱恩·克尼根

请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
(461.汉明距离:两个数字二进制表示形式的对应位置不同的数目。–将两个数据取异或,然后统计异或结果的个数)

布莱恩·克尼根算法:利用特定的比特位和算术运算符移除最右边的1。
在这里插入图片描述

class Solution(object):
    def hammingWeight(self, n):
        """
        :type n: int
        :rtype: int
        """
        res=0
        while(n):
            res+=1
            n=n &(n-1)
        return res

15.面试题16-数值的整数次方-快速幂解析法

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

class Solution(object):
    def myPow(self, x, n):
        """
        :type x: float
        :type n: int
        :rtype: float
        """
        res=1
        if  n<0:
            flag=-1
            n*=-1
        else:
            flag=1
        for i in range(n):
            res*=x
        return res if flag==1 else 1/res

291.304 最后执行输入:0.00001,2147483647 ,报错memotyError

参考思路:
快速幂解析法(二进制):略
快速幂解析法(二分): x n x^n xn可以转换为以下两种情况:
x n = ( x 2 ) n / / 2 x^n=(x^2)^{n//2} xn=(x2)n//2 ,n为偶数
x n = x ( x 2 ) n / / 2 x^n=x(x^2)^{n//2} xn=x(x2)n//2,n为奇数
可以通过以上操作,将幂从n降到n//2,不断重复,直至幂将为0.
在这里插入图片描述

class Solution(object):
    def myPow(self, x, n):
        """
        :type x: float
        :type n: int
        :rtype: float
        """
        if x==0:
            return 0
        if n<0:
            x,n=1/x,-n
        res=1
        while(n):
            if n%2==1:    # n&1
                res*=x
            x*=x
            n=n//2   # n>>=1
        return res

16.面试题17-打印从1到最大的n位数

输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。

也不是很明白考察的什么!!!

class Solution(object):
    def printNumbers(self, n):
        """
        :type n: int
        :rtype: List[int]
        """
        l=10**n
        res=[]
        for i in range(1,l):
            res.append(i)
        return res

17.面试题18-删除链表的节点

给定单向链表的头指针和一个要删除的节点的,定义一个函数删除该节点。返回删除后的链表的头节点。
说明:题目保证链表中节点的值互不相同

删除链表节点注意dummy节点技巧,避免需要删除的节点是头节点。

class Solution(object):
    def deleteNode(self, head, val):
        """
        :type head: ListNode
        :type val: int
        :rtype: ListNode
        """
        dummy=ListNode(0)
        dummy.next=head
        pre_node=dummy
        curr_node =head
        while(curr_node):
            next_node=curr_node.next
            if curr_node.val==val:
                pre_node.next=next_node
                return dummy.next
            pre_node=curr_node
            curr_node=next_node
        return dummy.next

18.面试题19-正则匹配-回溯

请实现一个函数用来匹配包含’. ‘和’‘的正则表达式。模式中的字符’.‘表示任意一个字符,而’'表示它前面的字符可以出现任意次(含0次)。在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"abaca"匹配,但与"aa.a"和"ab*a"均不匹配。

回溯方法:如果没有星号,问题就会很简单–只需要从左往右检查匹配串s是否能够匹配模式串p中的每一字符。
当模式串中有星号时,我们需要检查匹配串s中的不同后缀,判断它们是否能匹配模式串中的剩余部分,回溯方法可以用来解决这一类问题。当模式串中的星号出现在pattern[1]的位置时,可以将问题更新为(1)s与pattern[2:]的匹配问题(2)s[1:]与pattern的匹配问题(s[0]与pattern[0]匹配,pattern[1]的星号可以复制patern前面的字符。)

class Solution(object):
    def isMatch(self, s, p):
        """
        :type s: str
        :type p: str
        :rtype: bool
        """
        if not p:
            return not s
        first_match=bool(s) and  p[0] in {s[0],"."}

        if len(p)>=2 and p[1]=="*":
            return (self.isMatch(s,p[2:])) or (first_match and self.isMatch(s[1:],p))
        else:
            return first_match and self.isMatch(s[1:],p[1:])

19.面试题20-表示数值的字符串

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

参考思路:有限状态机器DFA
有限状态机可以先写正则表达式,然后转换成DFA/直接写,大概率不是最简的。
有限状态机:对于每个状态接收下一个字符,DFA能够确定一条唯一的转换路径,简单的表驱动的一些方法就能实现,只需要读一遍输入流。
真不会写!!!

20.面试题21-调整数组的顺序使得奇数位于偶数的前面-双指针

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。
暴力法:时间超出限制:15/17

class Solution(object):
    def exchange(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        n=len(nums)
        flag=n
        i=0
        while(i<flag):
            if nums[i]%2==0:    
                for j in range(i,flag-1):
                    nums[j],nums[j+1]=nums[j+1],nums[j]
                    # print(i,j,j+1,nums)
                flag-=1 # 用于控制已经操作过的偶数,换完可能还是偶数
            else:
                i+=1
        return nums

不要逐个换,直接用右指针指向结尾

class Solution(object):
    def exchange(self, nums):
        """
        :type nums: List[int]
        :rtype: List[int]
        """
        n=len(nums)
        flag=n-1
        i=0
        while(i<flag):
            if nums[i]%2==0:    
                nums[i],nums[flag]=nums[flag],nums[i]
                flag-=1 # 用于控制已经操作过的偶数,换完可能还是偶数
            else:
                i+=1
        return nums

注意点:换过之后的索引是不增加的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值