Python 练习题总结(待续)
1、编写一个函数,接受一个参数n,n为正整数,左右两种打印方 式。要求数字必须对齐
正三角
倒三角
实现思路:
思路1、一行一行打印,前面追加空格,每一个空格的宽度等于数字字符串的宽度
#正三角打印
def triangle(k): for a in range(1,k+1): a =1 for b in range(k,0,-1): a=15 if a < b: print(' '*len(str(b)),end=' ') else: print(b,end=' ') print()
思路2、切割打印,首先每个数字隔一个空格,获取长度,当遇到遇到一个空格,就把前面全部补成空格后面数字和空格全部打印。
#正三角打印 def triangle2(n): tail=" ".join([str(i) for i in range(n,0,-1)]) width = len(tail) for i in range(-1,-width,-1): if tail[i] == ' ':#1 print(' '*(width+i),tail[i+1:]) print(tail)
#倒三角打印 def triangle1(n): tail=" ".join([str(i) for i in range(n,0,-1)]) print(tail) for i in range(len(tail)): if tail[i] == ' ': print(' '*i,tail[i+1:])
2、阶乘解法
思路分析:
阶乘即n!=1×2×3×...×n。阶乘亦可以递归方式定义:0!=1,n!=(n-1)!×n。
1、函数解法
#方法1 def fac(n): if n == 1: return 1 return n * fac(n-1) print(fac(4)) #方法2 def fac1(n,fac=1): #n=4 fac=1 #n=3,fac=4 #n=2,fac=12 #n=1 fac=24 if n == 1: #4 3 2 1 return fac #24 fac = fac * n # fac=4 #fac = 12 #fac=24 return fac1(n-1,fac) # fac(3,4) #fac(2,12) #fac(1,24) print(fac1(4))
2、for 循环解法
fac = 1 for a in range(1,5): fac *= a print(fac)
3、猴子吃桃
思路分析:
#假设猴子摘x个桃 d1 x //2 - 1 d2 d1//2 - 1 d3 d2//2 - 1 ... d9 d8//2 - 1 d10 1
#猴子偷桃 """ 猴子第一天摘下若干桃子,当即吃了一半,还不过瘾,又多吃了一个,第二天早上又将剩下的桃子吃掉一半,又多吃了一个。 以后每天早上又将剩下的桃子吃掉一半,又多吃一个。以后每天早上都吃前一天剩下的一半零一个。到第10天早上想吃的时候, 只剩下一个桃子,求第一天工摘多少桃子 """ def peach(days=10): if days == 1: return 1 return 2*(peach(days-1)+1) print(peach(days=10))
4、将一个数逆序放入列表中,例如:1234 -->[4,3,2,1]
#方法一 递归切片 def revert(x,data=[]): if x: data.append(x[-1]) revert(x[:-1]) return data print(revert("98adad123")) #方法二 使用数字整除取模递归 def revert(x, target=None): #x="123045" #12304 [5,] if target is None: target = [] #target = [] x, y = divmod(x, 10) # x=12304 y=5 # x=1230 y=4 target.append(y) #target = [5,] #[5,4] if x == 0: return target return revert(x, target) #12304 [5,] #1230 [5,4] print(revert(123045))
5、字典扁平化(主要练习递归)
源字典 {'a':{'b':1,'c':2}, 'd':{'e':3,'f':{'g':4}}} 目标字典 {'a.c': 2, 'd.e': 3, 'd.f.g': 4, 'a.b': 1}
src={"a":{"b":1,"c":2},"d":{"e":4,"f":{"g":3}}} def dst(src:dict,prekey="",targetdct = {}): for k,v in src.items(): if isinstance(v,dict): prekey=prekey + k + "." dst(v,prekey) prekey = "" else: targetdct[prekey + k] = v # return targetdct print(dst(src))
6、实现base64编码
Base64编码核心思想: 每3个字节断开,拿出一个3个字节,每6个bit断开成4段。 因为每个字节其实只占用了6 位, 2**6 = 64 ,因此有了base64的编码表。 每一段当做一个8bit看它的值,这个值就是Base64编码表的索引 值,找到对应字符。 再取3个字节,同样处理,直到最后。
举例: abc对应的ASCII码为:0x61 0x62 0x63 01100001 01100010 01100011 # abc 011000 010110 001001 100011 00011000 00010110 00001001 00100011 # 每6位补齐为8位 24 22 9 35
问题: 一个字节能变成几个Base64的字节? 两个字节能变成几个Base64的字节? 字符串'`'反引号如何处理? 末尾的处理? 1、正好3个字节,处理方式同上 2、剩1个字节或2个字节,用0补满3个字节 3、补0的字节用=表示
alphabet = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" def base64encode(src:str): ret = bytearray() if isinstance(src, str): _src = src.encode() else: return length = len(_src) r = 0 for offset in range(0, length, 3): triple = _src[offset:offset + 3] # 切片边界可以超界 # print(triple) if offset + 3 > length: r = 3 - len(triple) triple += b'\x00' * r # 便于计算先补零 b = int.from_bytes(triple, 'big') for i in range(18, -1, -6): if i == 18: index = b >> i else: index = b >> i & 0x3F # 0b11 1111 ret.append(alphabet[index]) # 索引查表 if r: ret[-r:] = b'=' * r # 从索引-r到末尾使用右边的多个元素依次替换 return bytes(ret) import base64 strlist = ['a', '`', 'ab', 'abc', 'abcd', "ManMa", '教育a'] for x in strlist: print(x) print(base64encode(x)) print()
7、求2个字符串的最长公共子串
思考: s1 = 'abcdefg' s2 = 'defabcd'
方法一:矩阵算法 让s2的每一个元素,去分别和s1的每一个元素比较,相同就是1,不同就是0,有下面的矩阵
s1 | |
s1 | 0001000 |
s1 | 0000100 |
s1 | 0000010 |
s1 | 1000000 |
s1 | 0100000 |
s1 | 0010000 |
s1 | 0001000 |
上面都是s1的索引。 看与斜对角线平行的线,这个线是穿过1的,那么最长的就是最长子串。 print(s1[3:3+3]) print(s1[0:0+4]) 最长
矩阵求法,需要一个字符扫描最长子串的过程,扫描的过程就是len(s1) * len(s2)次,O(n * m),这是必须的。 难 道还需要遍历一遍找出谁才是最长的吗?能够在求矩阵过程中就找出最长的子串吗? 0001000 第一行,索引为 (3,0)。 第二行的时候如果索引(4,1)处是1,就判断(3,0)处是否为1,为1就把(3,0)处加1。 第二行的时候如果索引 (5,2)处是1,就判断(4,1)处是否为1,是1就加1,再就判断(3,0)处是否为1,为1就把(3,0)加1。
s1 | |
s1 | 0003000 |
s1 | 0000200 |
s1 | 0000010 |
s1 | 4000000 |
s1 | 0300000 |
s1 | 0020000 |
s1 | 0001000 |
上面的方法是个递归问题,不好。 最后在矩阵中找到最大的元素,从它开始就能写出最长的子串了。 但是这个不 好算,因为是逆推的,改为顺推。
s1 | |
s1 | 0001000 |
s1 | 0000200 |
s1 | 0000030 |
s1 | 1000000 |
s1 | 0200000 |
s1 | 0030000 |
s1 | 0004000 |
顺推的意思,就是如果找到一个就看前一个的数字是几,然后在它的基础上加1。
def findstr(str1:str,str2:str): lenght1,lenght2=len(str1),len(str2) matrix = [[0]*lenght1 for i in range(lenght2)] #从x轴或者y轴取都可以,选择x轴,xmax和xindex xmax = 0 xindex = 0 for i,x in enumerate(str2): for j,y in enumerate(str1): if x != y: pass else: if i == 0 or j == 0 : matrix[i][j] = 1 else: matrix[i][j] = matrix[i-1][j-1] + 1 if matrix[i][j] >xmax: xmax = matrix[i][j] xindex = j start = xindex + 1 - xmax end = xindex + 1 print(matrix, xmax, xindex, start, end) return str1[start:end] s1 = 'abcdefg' s2 = 'defabcd' s2 = 'defabcdoabcdeftw' s3 = '1234a' s4 = "5678" s5 = 'abcdd' print(findstr(s1, s2)) print(findstr(s1, s3)) print(findstr(s1, s4)) print(findstr(s1, s5))
方法二:可不可以这样思考? 字符串都是连续的字符,所以才有了下面的思路。
思路一: 第一轮 从s1中依次取1个字符,在s2查找,看是否能够找到子串。 如果没有一个字符在s2中找到,说明 就没有公共子串,直接退出。如果找到了至少一个公共子串,则很有可能还有更长的公共子串,可以进入下一轮。
第二轮 然后从s1中取连续的2个字符,在s2中查找,看看能否找到公共的子串。如果没找到,说明最大公共子串就 是上一轮的随便的哪一个就行了。如果找到至少一个,则说明公共子串可能还可以再长一些。可以进入下一轮。
改进 其实只要找到第一轮的公共子串的索引,最长公共子串也是从它开始的,所以以后的轮次都从这些索引位置开 始,可以减少比较的次数。
思路二: 假设s1、s2两个字符串,s1短一些。 既然是求最大子串,最长子串不会超过最短的字符串,先把s1全长 作为子串。 在s2中搜索,是否返回正常的index,正常就找到了最长子串。
没有找到,把s1按照length-1取多个子串。 在s2中搜索,是否能返回正常的index。
注意: 不要一次把s1的所有子串生成,用不了,也不要从最短开始,因为题目要最长的。 但是也要注意,万一他 们的公共子串就只有一个字符,或者很少字符的,思路一就会占优势。
s1 = 'abcdefg' s2 = 'defabcdoabcdeftw' s3 = '1234a' def findstr(str1:str,str2:str): count = 0 # 计数,看看效率 if len(str2) < len(str1): # str2更短 str1, str2 = str2, str1 length = len(str1) for sublen in range(length, 0, -1): for start in range(0, length - sublen + 1): substr = str1[start:start + sublen] # 切割子串 count += 1 if str2.find(substr) > -1: # found print("count={}, substrlen={}".format(count, sublen)) return substr print(findstr(s1,s2)) print(findstr(s1,s3))
8、实现COPY函数
指定一个源文件,实现copy到目标目录
例如把/tmp/test.txt 复制到 /tmp/test1.txt
file1="/tmp/test.txt" file2="/tmp/test1.txt" f = open(file1,"w") lines=["abc","123","jaxzhai"] f.writelines("\n".join(lines)) #f.seek(0) #print(f.read()) f.close() def copy(src,dest): with open(src) as f1: with open(dest,"w") as f2: f2.write(f1.read()) copy(file1,file2)
9、单词统计
有一个文件,对其进行单词统计,不区分大小写,并显示单词重复最多的10个单词
#方法1
filename = 'sample.txt' d = {} with open(filename, encoding='utf8') as f: for line in f: words = line.split() for word in map(str.lower, words): d[word] = d.get(word, 0) + 1 print(sorted(d.items(), key=lambda item: item[1], reverse=True)) # 或使用缺省字典 from collections import defaultdict d = defaultdict(lambda :0) with open(filename, encoding='utf-8') as f: for line in f: words = line.split() for word in map(str.lower, words): d[word] += 1 print(sorted(d.items(), key=lambda item: item[1], reverse=True))
for k in d.keys(): # 从key里面看看还有好多带有path的 if k.find('path') > -1: print(k)
使用上面的代码,就可以看到path非常多 os.path.exists(path) 可以认为含有2个path
# 思路:遇到特殊字符,就用空格替代
def makekey(s:str): chars = set(r"""!'"#./\()[],*-""") key = s.lower() ret = [] for i, c in enumerate(key): if c in chars: ret.append(' ') else: ret.append(c) return ''.join(ret).split() d = {} with open('sample', encoding='utf8') as f: for line in f: words = line.split() for wordlist in map(makekey, words): for word in wordlist: d[word] = d.get(word, 0) + 1 for k,v in sorted(d.items(), key=lambda item: item[1], reverse=True): print(k,v)
分割key的另一种思路
def makekey1(s:str): #[os.path ] chars = set(r"""!'"#./\()[],*-""") key = s.lower() ret = [] start = 0 for i, c in enumerate(key): # if c in chars: if start == i: # 如果紧挨着还是特殊字符,start一定等于i start += 1 # 加1并continue continue ret.append(key[start:i]) start = i + 1 # 加1是跳过这个不需要的特殊字符c else: if start < len(key): # 小于,说明还有有效的字符,而且一直到末尾 ret.append(key[start:]) return ret d = {} with open(filename, encoding='utf8') as f: for line in f: #11.2. os.path — Common pathname manipulations words = line.split() #[..,os.path,..] for wordlist in map(makekey1, words): #[os,path] for word in wordlist: d[word] = d.get(word, 0) + 1 for k, v in sorted(d.items(), key=lambda item: item[1], reverse=True): print(k, v)