剑指offer在线编程(08-12)【8】

Date: 2019--8-12

1.  把字符串转换成整数 (考察知识点:字符串和进制转换)

题目描述

将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。

输入描述:

输入一个字符串,包括数字字母符号,可以为空

输出描述:

如果是合法的数值表达则返回该数字,否则返回0

示例1

输入

复制

+2147483647
    1a33

输出

复制

2147483647
    0

分析:刚开始对于该题想到的是直接int,利用一个try 语法;但是想想应该考察的不是这一点,需要考虑字符串的写法,所以就改为一个字符一个字符的进行判断:

首先仍然是需要对输入字符进行非空判断;

然后把第一个字符单独进行考察:

   i)第一个字符为符号或者为非数字时,先不直接返回0,可以直接将其结果a令为0.

   ii)对于第一位是数字时,可以直接第一位的数字*(10**(len(s)-1)),表达最高位。

然后遍历考察后面位上的字符,如果在0~9之间,则相应*之后进行累加,否则不属于0~9之间的字符时,直接返回0

最后补充考虑第一位是符号位时的情况,+ 时输出a,-时输出-a

最后在最外层还需输出一个a,考虑到如果第一位不是符号位,而且满足每一位都在0~9之间时的输出。

# -*- coding:utf-8 -*-
class Solution:
    def StrToInt(self, s):
        # write code here
        if not s:
            return 0
        else:
            if s[0] >'9'or s[0] <'0':  # 这里是连接是or,因为else中是与条件
                a = 0  # 不能直接返回0,因为可能第一位是符号位
            else:
                a = int(s[0])*(10**(len(s)-1))  # 如果第一位的字符在0~9之间,则转换为最高位的数
            if len(s)>1:
                for i in range(1, len(s)):
                    if s[i] <= '9' and s[i] >='0':  # 首先判断第i位的字符是否在0~9之间。如果是,累加
                        a += int(s[i])*(10**(len(s)-1-i))
                    else:  # 否则,第i位上的为字符,则不符合规则,返回0
                        return 0
        if s[0] == '+':     # 有 + 符号的时候的输出
            return a
        elif s[0] == '-':   # 有 - 符号时的输出
            return -a 
        return a     # 没有前缀符号时的一般输出
# -*- coding:utf-8 -*-  另一种取巧的思路,利用try语句,也通过,而且时间复杂度更小!
class Solution:
    def StrToInt(self, s):
        # write code here
        try:
            return int(s)
        except Exception as e:
            return 0

另外该题也可以先初始化一个数字列表0~9 和 一个符号字典{‘+’:1, '-':-1},然后根据第一位字符的情况进行两路讨论,后面的字符进行遍历,讨论字符情况进行处理(不断进行累加)。

# -*- coding:utf-8 -*-  # 引入数字列表和符号字典,时间复杂度与第一个方法相似,空间复杂度大一点点
class Solution: 
    def StrToInt(self, s):
        if not s:
            return 0
        str2num={'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'0':0}
        flag2num={'-':-1,'+':1}
        first=s[0]
        if first in ['+','-']:
            flag=flag2num[first]
            x=0
            for i in s[1:]:
                if i not in str2num:
                    return 0
                x=x*10+str2num[i]
            return flag*x
        else:
            x=0
            for i in s:
                if i not in str2num:
                    return 0
                x=x*10+str2num[i]
            return x

2.  数组中重复的数字  (考查知识点:  数组)

题目描述

在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

分析:刚开始没有理解这题是什么意思,后来想一想题目:需要找到任意一个重复的数字,首先将其赋值给duplication[0],然后return true,系统后台会自动给出相应的正确输出。

思路:我想的第一种方法就是:借用一个长度为len(numbers)的全零数组B,然后遍历numbers中的每一个数字,在B数组中的相应位置对其出现次数进行累加,如果第一次出现次数等于2,则直接赋值并返回。否则在最外层返回false.(这里题目给出了:numbers是一个长度为n的数组里的所有数字都在0到n-1的范围内,所以可以假定B数组的数组也是默认从0~n-1的)

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        if not numbers:
            return False
        B = [0]* len(numbers)  # 初始化全零数组 B,用于计算numbers中的数字的频次
        for i in range(len(numbers)):  # 遍历
            B[numbers[i]] += 1   # 根据numbers[i]的数字,在B中对应位置进行累加计算频次,
            if B[numbers[i]] == 2:  # 如果频次第一次出现2,即可进行赋值并return
                duplication[0] = numbers[i]
                return True
        return False

第二种取巧的方法是利用collections模块中的Counter()库,对numbers中的每个数字进行计算频次,但我觉得考该题的本意不在这里(这是一种高级算法),但解决问题还是可以的。

# -*- coding:utf-8 -*-
import collections
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        if not numbers:
            return False
        c_dicts = collections.Counter(numbers)  # 根据该函数得到每个数字的频次统计,是一个字典
        for k,v in c_dicts.items():
            if v >1:   # 如果相应的K的频次大于1,进行赋值并返回
                duplication[0] = k
                return True
        return False

3.  构建乘积数组  (考查知识点:数组)  (下标的选择有点难以理解,多看!!)

题目描述

给定一个数组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]。不能使用除法。

分析:思路一:超级高的时间复杂度,不考虑,但写一下思路:对于每一个元素B[i ]的计算直接采用累乘A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。但是显然从因子来分析,发现其可以当成前后两部分,进而看成两部分的乘积!

思路二:从上面直观的想法来看可以发现,我们可以利用“牺牲空间换取时间”的想法,构造两个列表,用来存储前向的递归乘积和后向的递归乘积;head[i] = A[0]*A[1]*……*A[i-1]  tail[i] = A[-1]*A[-2]*……*A[-i],注意的是:这两个列表的首元素都是1,而且tail在取的时候采用后向遍历法(负索引)

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        if not A:
            return False
        head = [1]  # 初始值,是当求第一个元素时,第一个元素的值应当被赋予1,而是采用A[1]……*A[n-1]
        tail = [1]  # 初始值,是当求最后一个元素时,最后一个元素的值应当被赋予1,而是采用A[-2]……*A[-n]
        for i in range(len(A)-1):
            head.append(head[i]*A[i])  # 前向进行累乘的结果,最后长度等于len(A)
            tail.append(tail[i]*A[-i-1])  # 反向进行累乘的结果,最后长度等于len(A)
        return [head[j]*tail[-j-1] for j in range(len(head))]  # 注意head是前向取,而tail是后向索引在取!

附上牛客上别人直观的解释:

 

思路:按上图把每行被1分割的两部分乘积都计算出来,这样可以从首尾分别用累乘算出两个列表,然后两个列表首尾相乘就是B的元素

4.  正则表达式匹配   (考察知识:字符串,正则表达式)  好多细节需要去讨论,较为复杂!!! 多看看

题目描述

请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配

分析思路:(先写一下整体的思路)总共分为四个大部分,然后有的部分下面需要进行讨论

1) 两个字符串都为空,直接匹配成功

2)字符串为空,但模式非空,有可能匹配不成功,有可能成功(当模式长度大于1且第二位是*时候,进行模式后移两位的递归调用;否则直接匹配不成功)

3)字符串非空,但是模式为空,显然匹配不成功

4)字符串和模式都不为空的时候,需要借助模式中的第二位是否为*进行分类讨论:

     4.1)如果模式长度大于1且模式第二位是*时, 需要进行首位是否相等的判断

          4.1.1) 如果首位不相等且模式首位不为.时,字符串不变,模式后移两位(相当于将模式前两位视为空)

          4.1.2) 否则分为下面三种情况,任意一种成立都可以递归调用:

                     i) 将p的前两位当成空:p后移动两位,s不变,进行递归调用

                     ii) 将p的前两位去匹配s的第一位:p后移动两位,s移动一位,(相当于*代重复0次)

                    iii) 尝试用p的前两位去匹配s的前多位字符:p不变,s后移动一位(尝试匹配多位字符)

    4.2) 如果第二个字符不等于*,  当字符串和模式的第一位相等或者模式第一位为.时,匹配第一位成功,则往后递归调用进行匹配;否则匹配不成功!

# 这是目前所有offer题中,我注释写的最长的了。因为难易理解,有很多细节需要去考虑,一定要多看多看多看!!!
# -*- coding:utf-8 -*-
class Solution:
    # s, pattern都是字符串
    def match(self, s, pattern):
        # write code here
        # 如果两者都为空,则匹配成功
        if len(s) == 0 and len(pattern) == 0:
            return True
        # 如果字符串为空,模式不为空。需要判断模式的长度,如果大于1且第二位为*,则递归匹配,且模式后移两位,否则如果长度为1或者第二位不是*,匹配不成功
        elif len(s) == 0 and len(pattern) != 0:
            if len(pattern)>1 and pattern[1] == '*':
                return self.match(s,pattern[2:])  # 此时相当于将pattern中的*运用为0次。
            # 否则如果长度为1或者第二位不是*,匹配不成功
            else:
                return False  
        # 如果字符串非空,但是模式为空,显然匹配不会成功。
        elif len(s) != 0 and len(pattern) == 0:
            return False
        # 两者都不为空,需要借助模式的第二位进行讨论:
        else:
            # 模式第二位是*
            if len(pattern)>1 and pattern[1] == '*':
                # 如果第一位不等且不为.  此时模式后移两位,相当于将模式的前两位视为空。
                if  s[0] != pattern[0] and pattern[0] != '.':
                    return self.match(s, pattern[2:])
                else:
                    # 模式第二位为*,且模式第一位和字符串第一位相等,有以下三种情况,任意一种情况成立都可以:
                    #1 将p前两位当成空:p后移两位,s不变
                    #2 p的前两位去匹配第一位:p后移两位,s后移一位(此时相当于*代表重复0次,p与s[0]匹配)
                    #3 p的前两位去匹配s的多位:p不变,s后移一位(此时相当于p的前两位尝试着去匹配s中的前多位字符)
                    return self.match(s,pattern[2:]) or self.match(s[1:],pattern[2:]) or self.match(s[1:],pattern)
            else:
                # 模式第二位不是*时,先判断如果第一位相等或者模式的第一位是.  则他们第一位匹配成功,都向后移动一位
                if s[0] == pattern[0] or pattern[0] == '.':
                    return self.match(s[1:],pattern[1:])
                # 否则,模式第二位不是*,且第一位不相等 且模式第一位非. 则匹配不成功
                else:
                    return False

5.  表示数值的字符串   (考查知识点:字符串+正则表达式+数学规则)

题目描述

请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。

分析:该题和第一题有着类似的取巧通过的方法,但不是题目的本意(强烈不建议大家这样操作,我是个什么样怪人啊!!)

先粘出取巧的通过代码:

# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        try:
            return float(s)
        except Exception as e:
            return False

第二种方法:基于正则表达式的思想 利用re库进行匹配

# -*- coding:utf-8 -*-
import re
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        return re.match(r"^[\+\-]?[0-9]*(\.[0-9]*)?([eE][\+\-]?[0-9]+)?$",s)

第三种:数学规则方法,建议多看!

思路解析:分别初始化三个标记来分别标记符号位、小数点、e是否出现过

  1) 对于e的情况,不能同时出现两个e;而且e不能在最后一位(因为e后要接数字)

  2)对于符号位的情况,如果前面出现过了符号位,则该符号应该出现在e之后,否则错误

                                       如果是第一次出现符号位,如果i>1,则前一位必须是e,否则false

  3) 对于小数点的情况,不能出现两次,且出现过了e就不能再有小数点了出现了

                                    如果是第一次出现小数点,但前面已经出现过了e就不行

  4)否则不是e 小数点  符号 ,就应该是0-9之间的数,否则false

因为整个里面是来判断不符合的情况,所以里面的情况都不满足的话,跳到最外层给出True的结果!

# -*- coding:utf-8 -*-
class Solution:
    # s字符串
    def isNumeric(self, s):
        # write code here
        if len(s) <= 0:
            return False
        # 分别标记是否出现过正负号、小数点、e,因为这几个需要特殊考虑
        has_sign = False
        has_point = False
        has_e = False
        for i in range(len(s)):
            # 1 对于e的情况
            if s[i] == 'E' or s[i] == 'e':
                # 不同出现两个e
                if has_e:
                    return False
                # e不能出现在最后面,因为e后面要接数字
                else:
                    has_e = True
                    if i == len(s)-1:
                        return False
             # 2 对于符号位的情况
            elif s[i] == '+' or s[i] == '-':
                # 如果前面已经出现过了符号位,那么这个符号位,必须是跟在e后面的
                if has_sign:
                    if s[i-1] != 'e' or s[i-1] != 'E':
                        return False
                 # 如果这是第一次出现符号位,而且出现的位置不是字符串第一个位置,那么就只能出现在e后面
                else:
                    has_sign = True
                    if i > 0 and s[i-1] != 'e' and s[i-1] != 'E':
                        return False
             # 3 对于小数点的情况
            elif s[i] == '.':
                # 小数点不能出现两次;而且如果已经出现过e了,那么就不能再出现小数点,因为e后面只能是整数
                if has_point or has_e:
                    return False
                # 如果是第一次出现小数点,如果前面出现过e,那么还是不能出现小数点
                else:
                    has_point = True
                    if i > 0 and has_e:
                        return False
            else:
                # 4 其他字符必须是‘0’到‘9’之间的
                if s[i] < '0' or s[i] > '9':
                    return False
            return True

 6.  字符流中第一个不重复的字符   (考查知识点:字符串)

题目描述

请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。

输出描述:

如果当前字符流没有存在出现一次的字符,返回#字符。

分析:本题刚开始没有理解为什么有两个函数,还有一个insert函数,难道时需要不断进行字符串的补充,后来看他们的讨论确实是这样的。所以便采用__init__函数,建立一个全局成员函数:self.s。然后不断对这个s进行char的补充。

正式的输出不重复的字符是来自于FirstAppearingOnce函数,然后利用了count函数对s中的字符进行频次计算,频次等于1的字符将被依次放入res中,然后返回第一个即可。

# -*- coding:utf-8 -*-
class Solution:
    # 返回对应char
    def __init__(self,):
        self.s = ''
    def FirstAppearingOnce(self):
        # write code here
        res = list(filter(lambda c: self.s.count(c)==1,self.s))
        return res[0] if res else '#'
    def Insert(self, char):
        # write code here
            self.s += char

但我感觉上面的思路也不是本题的考察本意,毕竟我现在没有剑指offer书,不知道寓意如何?

第二种方法:

"""
解法:利用一个int型数组表示256个字符,这个数组初值置为-1.    
每读出一个字符,将该字符的位置存入字符对应数组下标中。    
若值为-1标识第一次读入,不为-1且>0表示不是第一次读入,将值改为-2.    
之后在数组中找到>0的最小值,该数组下标对应的字符为所求。    
在python中,ord(char)是得到char对应的ASCII码;chr(idx)是得到ASCII位idx的字符    
"""
class Solution:
    def __init__(self):
            self.char_list = [-1 for i in range(256)]
            self.index = 0  # 记录当前字符的个数,可以理解为输入的字符串中的下标
    def FirstAppearingOnce(self):
        # write code here
        min_value = 500
        min_idx = -1
        for i in range(256):
            if self.char_list[i] > -1:
                if self.char_list[i] < min_value:
                    min_value = self.char_list[i]
                    min_idx = i
        if min_idx > -1:
            return chr(min_idx)
        else:
            return '#'
    def Insert(self, char):
        # 如果是第一出现,则将对应元素的值改为下边
        if self.char_list[ord(char)] == -1:
            self.char_list[ord(char)] = self.index
        # 如果已经出现过两次了,则不修改
        elif self.char_list[ord(char)] == -2:
            pass
        # 如果出现过一次,则进行修改,修改为-2
        else:
            self.char_list[ord(char)] = -2
        self.index += 1

第三种方法: 该解法虽然通过了,但我觉得有问题。

class Solution:
    def __init__(self):
        self.s = ''
        self.queue = []     #按顺序保存所有只出现一次的字符
        self.second = []    #按顺序保存所有出现过的字符
    def FirstAppearingOnce(self):
        if self.queue:
            return self.queue[0]
        return '#'     
    def Insert(self, char):
        self.s += char
        if char in self.queue:
            self.queue.pop(self.queue.index(char))  # 如果一个字符出现了3次呢,前两次就抵消了?
        elif char not in self.second:
            self.queue.append(char)  # 是否会把出现了奇数词的字符放进去?
            self.second.append(char)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值