剑指offer刷题0406

本文探讨了构建乘积数组的两种高效算法,并深入解析了链表中寻找环入口结点的问题。此外,还介绍了正则表达式匹配及数值字符串识别的技术实现。

 

构建乘积数组

正则表达式匹配

表示数值的字符串   

链表中环的入口结点

语言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

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值