``
4.4 递归以及分治回溯算法
什么是递归
从语法的角度而言,递归其实就是函数调用函数自身的一种语法结构
从内存的角度而言,递归其实就是函数帧依次进栈的过程(栈内存是有限的)
当函数栈内存,存不下函数帧的时候,就会出现栈内存溢出
错误
def show01():
print("This is show method")
show01()
def show02(n):
if n == 1:
print("n = ", n)
print("end....")
else:
print("n = ", n)
show02(n - 1)
return
show02(5)
show01()
当我们在写递归的时候,先考虑递归的边界条件
,递归的前进段:就是递的过程
,递归的返回段:就是归的过程
递归能干啥
递归它也是一种解决问题的思路,我们之前大部分迭代(循环)的代码都可以使用递归来解决
但是用递归写的代码不一定能用循环来解决!
相对而言,迭代的代码更加通俗易懂但是繁杂,递归的代码更加晦涩难懂但是简洁
当我们在去解决一些大型问题的时候,通常使用递归来做的
一句话:同样的问题,同样的解决思路,递归的代码要少于迭代的代码,但是递归代码的可读性没有迭代代码的可读性高
累加的问题
"""
累加问题
从1+2+3+4+...+98+99+100
"""
# 迭代
def getSum(n):
sum = 0
for num in range(1,n + 1):
sum += num
return sum
print(getSum(100))
# 递归
"""
f(x) = 1 + 2 + 3 + 4 +... + x-2 + x-1 + x
f(100) = 1+2+3+4+...+98+99+100
f(99) = 1+2+3+4+...+98+99
f(100) = f(99) + 100 (1+2+3+...+99) + 100
f(99) = f(98) + 99
f(98) = f(97) + 98
...
f(4) = f(3) + 4 (1+2+3) + 4
f(3) = f(2) + 3 (1+2) + 3
f(2) = f(1) + 2 (1) + 2
f(1) = 1
f(x-1) + x,x>1
f(x)
1, x==1
"""
def getSumRercusion(n):
if n == 1:
return 1
else:
return getSumRercusion(n - 1) + n
print(getSumRercusion(100))
斐波那契数列的问题
"""
斐波那契数列问题
1 1 2 3 5 8 13 21 34 55 ...
求斐波那契数列当中第n项的值
"""
# 迭代
def fibonacci(n):
if n == 1 or n == 2:
return 1
a = 1
b = 1
c = 0
for i in range(3, n + 1):
c = a + b
a = b
b = c
return c
print(fibonacci(35))
# 迭代-列表
def fibonacciList(n):
if n == 1 or n == 2:
return 1
arr = [0] * n
arr[0] = 1
arr[1] = 1
for i in range(2,n):
arr[i] = arr[i - 1] + arr[i - 2]
return arr[n - 1]
print(fibonacciList(35))
# 递归
"""
f(x) = f(x - 1) + f(x - 2)
f(5) = f(4) + f(3)
f(4) = f(3) + f(2)
f(3) = f(2) + f(1)
f(2) = 1
f(1) = 1
f(x-1) + f(x - 2) , x > 2
f(x)
1 , x == 1 or x == 2
"""
def fibonacciRecursion(n):
if n == 1 or n == 2:
return 1
else :
return fibonacciRecursion(n - 1) + fibonacciRecursion(n - 2)
print(fibonacciRecursion(35))
什么是分治算法
分治算法指的是一种算法思想,并没有固定的代码套路
分治:分而治之
当我们在处理一个大规模问题的时候,通过分析,我们可以看到这个大规模的问题可以划分为若干个较小规模的问题,这些若干个较小规模的问题又可以接着向下划分为若干个更小的问题,依次类推,直到该问题不能再划分为止。其次是,无论我们划分成多么小的问题,对于小问题的求解方式和大问题的求解方式是一致的话,就可以使用分治的思想来解决。将若干个小问题处理的结果进行合并,合并的结果就是最初大规模问题的结果。
向下划分问题的过程:递归当中递的过程
向上合并结果的过程:递归当中归的过程
二分查找
def binarySearchRecursion(arr,L,R,key):
if L > R:
return -1
M = (L + R) // 2
if arr[M] == key:
return M
elif arr[M] < key:
return binarySearchRecursion(arr, M + 1, R, key)
else:
return binarySearchRecursion(arr, L, M - 1, key)
arr = [1,2,3,4,5,6,7,8,9]
key = 8.5
index = binarySearchRecursion(arr,0, len(arr) - 1,key)
print(index)
插值查找
def interploationSearch(arr,low,high,key):
if low > high:
return -1
M = int((key - arr[low]) / (arr[high] - arr[low]) * (high - low)) + low
print("interpolation M = ", M)
if M < low or M > high:
return -1
if arr[M] == key:
return M
elif arr[M] < key:
return interploationSearch(arr, M + 1, high, key)
else:
return interploationSearch(arr, low, M - 1, key)
arr = [666]
for i in range(1,10000):
arr.append(random.randint(1,20000))
arr.sort()
key = 666
index = binarySearch(arr,0, len(arr) - 1,key)
print(index)
index = interploationSearch(arr,0,len(arr) - 1, key)
print(index)
汉诺塔问题
"""
前4个 X->Z
前3个 X->Y
前2个 X->Z
前1个 X->Y 1
第2个 X->Z 2
前1个 Y->Z 3
第3个 X->Y 4
前2个 Z->Y
前1个 Z->X 5
第2个 Z->Y 6
前1个 X->Y 7
第4个 X->Z 8
前3个 Y->Z
前2个 Y->X
前1个 Y->Z 9
第2个 Y->X 10
前1个 Z->X 11
第3个 Y->Z 12
前2个 X->Z
前1个 X->Y 13
第2个 X->Z 14
前1个 Y->Z 15
"""
def hanoi(level, start, mid, end):
if level == 1:
print(start + "->" + end)
else:
"""
第n-1层的起点是第n层的起点
第n-1层的终点是第n层的过度
第n-1层的过度是第n层的重点
"""
hanoi(level - 1, start, end, mid)
print(start + "->" + end)
"""
第n-1层的起点是第n层的过度
第n-1层的终点是第n层的终点
第n-1层的过度是第n层的起点
"""
hanoi(level - 1, mid, start, end)
level = 3
hanoi(level,"X","Y","Z")
什么是回溯算法
回溯算法一般是和分治算法联合起来使用的
回溯算法其实本质上就是一种暴力破解法(就是进行有限的穷举,得到每一种可能的解),但它是基于暴力破解法之上进行了相对优化的内容(优化条件),自从有了优化条件就可以避免一些没有必要的计算,大大降低了暴力破解中穷举的次数-降低了时间。
回溯的过程其实就是一个不断向下求解的过程,如果在某一层面得不到一个正确的解,则无需向下执行而是返回至上一层,再去重新进行向下的求解,这个过程类似于在迷宫中找一条可以通的路径。
回溯只要在递归过程中,归的时候进行回溯,当回溯到某一层的时候,再接着向下递归,依次类推
全排列问题
def swap(arr,a,b):
arr[a], arr[b] = arr[b], arr[a]
def fullPermutation(arr, start, end, lst):
if start == end:
s = ""
for c in arr:
s += c
lst.append(s)
else:
for i in range(start, end + 1):
swap(arr,start,i)
fullPermutation(arr,start + 1, end,lst)
swap(arr,start,i)
s = "ABB"
arr = list(s)
lst = []
fullPermutation(arr,0,len(arr) - 1,lst)
lst = list(set(lst))
print(lst)
八皇后问题
def isSafe(row,col,board):
global size
# 正上方
r = row - 1
while r >= 0:
if board[r][col] == 1:
return False
r -= 1
# 左上方
r = row - 1
c = col - 1
while r >= 0 and c >= 0:
if board[r][c] == 1:
return False
r -= 1
c -= 1
# 右上方
r = row - 1
c = col + 1
while r >= 0 and c < size:
if board[r][c] == 1:
return False
r -= 1
c += 1
return True
def eightQueen(board,row):
global size,count
if row == size:
count += 1
print("第%d种解:" % (count))
for i in range(0,size):
for j in range(0,size):
print(board[i][j],end = " ")
print()
else:
for col in range(0,size):
if isSafe(row,col,board):
for c in range(0, size):
board[row][c] = 0
board[row][col] = 1
eightQueen(board, row + 1)
count = 0
size = 4
board = [None] * size
for i in range(0, size):
board[i] = [0] * size
eightQueen(board,0)
index)
index = interploationSearch(arr,0,len(arr) - 1, key)
print(index)
**汉诺塔问题**
“”"
前4个 X->Z
前3个 X->Y
前2个 X->Z
前1个 X->Y 1
第2个 X->Z 2
前1个 Y->Z 3
第3个 X->Y 4
前2个 Z->Y
前1个 Z->X 5
第2个 Z->Y 6
前1个 X->Y 7
第4个 X->Z 8
前3个 Y->Z
前2个 Y->X
前1个 Y->Z 9
第2个 Y->X 10
前1个 Z->X 11
第3个 Y->Z 12
前2个 X->Z
前1个 X->Y 13
第2个 X->Z 14
前1个 Y->Z 15
“”"
def hanoi(level, start, mid, end):
if level == 1:
print(start + “->” + end)
else:
“”"
第n-1层的起点是第n层的起点
第n-1层的终点是第n层的过度
第n-1层的过度是第n层的重点
“”"
hanoi(level - 1, start, end, mid)
print(start + “->” + end)
“”"
第n-1层的起点是第n层的过度
第n-1层的终点是第n层的终点
第n-1层的过度是第n层的起点
“”"
hanoi(level - 1, mid, start, end)
level = 3
hanoi(level,“X”,“Y”,“Z”)
> 什么是回溯算法
回溯算法一般是和分治算法联合起来使用的
回溯算法其实本质上就是一种暴力破解法(就是进行有限的穷举,得到每一种可能的解),但它是基于暴力破解法之上进行了相对优化的内容(优化条件),自从有了优化条件就可以避免一些没有必要的计算,大大降低了暴力破解中穷举的次数-降低了时间。
回溯的过程其实就是一个不断向下求解的过程,如果在某一层面得不到一个正确的解,则无需向下执行而是返回至上一层,再去重新进行向下的求解,这个过程类似于在迷宫中找一条可以通的路径。
回溯只要在递归过程中,归的时候进行回溯,当回溯到某一层的时候,再接着向下递归,依次类推
**全排列问题**
def swap(arr,a,b):
arr[a], arr[b] = arr[b], arr[a]
def fullPermutation(arr, start, end, lst):
if start == end:
s = “”
for c in arr:
s += c
lst.append(s)
else:
for i in range(start, end + 1):
swap(arr,start,i)
fullPermutation(arr,start + 1, end,lst)
swap(arr,start,i)
s = “ABB”
arr = list(s)
lst = []
fullPermutation(arr,0,len(arr) - 1,lst)
lst = list(set(lst))
print(lst)
**八皇后问题**
def isSafe(row,col,board):
global size
# 正上方
r = row - 1
while r >= 0:
if board[r][col] == 1:
return False
r -= 1
# 左上方
r = row - 1
c = col - 1
while r >= 0 and c >= 0:
if board[r][c] == 1:
return False
r -= 1
c -= 1
# 右上方
r = row - 1
c = col + 1
while r >= 0 and c < size:
if board[r][c] == 1:
return False
r -= 1
c += 1
return True
def eightQueen(board,row):
global size,count
if row == size:
count += 1
print(“第%d种解:” % (count))
for i in range(0,size):
for j in range(0,size):
print(board[i][j],end = " ")
print()
else:
for col in range(0,size):
if isSafe(row,col,board):
for c in range(0, size):
board[row][c] = 0
board[row][col] = 1
eightQueen(board, row + 1)
count = 0
size = 4
board = [None] * size
for i in range(0, size):
board[i] = [0] * size
eightQueen(board,0)