leetcode-剑指offer-2
- 11.面试题13-机器人的运动范围-广度优先搜索
- 12.面试题14-1-剪绳子
- 13.面试题14-2-剪绳子2
- 14.面试题16-二进制中1的个数-布莱恩·克尼根
- 15.面试题16-数值的整数次方-快速幂解析法
- 16.面试题17-打印从1到最大的n位数
- 17.面试题18-删除链表的节点
- 18.面试题19-正则匹配-回溯
- 19.面试题20-表示数值的字符串
- 20.面试题21-调整数组的顺序使得奇数位于偶数的前面-双指针
本系列博文为题库刷题笔记,(仅在督促自己刷题)如有不详之处,请参考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%10sum_ij+=modi=(i-mod)/10while(j):mod=j%10sum_ij+=modj=(j-mod)/10if sum_ij<=k:return Trueelse:return Falseres=0que=[(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)]=Trueres+=1que.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]=1for 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 dpreturn 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]=1for 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)
基于数学推到的方案
算术几何均值不等式:
n1+n2+...+naa>=n1n2...naa\frac{n_1+n_2+...+n_a}{a}>=\sqrt[a]{n_1n_2...n_a}an1+n2+...+na>=an1n2...na
a个数字的算术平均值,大于等于,这a个数的几何平均值。
a=2时:
n1+n22>=n1n22\frac{n_1+n_2}{2}>=\sqrt[2]{n_1n_2}2n1+n2>=2n1n2
右边取得最大值的当且仅当n1=n2=,...,nan_1=n_2=,...,n_an1=n2=,...,na
即可得到推论1:将绳子 以相等的长度 等分多段,得到的乘积最大。
将绳子按照长度x分成a段,n=axn=axn=ax,乘积最大为xax^axa(x和a都是变量).因为a=nxa=\frac{n}{x}a=xn,即最大值为:
xa=xnx=[x1x]nx^a=x^{\frac{n}{x}}=[x^{\frac{1}{x}}]^nxa=xxn=[xx1]n
问题转换为求上式子的最大值,对上式求导让其为0,求得极大值点为e。因为切分长度必须为整数,比较x=2,x=3的大小。x=3时x1xx^{\frac{1}{x}}xx1更大。
所以推论2为:将绳子尽量分为长度为3的多个等分段时,乘积最大。
综上:
推论1说的是需要将绳子绳子分为k段,这k段的长度是等分时这个k段的乘积最大。
推论2说的是每段的长度最好是3,对应的等分方式乘积最大。
n的取整依据能不能被3整除,可以分为(n=3a+bn=3a+bn=3a+b)-确定的方法有贪心选择的性质:
b=0:最大乘积3a3^a3a
b=1:前面切出的a段3中拿出一段来,和1凑成长度为4子段,切成22=4>13
b=2:最大值为3a∗23^a*23a∗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 = 2a,b=n//3,n%3res=0if b==0:res=3**aelif b==1:res=3**(a-1)*4else:res=3**(a)*2return 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=0while(n):res+=1n=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=1if n<0:flag=-1n*=-1else:flag=1for i in range(n):res*=xreturn res if flag==1 else 1/res
291.304 最后执行输入:0.00001,2147483647 ,报错memotyError
参考思路:
快速幂解析法(二进制):略
快速幂解析法(二分):xnx^nxn可以转换为以下两种情况:
xn=(x2)n//2x^n=(x^2)^{n//2}xn=(x2)n//2 ,n为偶数
xn=x(x2)n//2x^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 0if n<0:x,n=1/x,-nres=1while(n):if n%2==1: # n&1res*=xx*=xn=n//2 # n>>=1return 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**nres=[]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=headpre_node=dummycurr_node =headwhile(curr_node):next_node=curr_node.nextif curr_node.val==val:pre_node.next=next_nodereturn dummy.nextpre_node=curr_nodecurr_node=next_nodereturn 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 sfirst_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=ni=0while(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+=1return nums
不要逐个换,直接用右指针指向结尾
class Solution(object):def exchange(self, nums):""":type nums: List[int]:rtype: List[int]"""n=len(nums)flag=n-1i=0while(i<flag):if nums[i]%2==0: nums[i],nums[flag]=nums[flag],nums[i]flag-=1 # 用于控制已经操作过的偶数,换完可能还是偶数else:i+=1return nums
注意点:换过之后的索引是不增加的。