leetcode-剑指offer-2
本系列博文为题库刷题笔记,(仅在督促自己刷题)如有不详之处,请参考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
3a∗2
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
注意点:换过之后的索引是不增加的。