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等 |
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 '''