杨辉三角+优化算法
1 # 杨慧三角基本实现方式 2 # triangle = [[1],[1, 1]] # 将所有的结果放在一个大列表中 3 4 # for i in range(2, 6): # 因为已经有两个了,索引从 2 开始 5 # pre = triangle[i - 1] # 上一个大列表中的 元素 6 # cur = [1] # 当前大列表中的 元素,定义新的元素,并且首位补1 7 # for j in range(i - 1): # 对上一个大列表中的元素循环计算,得到当前列表中的 元素(通过循环得到循环次数) 8 # cur.append(pre[j] + pre[j + 1]) # 上一个元素中前一项 + 后一项,就是当前元素 9 # cur.append(1) # 尾部补 1 10 # triangle.append(cur) 11 # print(triangle) 12 13 # 变体: 14 15 # triangle = [[1]] # 将所有的结果放在一个大列表中 16 17 # for i in range(1, 6): # 因为已经有两个了,索引从 2 开始 18 # pre = triangle[i - 1] # 上一个大列表中的 元素 19 # cur = [1] # 当前大列表中的 元素,定义新的元素,并且首位补1 20 # for j in range(i - 1): # 对上一个大列表中的元素循环计算,得到当前列表中的 元素(通过循环得到循环次数) 21 # cur.append(pre[j] + pre[j + 1]) # 上一个元素中前一项 + 后一项,就是当前元素 22 # cur.append(1) # 尾部补 1 23 # triangle.append(cur) 24 # print(triangle) 25 26 # 再变体 27 # triangle = [] # 将所有的结果放在一个大列表中 28 29 # for i in range(6): # 因为已经有两个了,索引从 2 开始 30 # cur = [1] # 当前大列表中的 元素,定义新的元素,并且首位补1 31 # triangle.append(cur) 32 # if i == 0: continue 33 # pre = triangle[i-1] 34 # for j in range(i - 1): # 对上一个大列表中的元素循环计算,得到当前列表中的 元素(通过循环得到循环次数) 35 # cur.append(pre[j] + pre[j + 1]) # 上一个元素中前一项 + 后一项,就是当前元素 36 # if i > 0: 37 # cur.append(1) # 尾部补 1 38 39 # print(triangle)
1 # 补零法 2 # # NO 1 只补一个:右侧补零 3 # 1 0 4 # 1 1 0 # 上一位的最后一项 即-1 位置 + 0 位置 5 # 1 2 1 0 6 # 1 3 3 1 0 7 # triangle = [[1],[1, 1]] 8 9 # for i in range(2, 5): 10 # pre = triangle[i - 1] + [0] # 注 ,这里不能用append() ,因为返回None 11 # cur = [] 12 # for j in range(i + 1): # 当i = 2 时,测试得需要循环 3 次,所以 i + 1 13 # cur.append(pre[j - 1] + pre[j]) # j = 0 ,pre[-1] + pre[0] j = 1 ,pre[0] + pre[1] 14 # triangle.append(cur) 15 # print(triangle) 16 17 18 # triangle = [[1]] 19 # for i in range(1, 5): 20 # pre = triangle[i - 1].copy() # 浅拷贝 21 # pre.append(0) # 补零 22 # cur = [] # 如果不需要之前的结果,这里用cur.clear() ,否则内存空间产生很多的垃圾。 23 24 # for j in range(i + 1): # 等价 while offset <= i 25 # cur.append(pre[j - 1] + pre[j]) # cur.append(pre[offset - 1] + pre[offset]) 26 # triangle.append(cur) 27 # print(triangle) 28 29 30 31 # ------------------------------------------------------------------------------------- 32 33 34 # # NO 2 两侧补零 35 # 0 1 0 36 # 0 1 1 0 # 上一位 位置0 + 位置 1 37 # 0 1 2 1 0 38 # 0 1 3 3 1 0 39 40 # triangle = [[1],[1, 1]] 41 # for i in range(2, 5): 42 # pre = [0] + triangle[i - 1] + [0] # [0,1,1,0] 43 # cur = [] 44 # for j in range(i + 1):# i = 2 , 0.1 45 # cur.append(pre[j]+ pre[j+1]) #j = 0 pre[0]+pre[1, j = 1 pre[1] + pre[2] 46 # triangle.append(cur) 47 # print(triangle) 48 49 50 # # 变形 51 # triangle = [[1]] 52 # for i in range(1, 5): 53 # pre = [0] + triangle[i - 1] + [0] # [0,1,1,0] 54 # cur = [] 55 # for j in range(i + 1):# i = 2 , 0.1 56 # cur.append(pre[j]+ pre[j+1]) #j = 0 pre[0]+pre[1, j = 1 pre[1] + pre[2] 57 # triangle.append(cur) 58 # print(triangle) 59 60 # # 再变形 61 # triangle = [] 62 # for i in range(5): 63 # if i == 0: 64 # cur = [1] 65 # triangle.append(cur) 66 # continue 67 # pre = [0] + triangle[i - 1] + [0] 68 # cur = [] 69 # for j in range(i + 1): 70 # cur.append(pre[j]+ pre[j+1]) 71 # triangle.append(cur) 72 # print(triangle)
1 # 对称法: 2 # 1 3 # 1 1 4 # 1 2 1 i = 2 计算 1 次 2//2 奇数行 中间的数 j=1 i=2 i=2j 5 # 1 3 3 1 i = 3 1 3//2 6 # 1 4 6 4 1 i = 4 2 4//2 奇数行 中间的数 j=2 i=4 i=2j 7 # 1 5 10 10 5 1 i = 5 2 5//2 8 # 把所有行都保存了,这样往后越来越占空间 9 # n = 4 10 # triangle = [[1],[1,1]] 11 12 # for i in range(2,8):# 2 3 4 13 # # cur = [1] 14 # # for j in range(i): 15 # # cur.append(1 if j == i-1 else 0) 16 # cur = [1] * (i + 1) # i = 2,cur=[1,1,1] i=3 cur=[1,1,1,1] i=4 cur=[1,1,1,1,1] 17 # pre = triangle[i - 1] # pre=[1,1] pre=[1,2,1] pre [1,3,3,1] 18 19 # for j in range(i // 2): # j = 0 j= 0 j=0,1 20 # val = pre[j] + pre[j + 1] # pre[0]+pre[1] 4 pre[1]+pre[2] 6 21 # cur[j + 1] = val # 覆盖第二项 22 # # if i != 2 * j: # 跳过 i = 2*j 的一项 23 # cur[-j - 2] = val # cur[-2]=2 cur[-3] = 6 覆盖之前的值 24 # triangle.append(cur) 25 # print(triangle) 26 27 # i = 2 1 2 1 0,1,2 28 # i = 3 1 3 3 1 0.1.2.3 j=[0] 后面 -1 29 # I = 4 1 4 6 4 1 j=[0,1] 后面 -2 -1 -j-1 30 31 32 33 34 35 # 优化:降低空间复杂度(单行覆盖) 36 37 # 单行覆盖法 38 # n = 5 39 # row = [1] * n 40 41 # for i in range(n): 42 # offset = n - i # 比如n = 4,i = 3,n-i 就是去除左侧用不到的 43 # z = 1 44 # for j in range(i//2):# i=2,j=0 # i=3,j=0 45 # val = z + row[j + 1] # val=1+row[1] # val=1+row[1]=3 46 47 # # 记录上一该位置的值,比如 i = 3时,如果不记录,会被val覆盖 48 # # 此时计算下一个,3 = 3 + 1而不是 2 + 1 49 # z = row[j + 1] # z=row[1] # z=row[1]=2 50 51 # row[j + 1] = val # row[1]=2 #row[1]=2 52 # if i != 2 * j:# 2!=0 # 3!=0 53 # row[-j - 1 - offset] = val # row[-4]=2 # row[-3]=3 54 55 # print(row[:i + 1]) 56 57 58 # 1 1 1 1 1 59 # 1 1 1 1 1 ---- 1 2 1 1 1 --- 60 # 1 2 1 1 1 61 # 1 3 3 1 1
1 ''' 2 求杨辉三角某一个的某个值 3 思路1:使用之前的办法,将三角求出来,在找某行的某个值 4 思路2: 利用数学公式,即是组合数的值(二项式展开式的系数,(a + b)** n) 5 6 ''' 7 # NO:1 8 # 两行来处理 ,这是又一种求杨慧三角的算法,通过两行相互交替 9 # m = 6 10 # k = 2 11 12 # oldline = [] 13 # for i in range(m): 14 # newline = [1] * (i + 1)# 3 [1,1,1] [1,1] 15 16 # if i < 2: 17 # oldline = newline 18 # continue 19 20 # for j in range(i - 1): # 0,1 21 # newline[j + 1] = oldline[j] + oldline[j + 1] 22 # oldline = newline 23 # print(newline) 24 # print(newline[k-1]) 25 26 # 优化: 27 # m = 6 28 # k = 2 29 30 # oldline = [] 31 # for i in range(m): 32 # newline = [1] * (i + 1) 33 # for j in range(i - 1): 34 # newline[j + 1] = oldline[j] + oldline[j + 1] 35 # oldline = newline 36 # print(newline) 37 # print(newline[k-1]) 38 39 40 41 # NO.2 42 # 利用组合公式 C(n,m) = n!/(m!*(n-m)!) 43 # m = 6 44 # k = 2 45 46 # n = m - 1 # 这块可以不写,主要分析用 47 # r = k - 1 48 # d = m - k 49 50 # f = 1 51 # offset = [] # 用来放三个结果 52 53 # for i in range(1, n + 1): # 每一行都是从第一个数 1 开始 54 # f *= i 55 # 因为分子部分肯定包含了分子,所以算到分子的时候记录下来,知道算到分子大小的阶乘位置 56 # if i == (m - k): 57 # offset.append(f) 58 # if i == (k - 1): 59 # offset.append(f) 60 # if i ==(m - 1): 61 # offset.append(f) 62 # print(int(offset[2]/(offset[1] * offset[0])))
总结:
基本实现方式的编程思想:
1、对于杨慧三角,前两行比较特殊,先把前两行提取出来。
2、利用大列表,将所有的数据放在该列表中。
3、利用列表的性质,控制上一行和当前行
4、首尾补 1
5、用到双层for循环,第一层控制行,第二层控制运算。
6、对于规律性的问题,先测试前几行,测试通过后,在测试后续基本没有大问题,结构需要在调整一下。
7、变体,即结构的调整,尽量将特殊行也加入到循环中,注意是否需要条件判断这些特殊行
8、注意使用一些简化结构:if i == 0: continue
补零法:
1、分析杨慧三角,可以看出,上一行前后相加,,就是下一行的值,而首尾都是1,所以可以考虑首尾补零的方法。
2、补零可以有两种,首尾都补,一侧补
3、补零,注意虽然使用列表,但是注意列表返回值是否是None,还是新列表。
4、这里使用了+ 或浅拷贝追加0 两种方式
5、cur = [] 如果不需要之前的结果,这里用cur.clear() ,否则内存空间产生很多的垃圾。
6、for j in range(i + 1):
cur.append(pre[j - 1] + pre[j])
# 等价 while offset <= i
# cur.append(pre[offset - 1] + pre[offset])
对称法 and 单行覆盖法:
1、 对结果分析可以看出,只需要计算左侧的即可,所以考虑使用对称法
2、这里用到的主要思想是:
- 直接开辟一定的空间,上面的方法,每次都把之前的保存下来,按照需求,不需要,所以直接操作同一个列表即可
- 开辟一定大小的空间,对于杨慧三角来说,通过结果规律发现,首尾都是1,中间其他数字,所以可以开辟这样的空间:首尾为1,其他位为0.或者最好的方式是都为1,产生新的数值,覆盖原有的数字即可。
# # cur = [1]
# # for j in range(i):
# # cur.append(1 if j == i-1 else 0)
or
# # cur = [1] * (i + 1) # i = 2,cur=[1,1,1]
3、计算时,只计算左侧一般,而且有奇数行,会有一个中间数,此时 i = 2j, 因为要对称,所以算完前半部分,在跳过这个值(先判断不等于后),在利用列表索引赋值。
4、注意点:对单行运算,前一次的运算结果,会覆盖原来位置的值,所以需要一个临时变量接受这个值,以便下次计算使用。
注:要时刻注意内存的使用,当内存被占用到一定的阈值,就会出道GC 对内存清理,此时程序会暂停,降低了运行效率,所以根据具体情况使用列表,是一次性给,还是用一次删一次。