• python基础(9)--递归、二叉算法、多维数组、正则表达式


    1.递归

      在函数内部,可以调其他函数,如果一个函数在内部调用它本身,这个函数就是递归函数。递归算法对解决一大类问题是十分有效的,它往往使算法的描述简洁而且易于裂解

    递归算法解决问题的特点:

    1)递归是在过程或函数里调用自身

    2)在使用递归策略时,必须有一个明确的递归结束条件,称为递归出口

    3)递归算法解题通常显得很简洁,但递归算法解题的运行效率较低,所以一般不提倡用递归算法设计程序

    4)在递归调用的过程当中系统为每一层的返回点、局部量等开辟了栈来存储。递归次数过多容易造成栈溢出等。所以一般不提倡用递归算法设计程序

    递归算法所体现的“重复”一般有三个要求:

    1)每次调用在规模上都有所缩小(通常是减半)

    2)相邻两次重复之间有紧密的联系,前一次要为后一次做准备(通常前一次的输出就作为后一次的输入)

    3)在问题的规模极小时必须用直接给出解答而不再进行递归调用,因为每次递归调用都是有条件的(以规模未达到直接解答的大小为条件),无条件递归调用将会成为死循环而不能正常结束

    例:

    def func(num):
        if num / 2 > 0:
            num -= 1
            print(num)
            num = func(num)
            print('quit')
        return num
    
    func(10)

      二叉算法---具体实例可以通过递归实现二分查找

    def binary_search(data_list, find_num):
        mid_pos = int(len(data_list) / 2)   #计算需要查找数据长度的一半
        mid_val = data_list[mid_pos]    #获取中间位置的那个值
        print(data_list)    #查看每次剩余筛选的数据列表
        if len(data_list) > 0:  #当列表长度大于0时,则一直查找
            if mid_val > find_num:  #如果中间的数比实际要查找的数大,那么这个数肯定在左边
                print("%s should be in left of [%s]" % (find_num, mid_val))
                binary_search(data_list[:mid_pos], find_num)
            elif mid_val < find_num:    #如果中间的数比实际查找的数小,那么这个数肯定在右边
                print("%s should be in right of [%s]" % (find_num, mid_val))
                binary_search(data_list[mid_pos:], find_num)
            else:
                print("Find %s" % find_num)
    
        else:   #否则就没有这个数
            print("cannot find [%s] in data_list" % find_num)
    
    
    if __name__ == '__main__':
        primes = [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
        binary_search(primes, 1)    #在列表里面查找1
    
    '''
    执行结果
    [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
    1 should be in left of [41]
    [1, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37]
    1 should be in left of [17]
    [1, 3, 5, 7, 11, 13]
    1 should be in left of [7]
    [1, 3, 5]
    1 should be in left of [3]
    [1]
    Find 1
    '''

      多维数组交叉(多维数组旋转90度)

    array = [[col for col in range(4)] for row in range(4)] #初始化一个4*4数组
    
    for row in array:   #旋转前先看看数组长啥样
        print(row)
    
    
    for i, row in enumerate(array):
        for index in range(i, len(row)):
            tmp = array[index][i]   #将每一列数据在每一次遍历前,临时存储
            array[index][i] = array[i][index]   #将每一次遍历的值,赋值给交叉的列
            print(tmp, array[i][index])
            array[i][index] = tmp   #将之前保存的交叉列的值,赋值给交叉行的对应值
        for r in array:     #打印每次交换后的值
            print(r)
    
    
    
    '''
    [0, 1, 2, 3]
    [0, 1, 2, 3]
    [0, 1, 2, 3]
    [0, 1, 2, 3]
    0 0
    0 1
    0 2
    0 3
    [0, 0, 0, 0]
    [1, 1, 2, 3]
    [2, 1, 2, 3]
    [3, 1, 2, 3]
    1 1
    1 2
    1 3
    [0, 0, 0, 0]
    [1, 1, 1, 1]
    [2, 2, 2, 3]
    [3, 3, 2, 3]
    2 2
    2 3
    [0, 0, 0, 0]
    [1, 1, 1, 1]
    [2, 2, 2, 2]
    [3, 3, 3, 3]
    3 3
    [0, 0, 0, 0]
    [1, 1, 1, 1]
    [2, 2, 2, 2]
    [3, 3, 3, 3]
    '''

      正则表达式

      字符串是编程时涉及到最多的一种数据结果,对字符串进行操作的需求几乎无处不在,比如判断一个字符串是否是合法的Email地址,虽然可以编程提取@前后的子串,再分别判断是否是单词和域名,但这样做不但麻烦,而且代码难以服用

      正则表达式使用单个字符串来描述、匹配一系列符合某个句法规则的字符串,在文本处理方面功能非常强大,也经常用作爬虫,来爬取特地内容,Python本身不支持正则,但是通过导入re模块,Python也能用正则表达式,下面就来讲一下python正则表达式的用法

    Python支持的正则表达式元字符和语法

    语法 说明 表达式实例 完整匹配的字符串
    字符
    一般字符 匹配自己 abc abc
    . 匹配任意字符“ ”除外
    DOTALL模式中(re.DOTALL)也能匹配换行符
    a.b abc或abc或a1c等
    [...] 字符集[abc]表示a或b或c,也可以-表示一个范围如[a-d]表示a或b或c或d a[bc]c abc或adc
    [^...] 非字符集,也就是非[]里的之外的字符 a[^bc]c adc或aec等
    预定义字符集(也可以系在字符集[...]中)
    d 数字:[0-9] adc a1c等
    D 非数字:[^0-9]或[^d] aDc abc等
    s 空白字符:[<空格> fv] asc a b等
    S 非空白字符:[^s] aSc abc等
    w 字母数字(单词字符)[a-zA-Z0-9] awc abc或a1c等
    W 非字母数字(非单词字符)[^w] aWc a.c或a_c等
    数量词(用在字符或(...)分组之后)
    * 匹配0个或多个前面的表达式。(注意包括0次) abc* ab或abcc等
    + 匹配1个或多个前面的表达式。 abc+ abc或abcc等
    ? 匹配0个或1个前面的表达式。(注意包括0次) abc? ab或abc
    {m} 匹配m个前面表达式(非贪婪) abc{2} abcc
    {m,} 匹配至少m个前面表达式(m至无限次) abc{2,} abcc或abccc等
    {m,n} 匹配m至n个前面的表达式 abc{1,2} abc或abcc
    边界匹配(不消耗待匹配字符中的字符)
    ^ 匹配字符串开头,在多行模式中匹配每一行的开头 ^abc abc或abcd等
    $ 匹配字符串结尾,在多行模式中匹配每一行的结尾 abc$ abc或123abc等
    A 仅匹配字符串开头 Aabc abc或abcd等
     仅匹配字符串结尾 abc abc或123abc等
     匹配一个单词边界,也就是指单词和空格间的位置。例如, 'er' 可以匹配"never" 中的 'er',但不能匹配 "verb" 中的 'er'。    
    B 匹配非单词边界。'erB' 能匹配 "verb" 中的 'er',但不能匹配 "never" 中的 'er'。    
    逻辑、分组
    | 或左右表达式任意一个(短路)如果|没有在()中表示整个正则表达式(注意有括号和没括号的区别) abc|def
    ab(c|d)ef
    abc或def
    abcef或abdef
    (...) 分组,可以用来引用,也可以括号内的被当做一组进行数量匹配后接数量词 (abc){2}a abcabca
    (?P<name>...) 分组别名,给分组起个名字,方便后面调用    
    <number> 引用编号为<number>的分组匹配到的字符串(注意是配到的字符串不是分组表达式本身) (d)abc1 1ab1或5ab5等
    (?=name) 引用别名为name的分组匹配到的字符串(注意是配到的字符串不是分组表达式本身) (?P<id>d)abc(?P=id) 1ab1或5ab5等
           
     
       数量词的贪婪模式与非贪婪模式
      正则表达式通常用于在文本中查找匹配的字符串,Python里数量词默认是贪婪的(在少数语言里也可能默认非贪婪),总是尝试匹配尽可能多的字符,非贪婪模式则相反,总是尝试屁屁额尽可能少的字符,例如:正则表达式"ab*"如果用于查找"abbbc",将 找到"abbb"。而使用非贪婪的数量词"ab*?",将找到"a"
     
       Python的re模块
      Python通过re模块提供对正则表达式的支持,使用re的一般步骤是先将正则表达式的字符串形式编译为Pattern实例,然后使用Pattern实例处理文本并获得匹配结果(一个Match实例),最后使Match实例获得信息,进行其他操作
    import re
    
    #将正则表达式编译成Pattern对象
    pattern = re.compile(r'hello')
    
    #使用Pattern匹配文本,获得匹配结果,无法匹配时将返回None
    match = pattern.match('hello world!')
    
    if match:
        #使用Match获得分组信息
        print(match.group())
    
    #执行结果hello

      

      还可以直接使用re模块的方法的时候直接传递正则表达式参数来完成

     
    match = re.match('hello', 'hello world')
    print(match.group()

      re模块常用方法

    1)findall(获取字符串中所有的匹配字符串)

    import re
    l = re.findall('d', '4k3sadsd8,9') #d代表数字,将匹配带的元素放到一个列表里
    print(l)    #['4', '3', '8', '9']
    
    print(re.findall('w', 'de..,_2'))  #['d', 'e', '_', '2']匹配字母数字下划线
    
    print(re.findall('^dk', 'dkwej,ds34,')) #以dk开头的['dk']
    
    print(re.findall('^dk', 'des,423')) #[]
    
    print(re.findall('d{3,5}', 'sddddd'))   #取前面一个字符d的3到5次
    
    print(re.findall('s{2}', 'ssaesssfgss'))    #匹配前一个字符s两次['ss', 'ss', 'ss']
    
    print(re.findall('a*x', 'aaaaaax')) #['aaaaaax']匹配前面一个字符0次或多次,贪婪匹配
    
    print(re.findall('d*', 'swe32452'))    #['', '', '', '32452', '']
    
    print(re.findall('a+c', 'aaaccc'))  #['aaac']匹配前面一个字符的一次或多次,贪婪匹配
    
    print(re.findall('a?c', 'aaaccc'))  #['ac', 'c', 'c']匹配前面一个字符的0次或1次
    
    print(re.findall('a[.]s', 'adfff afs')) #.在[]里面失去意义,所以为[]
    
    print(re.findall('[a-z]', 'we3.j4_re')) #['w', 'e', 'j', 'r', 'e']
    
    print(re.findall('[^a-z]', 'we3.j4_re'))    #['3', '.', '4', '_']取反
    
    print(re.findall('ax$', 'dsadax'))  #以ax结尾['ax']
    
    print(re.findall('a(d+)b', 'a42323b')) #['42323']
    
    print(re.findall('a(d+?)b', 'a42323b'))#['42323']前后均有限定条件,则非贪婪模式,失效
    
    print(re.findall('a(d+)', 'a23b')) #['23']
    
    print(re.findall('a(d+?)', 'a23b'))#['2']加上?变成非贪婪模式

      findall高级用法: ?:

      默认取分组()内的信息,但是想让分组外的匹配信息也渠道,就要用?:

    import re
    l = re.findall("www.(baidu|laonanhai).com", "asd www.baidu.commwww.laonanhai.com")
    print(l)    #['baidu', 'laonanhai']默认取括号内的信息
    
    l = re.findall("www.(?:baidu|laonanhai).com", "asd www.baidu.commwww.laonanhai.com")
    print(l)    #['www.baidu.com', 'www.laonanhai.com']

      finditer(): 迭代查找

    import re
    p = re.compile('d+')
    iterator = p.finditer('12 drumm44ers drumming, 11 ... 10 ...')
    for match in iterator:
        print(match.group())
        print(match.span())
    
    
    '''
    执行结果
    12
    (0, 2)
    44
    (8, 10)
    11
    (24, 26)
    10
    (31, 33)
    '''

    2)match(pattern, string, flag=0)

    ·正则表达式

    ·要匹配的字符串

    ·标志位,用于控制正则表达式的匹配方式

    注意:这个方法并不是完全匹配。当pattern结束时若string还有剩余字符,仍然视为成功。想要完全匹配,可以在表达式末尾加上边界匹配符'$'。 

    返回值:如果匹配成功返回match对象,否则返回None

    Match对象是一次匹配的结果,包含了很多关于此次匹配的信息,可以使用Match提供的可读属性或方法来获取这些信息

    方法:
    1.group([group1, ...]):
    获得一个或多个分组截获的字符串,指定多个参数时将以元组形式返回,group1可以使用编号也可以使用别名,编号0代表整个匹配的子串,不填写参数时,返回group(0),没有截获字符串的返回None,截获了多次的返回最后一次的子串
    2.group([default]):
    以元组形式返回全部分组截获的字符串,相当于调用group(1,2,...last),default表示没有截获字符串的组以这个代替,默认为None
    3.groupdict([default]):
    返回以有别名的组的别名为键、以该组截获的子串为值得字典,没有别名的组不包含在内,default含义同上
    4.start([group]):
    返回以指定的组截获的子串string中的其实索引(子串第一个字符的索引),group默认值为0
    5.end([group])
    返回指定的组截获的子串在string中的结束索引(子串最后一个字符的索引+1),group默认值为0
    6.span([group]):
    返回(start(group),end(group))
    7.expand(template):
    将匹配到的分组带入template中然后返回,template中可以使用id或g<id>、g<name>引用分组,但不能使用编号0,id与g<id>是等价的;但10将被认为是第10个分组,如果你想表达1之后是字符'0',只能使用g<1>0。

    3)search(pattern,string,flag=0)

      根据模型去字符串中匹配指定内容,匹配单个,只匹配一次,可以结合split将匹配到内容分割、拼接,然后再次循环查找,因为findall尽管可以找到所以,但是在处理分组()时候分组外的内容匹配不到,findall返回的是列表

    可以通过一个实例来看下match跟search的区别

    import re
    s = 'hello world'
    print(re.match('ello', s))  #输出None
    
    print(re.search('ello', s)) #<_sre.SRE_Match object; span=(1, 5), match='ello'>
    
    '''
    可以看到match只匹配开头,search则可以从中间匹配,只要有就能匹配到
    '''

    4)group和groups

    group(0)  显示全部

    group(1)  显示第一个分组()

    group(2)  显示第二个分组()

    如果没有分组或超出分组个数就会报错

    5)split(pattern, string, maxsplit=0, flags=0)

      按照匹配子串将字符串进行分割,返回分割的列表,maxsplit用于指定最大分割次数,不指定将全部分割。

    import re
    s = 'one1two2three3four4'
    print(re.split('d+', s))
    
    #['one', 'two', 'three', 'four', '']

    6)re.compile(strPattern[, flag]): compile 编译方法

      如果一个匹配规则,以后要使用偶次,就可以先将其编译,以后就不用每次都在去写匹配规则

    这个方法是Pattern类的工厂方法,用于将字符串形式的正则表达式编译为Pattern对象。第二个参数flag是匹配模式,取值可以使用按位或运算符'|',表示同时生效,比如re.l | re.M

      可以把正则表达式编译成一个正则表达式对象。可以把那些经常使用的正则表达式编译成正则表达式对象,可以提高一定的效率,下面是一个正则表达式对象的例子

    import re
    text = "JGood is a handsome boy, he is cool, clever, and so on..."
    regx = re.compile('w*oow')
    print(regx.findall(text))   #查找所有包含'oo'的单词['ood', 'ool']

    7)sub(pattern, repl, string, count=0, flag=0)

      用于替换匹配的字符串,pattern内必须为正则表达式,不能是正则比倒是search或findall查找到的赋值变量

      比如我的计算器处理括号的方法,用正则search匹配到后,不能直接将变量输入sub的pattern,因为不起做哟用

      sub(repl, string[, count]) | re.sub(pattern, repl, string[, count]): 

      使用repl替换string中每一个匹配的子串后返回替换后的字符串

      当repl是一个字符串时,可以使用id或g<id>、g<name>引用分组,但不能使用编号0

      当repl是一个方法时,这个方法应当只接受一个参数(Match对象),并返回一个字符串用于替换(返回的字符串中不能再引用分组)

    import re
    print(re.subn('d', 'ZZ', '34*,5sdfs,4hsdf'))
    
    #('ZZZZ*,ZZsdfs,ZZhsdf', 4)
    #subn方法,返回总共替换的次数

      实例:假设有一个四则元素表达式'--(1.1+1+1-(-1)-(1+1+(1+1+2.2)))+-----111+--++--3-+++++++---+---1+4+4/2+(1+3)*4.1+(2-1.1)*2/2*3',遵循奇数个负号等于正否则为负的原则进行替换,我们可以这样

    import re
    s = '--(1.1+1+1-(-1)-(1+1+(1+1+2.2)))+-----111+--++--3-+++++++---+---1+4+4/2+(1+3)*4.1+(2-1.1)*2/2*3'
    def replace_sign(expression):
        def re_sign(m):
            if m:
                if m.group().count('-') % 2 == 1:
                    return '-'
                else:
                    return '+'
            else:
                return ''
    
        expression = re.sub('[+-]{2,}', re_sign, expression)
        return expression
    
    s = replace_sign(s)
    print(s)
    
    
    #+(1.1+1+1-(-1)-(1+1+(1+1+2.2)))-111+3-1+4+4/2+(1+3)*4.1+(2-1.1)*2/2*3

    正则补充 match search示例

    import re
    print(re.match('www', "www.baidu.com"))  # 打印匹配结果
    print(re.match('www', "www.baidu.com").group())  # 打印匹配到的结果
    print(re.match('www', "www.baidu.com").end())  # 打印匹配到的结束位置,不包含该索引字符
    print(re.match('www', "www.baidu.comw").endpos)  # 打印最后一个字符串位置
    print(re.match('www', "www.baidu.com").groupdict())
    print(re.match('www', "www.baidu.com").groups())
    print(re.match('www', "www.baidu.com").lastindex)
    print(re.match('www', "www.wwwbaidu.com").pos)  # 打印匹配索引
    print(re.match('www', "www.baidu.com").re)  # 打印编译(解析公式)
    print(re.match('ww', "www.baidu.com").span())  # 打印匹配分片的位置
    print(re.match('www', "www.baidu.com").start())  # 打印匹配开始索引
    print(re.match('www', "www.baidu.com").string)  # 打印被匹配字符串
    
    print(re.search("www", "www.www.com"))
    print(re.search("www", "qq.www.com"))
    print(re.search("www", "www.www.com").string)
    print(re.search("www", "www.www.com").start())
    print(re.search("www", "qq.www.com").start())
    print(re.search("www", "qq.www.com").span())  # 返回匹配到的索引位置
    print(re.search("www", "www.www.com").span())
    print(re.search("www", "qq.www.com").group())
    print(re.search("www", "qq.www.com").groups())
    print(re.search("www", "qq.www.com"))
    
    # search 与match,
    # search 从头匹配,只要找到则停止匹配,如果有多个被匹配,只返回最先匹配到的位置字符
    # match  从头匹配,开头位置匹配不到就不继续匹配

    正则分组匹配模式 ()

    a = "123abc456"
    print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group())  # 模式()分组匹配
    print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(0))  # 显示第0组,也就是所有分组
    print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(1))  # 显示第一组分组
    print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).group(2))
    print(re.search("([0-9]*)([a-z]*)([0-9]*)", a).groups())  # 返回元组形式的分组匹配

    匹配中文

    re.findall("[u4e00-u9fa5]+", string)
    contactInfo = 'Oldboy School, Beijing Changping Shahe: 010-8343245'
    match = re.search('(?P<last>w+s+w+),(?P<first>s+w+s+w+s+w+): (?P<phone>S+)', contactInfo)
    print(match.group("last"))  # 打印自定义标签内容
    print(match.group('first'))
    print(match.group("phone"))
    
    '''
    Oldboy School
     Beijing Changping Shahe
    010-8343245
    '''
  • 相关阅读:
    使用yarn来替代npm
    React及Nextjs相关知识点小结
    appstore-react v2.0—redux-actions和redux-saga的应用
    开机SystemServer到ActivityManagerService启动过程分析
    java 读取气象专业格式NetCDF文件
    maven项目对于maven远程仓库没有资源的解决办法
    leaflet 使用kriging.js实现前端自定义插值
    leaflet 使用高德地图实例
    uni-app上使用leaflet地图的解决方案
    MySQL创建新用户并且赋予权限
  • 原文地址:https://www.cnblogs.com/caoj/p/7880701.html
Copyright © 2020-2023  润新知