数据结构与算法--Python实现递归经典问题:Python实现迷宫回溯问题 Python实现八皇后问题

迷宫问题

  • 如下图:小球必须避开红色墙体移动,直到找到箭头所指的出口,请用代码实现
    在这里插入图片描述

思路

利用递归和回溯,递归的终止条件是假设已经移动到了出口位置,上图中箭头位置即是maze_map[6][5];在此基础上,约定从maze_map[1][1] 出发,按照:下 右 上 左 的行进策略移动小球,然后假设 第一个点是没有走过的,即它为0,然后假设 当前位置能走同,即标为2 那么 在2的位置 它可以走四个方向,先试探第一个约定的位置:向下走,此时用递归,不断去找路,如果能走通都标为2,直到走不通则原路返回,返回到第一个方向的递归处,再来试探 右,以此类推,直到能走到maze_map[6][5]位置!

Python实现

class Maze(object):
    def __init__(self):
        self.r = 0  # 统计有多少行
        self.c = 0  # 统计有多少列

    # 先用 二维数组模拟一个迷宫,墙体设为1,上下全部置为1
    def print_maze(self, array_maze):  # 打印迷宫地图
        for row_item in array_maze:
            for col_item in row_item:
                print(col_item, end=" ")
            print()

    def create_maze(self):
        row = 8
        col = 7
        maze_map = [[0 for i in range(col)] for j in range(row)]
        self.r = len(maze_map)  # 有多少行 8
        self.c = len(maze_map[0])  # 有多少列 7
        # 上下全部置为1 即墙体
        for i in range(self.c):  # 这应该是7 取0-6的值,因为变得是列
            maze_map[0][i] = 1
            maze_map[self.r - 1][i] = 1
        # 左右全部置为1 即墙体
        for j in range(1, self.r):
            maze_map[j][0] = 1
            maze_map[j][self.c - 1] = 1
        # 设置挡板
        maze_map[3][1] = 1
        maze_map[3][2] = 1
        # maze_map[1][2] = 1
        # maze_map[2][2] = 1
        return maze_map

    # 利用递归和回溯为小球寻找路径
    # 约定,row,col,表示从地图的哪个位置出发,约定从(1,1)出发,到(6,5)结束,则找到通路
    # 当:maze_map [row][col] 为0 表示该点没有走过,为1则为墙,2表示通路可以走,3表示该路已经走过,但是走不通
    # 策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
    def find_way(self, maze_map, row, col):
        """
        :param maze_map: 传入一张地图
        :param row: 位置
        :param col: 位置
        :return: 如果找到通路就返回True,否则返回False
        """
        if maze_map[self.r - 2][self.c - 2] == 2: # 最后一个位置如果2为通路,即递归的出口
            return True
        else:
            if maze_map[row][col] == 0:  # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
                maze_map[row][col] = 2  # 假定该点是可以走通的
                if self.find_way(maze_map, row + 1, col):  # 向下走,行加1
                    return True
                elif self.find_way(maze_map, row, col + 1):  # 走不通,尝试向右走
                    return True
                elif self.find_way(maze_map, row - 1, col):  # 走不通,尝试向上走
                    return True
                elif self.find_way(maze_map, row, col - 1):  # 走不通,尝试向左走
                    return True
                else:  # 尝试后,改点走不通,是死路,所以置为3
                    maze_map[row][col] = 3
                    return False
            else:  # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
            	# 因为1是墙不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
                return False


if __name__ == '__main__':
    obj = Maze()
    get_map = obj.create_maze()
    obj.print_maze(get_map)  # 输出第一次制作的地图
    print("<===========>")
    obj.find_way(get_map, 1, 1)
    obj.print_maze(get_map)  # 输出策略:下 右 上 左 策略的地图
    print("<===========>")
    obj.find_way(get_map, 1, 1)

在这里插入图片描述

体现该过程中 产生回溯:放开create_maze方法中的“ maze_map[1][2] = 1 ,maze_map[2][2] = 1”
即多设置两面墙,那么还是从同样的位置出发,发现走不通,则都设为3

在这里插入图片描述

如何找出最短路径

  • 最短路径是 根据最开始的策略来判断的,所以我们只需要改变行进策略再求解,就能得出了
  • 前提和递归方式都不需要变,解决改变发生递归的行进方向,如下面实现:行进策略为:上 右 下 左
class Maze(object):
    def __init__(self):
        self.r = 0  # 统计有多少行
        self.c = 0  # 统计有多少列

    # 先用 二维数组模拟一个迷宫,墙体设为1,上下全部置为1
    def print_maze(self, array_maze):  # 打印迷宫地图
        for row_item in array_maze:
            for col_item in row_item:
                print(col_item, end=" ")
            print()

    def create_maze(self):
        row = 8
        col = 7
        maze_map = [[0 for i in range(col)] for j in range(row)]
        self.r = len(maze_map)  # 有多少行 8
        self.c = len(maze_map[0])  # 有多少列 7
        # 上下全部置为1 即墙体
        for i in range(self.c):  # 这应该是7 取0-6的值,因为变得是列
            maze_map[0][i] = 1
            maze_map[self.r - 1][i] = 1
        # 左右全部置为1 即墙体
        for j in range(1, self.r):
            maze_map[j][0] = 1
            maze_map[j][self.c - 1] = 1
        # 设置挡板
        maze_map[3][1] = 1
        maze_map[3][2] = 1
        # maze_map[1][2] = 1
        # maze_map[2][2] = 1
        return maze_map

    # 利用递归和回溯为小球寻找路径
    # 约定,row,col,表示从地图的哪个位置出发,约定从(1,1)出发,到(6,5)结束,则找到通路
    # 当:maze_map [row][col] 为0 表示该点没有走过,为1则为墙,2表示通路可以走,3表示该路已经走过,但是走不通
    # 策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
    def find_way(self, maze_map, row, col):
        """
        :param maze_map: 传入一张地图
        :param row: 位置
        :param col: 位置
        :return: 如果找到通路就返回True,否则返回False
        """
        if maze_map[self.r - 2][self.c - 2] == 2:
            return True
        else:
            if maze_map[row][col] == 0:  # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
                maze_map[row][col] = 2  # 假定该点是可以走通的
                if self.find_way(maze_map, row + 1, col):  # 向下走,行加1
                    return True
                elif self.find_way(maze_map, row, col + 1):  # 走不通,尝试向右走
                    return True
                elif self.find_way(maze_map, row - 1, col):  # 走不通,尝试向上走
                    return True
                elif self.find_way(maze_map, row, col - 1):  # 走不通,尝试向左走
                    return True
                else:  # 否则改点走不通,是死路,所以置为3
                    maze_map[row][col] = 3
                    return False
            else:  # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
                # 因为1是墙可以不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
                return False
                
 	# 新策略:行进的方向:下 右 上 左 ,如果该点走不通则回溯
    def find_new_way(self, maze_map, row, col):
        """
        :param maze_map: 传入一张地图
        :param row: 位置
        :param col: 位置
        :return: 如果找到通路就返回True,否则返回False
        """
        if maze_map[self.r - 2][self.c - 2] == 2:
            return True
        else:
            if maze_map[row][col] == 0:  # 如果当前这个点还没有走过,那按照策略:下 右 上 左 走
                maze_map[row][col] = 2  # 假定该点是可以走通的
                # 特别注意,是新方法的递归 ↓↓↓self.find_new_way
                if self.find_new_way(maze_map, row - 1, col):  # 向上走,行减1
                    return True
                elif self.find_new_way(maze_map, row, col + 1):  # 走不通,尝试向右走
                    return True
                elif self.find_new_way(maze_map, row + 1, col):  # 走不通,尝试向下走
                    return True
                elif self.find_new_way(maze_map, row, col - 1):  # 走不通,尝试向左走
                    return True
                else:  # 否则改点走不通,是死路,所以置为3
                    maze_map[row][col] = 3
                    return False
            else:  # 如果maze_map[row][col]!=0 ,则可能是 1,2,3;
                # 因为1是墙可以不考虑,2是通路不需要重复,剩下3是死路直接返回False即可
                return False


if __name__ == '__main__':
    obj = Maze()
    get_map = obj.create_maze()
    obj.print_maze(get_map)  # 输出第一次制作的地图
    # print("<===========>")
    # obj.find_way(get_map, 1, 1)
    # obj.print_maze(get_map)  # 输出策略:下 右 上 左 策略的地图
    print("<===========>")
    obj.find_new_way(get_map, 1, 1)
    obj.print_maze(get_map)  # 输出策略:上 右 下 左 策略的地图

在这里插入图片描述

八皇后问题

思路

Python实现

汉诺塔问题

思路

  • 概述:
    在这里插入图片描述
  • 回溯思路
    在这里插入图片描述
    在这里插入图片描述
    用一维数组的解释:对array 的 索引+1 表示第几行,也即是第几个皇后,array[i]=val,这个val值表示第i+1个皇后,放在i+1列,所以arr[8]={0,4,7,5,2,6,1,3} 表示 第一个皇后,放在第一行第一列的位置,第二个皇后放在第二行第五列的位置,第三个皇后放在第三行第八列的位置…

Python实现

  • 先补充一些小知识点
    (1)如何创建有一定长度和初始值的列表
array = [None]*8 
print(len(array)) # 8

(2)Python中求绝对值的方法

# 三种方法求绝对值
import math
 # 条件判断
def abs_value1():
    a = float(input('1.请输入一个数字:'))
    if a >= 0:
        a = a
    else:
        a = -a
    print('绝对值为:%f' % a)
    
 # 用abs
def abs_value2():
    a = float(input('2.请输入一个数字:'))
    a = abs(a)
    print('绝对值为:%f' % a)
 
 # 内置函数
def abs_value3():
    a = float(input('3.请输入一个数字:'))
    a = math.fabs(a)
    print('绝对值为:%f' % a)
 
abs_value1()
abs_value2()
abs_value3()

完整实现 八皇后问题

class EightQueen(object):
    def __init__(self, max_queen=8):
        self.max_queen = max_queen  # 皇后的数量
        self.array = [0] * self.max_queen  # 创建一个带长度的一维数组
        self.count = 0  # 用来统计
        self.judge_count = 0  # 用来统计判断冲突的次数

    def check(self, n):  # 放置第n个皇后的方法;检查它后方的是否满足
        # check 是每一次递归时,进入check中都有for i in range(self.max_queen),因此会有回溯
        if n == self.max_queen:  # n=8 其实8个皇后已经放好了,注意下标是从0开始的
            self.print_queen()
            return
        # 依次放入皇后并判断是否冲突
        for i in range(self.max_queen):  # 总共放8个皇后,总共8行
            # 先把当前的皇后n放到该行的第i列,即初始的皇后位置为:第一行第一列
            self.array[n] = i
            # 判断当放置第n个皇后到i列时(它前面的),是否冲突
            if self.judge(n):  # 不冲突
                # 接着放第n+1个皇后,即开始递归
                self.check(n + 1)
            # 如果冲突,就回去执行self.array[n] = i,i会自动加等于1,等于自动换一列

    def judge(self, n):  # 查看当放置第n个皇后,去检测该皇后是否和前方已经摆放的皇后冲突
        '''
        :param n: 表示第n个皇后
        :return: boolean
        '''
        self.judge_count += 1  # 判断调用了几次
        for i in range(n):
            # self.array[i] == self.array[n] 判断第n个皇后是否和前面n-1皇后在同一列
            # abs(n - i) == abs(self.array[n] - self.array[i]) 判断第n个皇后是否和第i个皇后在同一斜线
            # i是不断在变化的,只有当i变化到刚好是n前一个时,才可能出现在同一斜线情况
            if self.array[i] == self.array[n] or abs(n - i) == abs(self.array[n] - self.array[i]):
                return False
        return True  # 注意是所有的都检查完

    def print_queen(self):  # 输出皇后摆放的位置
        self.count += 1  # 看它调了多少次,就有多少种结果
        for index in range(len(self.array)):
            print(self.array[index], end=" ")
        print("")


if __name__ == '__main__':
    obj = EightQueen()
    obj.check(0)
    print("总共有:%d 种解法" % obj.count)
    print("总共判断了:%d 次冲突" % obj.judge_count)
  • 下面是结果的一部分,总共有92种解法
    在这里插入图片描述

    在这里插入图片描述

简化版

def queen(A, cur=0):
    if cur == len(A):
        print(A)
        return 0
    for col in range(len(A)):
        A[cur], flag = col, True
        for row in range(cur):
            if A[row] == col or abs(col - A[row]) == cur - row:
                flag = False
                break
        if flag:
            queen(A, cur + 1)


queen([None] * 8)
发布了146 篇原创文章 · 获赞 37 · 访问量 7865

猜你喜欢

转载自blog.csdn.net/storyfull/article/details/103645333