构建乘积数组
正则表达式匹配
表示数值的字符串
链表中环的入口结点
语言python ,题目来源牛客网
今天是第七天,感谢Datawhale这个学习组织和各位同学,我坚持一周了!
构建乘积数组
问题
给定一个数组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]。不能使用除法。
分析: 思路1:
其实对B的每一个元素,都可以以下标i,把整个乘积分为前后两个部分:B[i]=A[i-1]及之前的累计乘积* A[i]之后到A[n-1]的乘积(以A
[i]为分界点),分别对前后去求,然后更新。
实现细节:以对角线A[i]为界,分为左下部分与右上两部分。可以先做左下部分,从上往下,做一个循环,B中前半部分的当前的每一个元素,是A中对应的一个值和前一个B的值相乘得到。然后再对右上部分做一个循环,从下往上,开一个temp 记录右侧已有的乘积,再把右侧的值乘以原来左侧的值,更新B[i].
思路2:能否做一个循环,当计算B[i]再循环时,碰到A[i],就用1来作为值去乘. 虽然,相对1,计算量重复了一些.但bingo!
思路1代码:
# -*- coding:utf-8 -*-
class Solution:
def multiply(self,A):
if not A:
return []
B=[1]*len(A)
#先做左下部分,从上往下累乘左下半
for i in range(1,len(A)):
B[i]=B[i-1]*A[i-1]
#再做右上部分,这时可以从右下往上乘积。注意此时需要开辟一个temp来存储右侧A的累乘积了
temp=1
for i in range(len(A)-2,-1,-1):
temp=temp*A[i+1]
B[i]=B[i]*temp #左侧的*右侧的,更新了 B[i]
return B
思路2代码:
##**思路2:能否做一个循环,当计算B[i]再循环时,碰到A[i],就用1来作为值去乘.**
def multiply_two(A):
if not A:
return []
B=[1]*len(A)
for i in range(len(B)):
temp=1
for j in range(len(A)):
if i==j:
temp=temp*1
else:
temp=temp*A[j]
B[i]=temp
return B
A_two=[1,2,3,4,5]
BB_two=multiply_two(A)
print(BB_two)
正则表达式匹配
问题
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
思路
这道题相对比较复杂。重点是要思路清晰,注意用到递归去判断…要注意讨论各种各样的情况,导致无法通过所有用例。
对初始输入的判断:
如果s和pattern都为空,则True;如果s不空,而pattern为空,则直接False.
然后来读取pattern中的每个字符:
- 如果当前pattern字符的后面不是* ,则说明只可匹配1个
- 如果当前pattern字符是.的话,可匹配1个任意字符
- 如果不是点,则只能匹配给出的字符,若不匹配则直接返回
- 完成后两者均往后+1,继续判断
- 如果当前pattern字符的后面是* ,说明当前pattern字符可匹配0-任意多个。
- 如果当前pattern字符与串的字符不一致,而且pattern当前字符不是点,则当作是 * 的未匹配的情况,pattern往后+2而字符串不变(如aaa与ab*ac*中b*的情况)(此处自己改动后还是好理解多了)
- 否则其他情况:
- 要么共匹配0个,pattern往后+2,字符串不变
- 要么共匹配1个,字符串往后+1, pattern 往后+2
- 要么先匹配1个,留待继续匹配,此时字符串+1, pattern不变
- 然后继续匹配后面的是否match
-
# -*- coding:utf-8 -*- class Solution: # s, pattern都是字符串 def match(self,s,pattern): #如果s和pattern都为空,则返回true if len(s)==0 and len(pattern)==0: return True #如果s不为空,pattern为空,则返回false elif len(s)!=0 and len(pattern)==0: return False #如果s为空,而pattern不为空,则需要判断 elif len(s)==0 and len(pattern)!=0: #pattern的第二个字符为*,则pattern后移两位继续比较 if len(pattern)>1 and pattern[1]=="*": ###### ##注意用递归 ######### return self.match(s,pattern[2:]) else: return False else: #s 与pattern 都不为空的情况 #pattern 的第二个字符为*的情况 if len(pattern)>1 and pattern[1]=="*": # s与pattern的第一个元素不同,则s不变,pattern后移两位,相当于pattern前两位看成空 if s[0]!=pattern[0] and pattern[0]!=".": return self.match(s,pattern[2:]) else: #如果s[0]与pattern[0]相同,且pattern[1]为*,这时有三种情况 #pattern后移2个,s不变;相当于也把pattern前两位看成空,匹配后面的 #pattern 后移两位,s后移1位,相当于pattern的前两位与s[0]匹配 ####此注意与s多位情况 #pattern不变,s后移1个;相当于pattern前两位与s中多位进行 匹配,因为*可以匹配多位 return self.match(s,pattern[2:]) or self.match(s[1:],pattern[2:]) or self.match(s[1:],pattern) #pattern第二个字符不为*的情况 else: if s[0]==pattern[0] or pattern[0]==".": return self.match(s[1:],pattern[1:]) else: return False表示数值的字符串
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
-
分析与思路
首先应该熟悉数值表示中含有字符的表达形式的有哪些。除了题目列出的情况外,还有哪些?应该熟悉数值的表达形式 补充盲点知识:5e2表示的是5乘以10的2此方,e后面表示的是10的x次方 -
剑指offer 的思路:表示数值的字符串遵循模式
A[.[B]][e|EC]或者.B[e|EC]。其中,A为数值的整数部分,B为小数部分,C为指数部分。要满足以下条件:- 可以没有A部分,或者没有B部分,但不能AB同时没有
- AC均可能以+-开头的0-9字符
- B也是0-9字符但不能有+-号
判断时,从左到右扫描字符串,先尽可能地扫描出整数部分A,碰到小数点再扫描得到B,碰到e或E再扫描得到C。
# -*- coding:utf-8 -*-
class Solution:
def __init__(self):
self.cursor = 0
def scan_integer(self, s):
# 作用:扫描字符串从当前cursor位置往后,是否有整数
# 先处理掉前面的符号位(如果没有符号位就不处理,无影响)
if (self.cursor < len(s)) and (s[self.cursor] == '+' or s[self.cursor] == '-'):
self.cursor += 1
# 再调用判断无符号整数的方法
return self.scan_unsigned_integer(s)
def scan_unsigned_integer(self, s):
# 作用:扫描字符串从当前cursor位置往后,是否有无符号整数
# 先记下cursor本来的位置
original_cursor = self.cursor
# 遇到 0-9 数字就往后移动cursor
while (self.cursor < len(s)) and ('0' <= s[self.cursor] <= '9'):
self.cursor += 1
# 如果cursor比最初后移了,说明有无符号整数
return self.cursor > original_cursor
def isNumeric(self, s):
if not s:
return False
# 先判断是否存在A部分
numeric = self.scan_integer(s)
# 如果出现小数点,则判断小数部分
if (self.cursor < len(s)) and s[self.cursor] == '.':
self.cursor += 1
# 用or的原因是,既可以没有A部分,也可以没有B部分,也可以AB都有,但不能AB都没有
numeric = self.scan_unsigned_integer(s) or numeric
# 如果出现e,则判断指数部分
if (self.cursor < len(s)) and (s[self.cursor] == 'e' or s[self.cursor] == 'E'):
self.cursor += 1
# 用and的原因是,既然出现了E,后面必须要有整数,且E的前面必须是已判断好了的合法数字
numeric = numeric and self.scan_integer(s)
return numeric and self.cursor == len(s)
链表中环的入口结点
题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
方法1思路:(方法2代码更简洁)
环的定义即字面意思,有一个闭环的东东。这个题的关键在于,要知道如何判断是否包含环、如何计算环长度、如何通过两个指针差距的方法找到环的入口。
首先要确定是否包含环。(这个题在第二版书中是位于 链表倒数第k个结点 之后的,所以思路上会有些类似。)确定的方法是,两个指针同时从头出发,一个一次走一步,一个一次走两步,当快指针和慢指针重逢了,说明存在环,如果快指针走到了链表末尾,也没有和慢的重逢过,说明不包含环。
第二步是找出环的入口。仍然可以用两个指针,如图的环有4个节点,那么让p1先走4步,然后p1 p2 一起走,当两个指针重逢时,由于两个指针差距应该是环的长度,而他俩重逢了,说明p1已经绕环走了一圈,重逢的位置就是环的入口。
如何找到环的长度?在前面判断是否有环时,如果快慢指针相遇则说明有环,此时相遇的节点必定在环中,记住这个节点然后从它出发,等再次回到这个节点时就走了一圈,就得到环的长度了。
方法2,
求环长可以看成两个人同时从宿舍出发,到学校环形操场去跑步,只是同学1的速度是同学2速度v的两倍。宿舍到操场的路程记为a,环形操场的长度为b,当他两第一次相遇在距离环形操场起始点位置的Bi处。有如下等式:
a+b+Bi=2vt
a+Bi=vt
两个式子一相减,发现环形操场b的长度就等于slow1同学2走过的路程vt。 相遇时slow1走过的距离,就已经是环的长度了!所以无需再去费劲计算环长。即此时相遇时,slow1同学2已经走了一个环长的长度了。从而可以新开一个结点slow2从head宿舍开始与slow1以同样的速度,都继续往走,当两者相遇时,即slow1走到了环的开始位置了。
# -*- coding:utf-8 -*-
# class ListNode:
# def __init__(self, x):
# self.val = x
# self.next = None
#方法1代码
class Solution:
def EntryNodeOfLoop(self, pHead):
if not pHead:
return None
fast = pHead
slow = pHead
has_loop = False
# 先判断是否有环
while fast:
if fast.next:
fast = fast.next.next #快节点一次走两个
else: # fast已经走到最后的节点了
return None
slow = slow.next #慢节点一次走一个
if fast == slow:#快的值比慢的快一个单位,当两者位置一样时,说明肯定存在环
has_loop = True
break
if not has_loop:
return None
# 统计环中节点个数
count = 1
fast = fast.next #fast为新的fast了,它一次值移动一个
while slow != fast: #当两者相当时,新fast再走了一圈,遇到它时即环长了。
fast = fast.next
count += 1
# 寻找环的入口
fast = pHead
slow = pHead
while count > 0: #fast 先先前移动环长个单位,如
fast = fast.next
count -= 1
while fast != slow: #当两者第一次相等时,即环的开始位置了
fast = fast.next
slow = slow.next
return fast
方法2代码:
# 方法2:
class Solution:
def EntryNodeOfLoop(self, pHead):
slow, fast = pHead, pHead
while fast and fast.next:
slow = slow.next
fast = fast.next.next
if slow == fast: # 有环,此时slow走过的路程就是环的长度。
slow2 = pHead # 新开一个结点从链表头处开始,
while slow != slow2: #相等处即环的位置了
slow = slow.next
slow2 = slow2.next
return slow
参考与感谢:
https://codingcat.cn/article/48
本文探讨了构建乘积数组的两种高效算法,并深入解析了链表中寻找环入口结点的问题。此外,还介绍了正则表达式匹配及数值字符串识别的技术实现。
485

被折叠的 条评论
为什么被折叠?



