本文是采用python为编程语言,作者自行练习使用,题目列表为:剑指 Offer(第 2 版),未使用实体书,难度未标注的均为“简单”,我也不是很清楚为什么有几个编号没有提供。“《剑指 Offer(第 2 版)》通行全球的程序员经典面试秘籍。剖析典型的编程面试题,系统整理基础知识、代码质量、解题思路、优化效率和综合能力这 5 个面试要点。”,本文中的思路来源于每道题目中的题解部分,争取提供全面,优化后的题解,其中所有代码已通过题目检验。
i,j
都小于10的时候,对每个i
有cnt += min( k+1-i, rows)
,当i的十位数是num_i
的时候,相当于k-num_i
带入此式子计算。但写着代码意识到这是个二维的问题,也就是说cnt
的值还取决于j
,随着每个i
中j
的位数增长,就变成了二重循环。当然,对同样的i
的范围,j
每增加10,相对减少的cnt也是可以表达的,但是找规律太不优雅了,遂放弃class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def calnum(x):
sum = 0
while x != 0:
sum += x % 10
x = x // 10
return sum
def dfs(row, col):
if (row>=m or col>=n) or ((row, col) in visited) or (calnum(row)+calnum(col)>k):
return 0
else:
visited.add((row,col))
return 1+ dfs(row+1, col)+ dfs(row, col+1)
visited = set()
return dfs(0,0)
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def calnum(x):
sum = 0
while x != 0:
sum += x % 10
x = x // 10
return sum
def dfs(row, col):
sum = 0
stack.append((0,0))
while stack:
row, col = stack.pop()
if ((row, col) in visited) or calnum(row)+calnum(col)>k:
continue
visited.add((row,col))
sum+=1
if row+1< m:
stack.append((row+1, col))
if col+1< n:
stack.append((row, col+1))
return sum
stack = list()
visited = set()
return dfs(0,0)
class Solution:
def movingCount(self, m: int, n: int, k: int) -> int:
def calnum(x):
sum = 0
while x != 0:
sum += x % 10
x = x // 10
return sum
def dfs(row, col):
sum = 0
queue.append((0,0))
while queue:
row, col = queue.pop(0)
if ((row, col) in visited) or calnum(row)+calnum(col)>k:
continue
visited.add((row,col))
sum+=1
if row+1< m:
queue.append((row+1, col))
if col+1< n:
queue.append((row, col+1))
return sum
queue = list()
visited = set()
return dfs(0,0)
dp[n] = max( len*dp[n-len] )
,即将绳子分成len和n-len两部分,len从0取至n。而针对初始化问题,我们可以使用len*(n-len)
来完成,代表了无法再拆的情况。class Solution:
def cuttingRope(self, n: int) -> int:
dp = [0]*(n+1)
for i in range(2,n+1):
for len in range(1,i):
dp[i] = max(dp[i],len*(i-len),len*dp[i-len])
return dp[n]
*
和pow()
的时间复杂度为O(logn)。而math.pow()
执行浮点取幂,时间复杂度为 O(1)
,但对于大数存在溢出问题,所以在下一题中如果直接使用python的特性无长度,则需要使用**
而非pow()
class Solution:
def cuttingRope(self, n: int) -> int:
# 对应(2,1)(3,2)的情况
if n <= 3:
return n - 1
cnt, loss = n // 3, n % 3
if loss == 0:
return int(math.pow(3, cnt))
elif loss == 1:
return int(math.pow(3, cnt - 1) * 4)
return int(math.pow(3, cnt) * 2)
发现本题和上一题题干几乎完全相同,只增加了一个“取余”的条件,但如果你在最后的结果直接取余。。。发现,只能过去部分范例,这是因为大数取余问题,越界可能发生在每一步而导致结果错误,下面是大数取余的两种常见做法:
当然,该题也可以延续上一问使用动态规划,这源于python语言的无长度性。
class Solution:
def cuttingRope(self, n: int) -> int:
MOD = 1000000007
# 对应(2,1)(3,2)的情况
if n <= 3:
return (n - 1)%MOD
cnt, loss,res = n // 3, n % 3,1
# 使用循环取余法,注意因loss==1的情况需要除以3
# 在每一步都取余的情况下可能出现小数,所以循环至cnt-1,拿出最后一个3
for i in range(cnt-1):
res = ((res%MOD)*3)%MOD
if loss == 0:
return int(res*3%MOD)
elif loss == 1:
return int((res*4)%MOD)
return int((res*6)%MOD)
class Solution:
def cuttingRope(self, n: int) -> int:
MOD = 1000000007
# 对应(2,1)(3,2)的情况
if n <= 3:
return (n - 1)
# 使用快速幂法,注意因loss==1的情况需要除以3
# 在每一步都取余的情况下可能出现小数,所以循环至cnt-1,拿出最后一个3
cnt, loss, x, res = (n // 3) -1, n % 3, 3, 1
# 快速幂法的精髓在于,对半分指数,先算3*3=9,9*9=81...直到最后一个
# 奇数的情况对半除仍是奇数,先乘以一个3即可
while cnt:
if cnt % 2:
res = (res*x) % MOD
x = x**2 % MOD
cnt //= 2
if loss == 0:
return int(res*3%MOD)
elif loss == 1:
return int(res*4%MOD)
return int((res*6)%MOD)
n & (n−1)
,其预算结果恰为把 n 的二进制位中的最低位的 1 变为 0 之后的结果,所以进行循环直到n中的1全部变成0的次数,即为1的个数。注意,该方法还可以用来判断 n 是否是 2 的幂。class Solution:
def hammingWeight(self, n: int) -> int:
res = 0
while n:
res += n & 1
n >>= 1
return res
class Solution:
def hammingWeight(self, n: int) -> int:
res = 0
while n:
res += 1
n &= n - 1
return res
class Solution:
def myPow(self, x: float, n: int) -> float:
res = 1
if n<0:
x,n = 1/x,-n
while n:
if n & 1 :
res = res * x
x *= x
n >>= 1
return res
看起来很简单,但是这在剑指offer上主要考大数问题,需要考虑以下问题:
class Solution:
def printNumbers(self, n: int):
def dfs(x):
if x == n:
res.append(int(''.join(num))) # 拼接 num,转换为int,并添加至 res 尾部
return
for i in range(0, 10): # 每一位都要进行一遍0~9的循环,在每个数字的上进行深度递归,0即代表低位的,如009实际上是9
num[x] = str(i) # 只使用一个num即可,因为每次到达n位结束递归都会加入到
dfs(x + 1)
num = [''] * n
res = []
dfs(0)
res.pop(0)
return res
补充:1.题目保证链表中节点的值互不相同 2.原题的要求是给了需要删除节点的指针,在 O(1) 的时间复杂度完成操作
pre
先指向now
,再移动now
now.next.val
即可class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
pre, now = None, head
if head.val == val:
return head.next
while now.next and now.val != val:
pre = now
now = now.next
pre.next = now.next
return head
class Solution:
def deleteNode(self, head: ListNode, val: int) -> ListNode:
now = head
if head.val == val:
return head.next
while now.next and now.next.val != val:
now = now.next
now.next = now.next.next
return head
串的匹配问题,常常是动态规划问题,且转移方程存在于dp[i][j]
和dp[i-?][j-?]
之间:
f[i][j]=f[i-1][j-1]
如果s[i]=p[i]
时,否则变成false*
的情况,将有以下3种情况满足true:真没写出来,这是leetcode用户@Krahets的代码,注意动态规划时的i位对应的是字符串中的第i位,即s[i-1]
或p[i-1]
:
class Solution:
def isMatch(self, s: str, p: str) -> bool:
m, n = len(s) + 1, len(p) + 1
dp = [[False] * n for _ in range(m)]
dp[0][0] = True
for j in range(2, n, 2):
dp[0][j] = dp[0][j - 2] and p[j - 1] == '*'
for i in range(1, m):
for j in range(1, n):
dp[i][j] = dp[i][j - 2] or dp[i - 1][j] and (s[i - 1] == p[j - 2] or p[j - 2] == '.') \
if p[j - 1] == '*' else \
dp[i - 1][j - 1] and (p[j - 1] == '.' or s[i - 1] == p[j - 1])
return dp[-1][-1]
作者:jyd
链接:https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/solution/jian-zhi-offer-19-zheng-ze-biao-da-shi-pi-pei-dong/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
第一思路是使用正则表达式,写出来了一份很不优美的代码,再一步步简化和合并
有限状态机,两步走,最终会得到如下一张图:
by the way真正做工程的时候,我们一般选择try…catch以防出错(即使这样会慢一些但不常用的话无妨):
class Solution(object):
def isNumber(self, s):
try:
float(s)
except:
return False
return True
import re
class Solution:
def isNumber(self, s: str) -> bool:
def isdou(s):
dou_p = re.compile(r' *[+|-]?(\d*)\.(\d*)')
obj = re.match(dou_p, s)
if not obj or (not obj.group(1) and not obj.group(2)):
return -1
return obj.end()
def isint(s):
int_p = re.compile(r' *[+|-]?\d+')
obj = re.match(int_p, s)
return obj.end() if obj else -1
cut = max(isdou(s),isint(s))
if cut<0:
return False
s = s[cut:]
# 在前面是小数or整数的基础上判断是否是指数
obj = re.match(r'([E|e][+|-]?\d+)? *$',s)
if not s or obj and obj.end()==len(s):
return True
else:
return False
import re
class Solution:
def isNumber(self, s: str) -> bool:
obj = re.match( r' *[+-]?([0-9]*\.[0-9]*|[+-]?[0-9]+)([eE][+-]?[0-9]+)? *', s)
return True if obj and obj.end() == len(s) and obj.group(1) != '.' else False
作者:l1ttle_bad
链接:https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/solution/python-zheng-ze-biao-da-shi-liang-xing-j-nlwv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
def isNumber(self, s: str) -> bool:
# 当前状态下,遇到的情况(key值)转移到的状态(value值)
states = [
{ ' ': 0, 's': 1, 'd': 2, '.': 4 }, # 0. start with 'blank'
{ 'd': 2, '.': 4 } , # 1. 'sign' before 'e'
{ 'd': 2, '.': 3, 'e': 5, ' ': 8 }, # 2. 'digit' before 'dot'
{ 'd': 3, 'e': 5, ' ': 8 }, # 3. 'digit' after 'dot'
{ 'd': 3 }, # 4. 'digit' after 'dot' (‘blank’ before 'dot')
{ 's': 6, 'd': 7 }, # 5. 'e'
{ 'd': 7 }, # 6. 'sign' after 'e'
{ 'd': 7, ' ': 8 }, # 7. 'digit' after 'e'
{ ' ': 8 } # 8. end with 'blank'
]
p = 0
for c in s:
if '0' <= c <= '9': t = 'd'
elif c in "+-": t = 's'
elif c in "eE": t = 'e'
elif c in ". ": t = c
else: t = '?'
if t not in states[p]: return False
p = states[p][t]
# 满足以下状态才是正确的结尾
return p in (2, 3, 7, 8)
class Solution:
def exchange(self, nums: List[int]) -> List[int]:
# 注意长度要减去1才是最后的角标!!!!!!!!
left, right = 0, len(nums)-1
while left<=right:
# 采用与运算优化空间
if nums[left]&1==0 and nums[right]&1==1:
nums[left], nums[right] = nums[right],nums[left]
# 别忘记满足条件的也要变化,否则下一次需要重复判断
left+=1
right-=1
elif nums[left]&1==1:
left+=1
else:
right-=1
return nums
class Solution:
def getKthFromEnd(self, head: ListNode, k: int) -> ListNode:
first_p = head
for step in range(1,k):
first_p = first_p.next
second_p = head
while first_p.next:
first_p = first_p.next
second_p = second_p.next
return second_p
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
prenode = None
nownode = head
while nownode:
nextnode = nownode.next
nownode.next = prenode
prenode = nownode
nownode = nextnode
return prenode
class Solution:
def reverseList(self, head: ListNode) -> ListNode:
# 终止条件对应的是:输入为空 or 尾结点
if not head or not head.next:
return head
newnode = self.reverseList(head.next)
head.next.next = head
head.next = None
return newnode