精华内容
下载资源
问答
  • DFS bfs
    2021-08-25 21:50:11


    又水了一篇博客呜呜,第一次尝试写DFS和BFS,做题也迷迷糊糊,看着大佬文章简单写了写总结,后续会补上DFS和BFS的题目。

    BFS DFS介绍

    DFS:深度优先搜索,又叫回溯算法。这个算法的核心就是不断的往更深的地方搜索,如果更深的地方搜索失败了,就返回来搜,是一个回溯的过程。
    BFS:广度优先搜索,也是一种搜索算法,讲究搜索的广度,所以叫广度优先算法。这个算法的核心就是,先把周围的找完,再去找更深的地方。

    这里区分下dfs和bfs,dfs就是一条路走到底,发现没路了,返回来,走另一条路,不撞南墙不回头。bfs就是每条路都走一点,这样一点点走总会走到想要的路。

    实现思路

    这两种算法都可以使用的递归的方式,其中dfs还可以用栈来实现,bfs还可以用队列实现。
    DFS和BFS用递归实现的思路:
    根据各自传参的优先级的顺序,不断的调用自身。
    一般情况下,用递归实现BFS比较为难,所以常用队列实现。
    DFS用栈实现的思路:
    每遇到一个元素,就把这个元素的所有邻接元素入栈,当栈不为空的时候,不断的从栈顶拿出元素进行操作,直到栈为空。
    BFS用队列实现的思路:
    每遇到一个元素,就把这个元素的所有邻接元素放入队列,当队列不为空的时候,不断从队首拿出元素进行操作,直到队列为空。

    DFS BFS怎么应用

    BFS类似于水波纹的扩散,在求解一些最短路径,最优值问题的时候效率很高。
    BFS为了遍历,需要保存所有的状态,对空间来说是一个巨大的消耗。(因为通常是无法递归实现的)
    而DFS再解决回溯问题上优势很大,通过一个状态变量搜索所有,对空间是很大的节约。(但递归对我这样新手不是很友好ww)
    所以一般情况DFS用递归写,BFS用队列写
    那么如何使用DFS BFS解决问题呢?
    凡是在一个二维矩阵中,找路径,找方法数,搜索东西,想都不要想,一般都可以用上。
    去遍历图肯定是可以的。
    看到有关找方法的问题,可以尝试使用。
    凡是包含搜索思想的,都可以使用。

    DFS BFS对比

    DFS : 使用递归的方式对于恢复状态非常方便,因为大部分问题在回溯后都需要恢复相应的状态。但是对于递归理解不好的话,就感到很困难
    BFS : 一般用队列,队列消耗空间很大,但使用非递归的方式,可能会更好理解,更直观。

    更多相关内容
  • 深度优先遍历和广度优先遍历算法实现从定点开始的遍历序列。
  • DFS BFS 搜索题目归纳

    2022-03-25 09:27:59
    DFS BFS 搜索
    数据结构空间是否具有最短路特性备注
    DFSstack O ( h ) O(h) O(h)通常要回溯、剪枝,对空间要求比较高的用DFS
    BFSqueue O ( 2 h ) O(2^h) O(2h)最少步数、最短距离、最少操作几次

    DFS

    深搜(dfs)框架
    参考:https://blog.csdn.net/weixin_45559159/article/details/104187523

    void dfs(int x)//关于传入参数问题,根据题意而定,看在程序运行的过程中,哪些是在变的
    {
        if(满足输出条件)
        {
            输出解;
            return ;
        }
        //剪枝操作
        if(目前已经没有必要进行下去的条件){
            return ;
        }
        //如果传入的条件,还需要继续搜下去,分析每一种情况后面跟哪些情况,然后循环,每个情况(注意前提:得符合题意)都深搜一下
         for(int i=1;i<=每种情况数;i++)
                if(满足进一步搜索条件)//判断是否合理
                {
                    为进一步搜索所需要的状态打上标记;//是需要的状态
                    dfs(t+1);
                    恢复到打标记前的状态;//也就是说的{回溯一步}
                }
        }
    }
    

    DFS最重要的就是要考虑搜索顺序
    每个题目最好画一下递归搜索树,便于理解
    例题一:(输出不同方案,也可输出方案的总数)
    AcWing 842. 排列数字【DFS】

    例题二:(输出不同方案,也可输出方案的总数)
    AcWing 843. n-皇后问题【DFS】【剪枝】

    例题三:(输出不同方案,也可输出方案的总数)
    AcWing 92. 递归实现指数型枚举【DFS】【递归】

    例题四:(输出不同方案,也可输出方案的总数)
    AcWing 94. 递归实现排列型枚举【DFS】【递归】

    例题五:(输出不同方案,也可输出方案的总数)
    AcWing 93. 递归实现组合型枚举【DFS】【递归】

    例题七:(不同的方案数)
    AcWing 1209. 带分数【DFS】【递归】【剪枝】【第四届蓝桥杯省赛C++B/C组,第四届蓝桥杯省赛JAVAA/B组】

    例题八:(从初始位置出发能到达的瓷砖数、FloodFill)
    AcWing 1113. 红与黑【《信息学奥赛一本通》】【DFS】【BFS】【Flood Fill】

    例题九:(连通块的个数、FloodFill)
    AcWing 1233. 全球变暖【FloodFill】【BFS】【DFS】

    例题十:(最多花费的路费是多少)
    AcWing 1207. 大臣的旅费【第四届蓝桥杯省赛C++A组,第四届蓝桥杯省赛JAVAA组】【树的直径】【BFS】【DFS】【树形DP】

    例题十一:(从起点坐标到终点坐标的方案总数)
    洛谷 P1605 迷宫【搜索】【DFS】【BFS】

    例题十二:(输出所有可行的路径,即所有不同方案)
    洛谷 P1238 走迷宫【搜索】【DFS】


    BFS

    宽搜(bfs)框架

    一、 判重数组st[] 一般入队时判重(保证每个点只会入队一次,从而保证时间复杂度是线性的) (特殊情况出队时判重,如A*算法、Dijkstra算法)
    二、 queue
      1. queue ← \leftarrow 初始状态
      2. while (queue非空)
       {
         t ← \leftarrow 队头元素
         队头元素出队列
     
         for (扩展t)
         {
           ver ← \leftarrow 新节点
           if (!st[ver])
           {
             ver → \rightarrow 队尾
           }
         }
       }

    宽搜(bfs)本质从一个状态扩展到另一个状态。不同宽搜题目的不同点在于状态的定义
    主要有两大类, 第一类走迷宫类问题,矩阵的每一个格子是一个状态;第二类是八数码、数独类问题,整个矩阵(一个数组)是一个状态,每次枚举它能变成什么状态


    例题一:(最少移动次数)
    AcWing 844. 走迷宫【BFS】

    例题二:(最少交换次数)
    AcWing 845. 八数码【BFS】

    例题三:(最少需要多少时间就能吃到奶酪)
    AcWing 1101. 献给阿尔吉侬的花束【《信息学奥赛一本通》】【bfs】【bfs框架】

    例题四:(从初始位置出发能到达的瓷砖数、FloodFill)
    AcWing 1113. 红与黑【《信息学奥赛一本通》】【DFS】【BFS】【Flood Fill】

    例题五:(逃脱所需最短时间)
    AcWing 1096. 地牢大师【《信息学奥赛一本通》】【三维地图BFS】

    例题六:(连通块的个数、FloodFill)
    AcWing 1233. 全球变暖【FloodFill】【BFS】【DFS】

    例题七:(最多花费的路费是多少)
    AcWing 1207. 大臣的旅费【第四届蓝桥杯省赛C++A组,第四届蓝桥杯省赛JAVAA组】【树的直径】【BFS】【DFS】【树形DP】

    例题八:(从起点坐标到终点坐标的方案总数)
    洛谷 P1605 迷宫【搜索】【DFS】【BFS】

    展开全文
  • 规格为A4纸或A3纸折叠 佛山科学技术学院用四号宋体 实 验 报 告用小二号黑体 课程名称 数据结构实验 实验项目 实现DFSBFS算法 专业班级 姓 名 学 号 指导教师 成 绩 日 期 用小四号宋体 一目的与要求 1通过本实验...
  • DFS BFS

    千次阅读 2021-12-02 21:25:19
    DFS BFS [2049. 统计最高分的节点数目](https://leetcode-cn.com/problems/count-nodes-with-the-highest-score/) 851. 喧闹和富有 690. 员工的重要性 方法一:深度优先搜索 方法二:广度优先搜索 1034. 边界着色 ...

    2049. 统计最高分的节点数目

    class Solution:
        def countHighestScoreNodes(self, parents: List[int]) -> int:
            n = len(parents)
            children = [[] for _ in range(n)]
            for node, p in enumerate(parents):           
                p != -1 and children[p].append(node)
           
            def dfs(node: int) -> int:
                score, size = 1, n - 1
                for ch in children[node]:
                    sz = dfs(ch)
                    score *= sz
                    size -= sz
                if node: score *= size
    
                nonlocal maxScore, cnt
                if score == maxScore: cnt += 1
                elif score > maxScore:
                    maxScore, cnt = score, 1
                return n - size
            
            maxScore = cnt = 0
            dfs(0)
            return cnt
    
    class Solution {
        long maxScore = 0;
        int n, cnt = 0;
        List<Integer>[] children; 
        public int countHighestScoreNodes(int[] parents) {
            n = parents.length;
            children = new ArrayList[n];
            for (int i = 0; i < n; i++){
                children[i] = new ArrayList<>();
            }
            for (int i = 0; i < n; i++){
                int p = parents[i];
                if (p != -1) children[p].add(i);
            }
            dfs(0);
            return cnt;
        }
        int dfs(int node){
            long score = 1;
            int size = n - 1;
            for (int c : children[node]){
                int t = dfs(c);
                score *= t;
                size -= t;
            }
            if (node != 0) score *= size;
            if (score == maxScore) cnt ++;
            else if (score > maxScore){
                maxScore = score;
                cnt = 1;
            }
            return n - size;
        }
    }
    

    851. 喧闹和富有

    Leetcode
    搜索比自己有钱的最安静的一个

    class Solution:
        def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]:
    
            def dfs(x: int):
                if ans[x] != -1: return # 已经搜索过
                ans[x] = x
                for y in g[x]: # 搜索比自己有钱的最安静的一个
                    dfs(y) 
                    if quiet[ans[y]] < quiet[ans[x]]:
                        ans[x] = ans[y]
    
            n = len(quiet)
            ans = [-1] * n # -1 标志着没有访问过
            g = [[] for _ in range(n)] # 记录直接比自己有钱的人        
            for a, b in richer: g[b].append(a)   
                          
            for i in range(n): dfs(i)
            return ans
    
    class Solution:
        def loudAndRich(self, richer: List[List[int]], quiet: List[int]) -> List[int]:
    
            @lru_cache(None)
            def dfs(x):
                return x if not d[x] else min((x, min((dfs(o) for o in d[x]), key=KEY)), key=KEY)
    
            d, KEY = defaultdict(set), lambda y:quiet[y]
            for a,b in richer: d[b].add(a)
            
            return [dfs(i) for i in range(len(quiet))]
    

    690. 员工的重要性

    Leetcode

    方法一:深度优先搜索

    class Solution:
        def getImportance(self, employees: List['Employee'], id: int) -> int:
            mp = {e.id:e for e in employees}
    
            def dfs(id: int) -> int:
                e = mp[id]
                return e.importance + sum(dfs(subId) for subId in e.subordinates)
            
            return dfs(id)
    

    方法二:广度优先搜索

    class Solution:
        def getImportance(self, employees: List['Employee'], id: int) -> int:
            mp = {e.id: e for e in employees}
    
            total = 0
            q = collections.deque([id])
            while q:
                curId = q.popleft()
                e = mp[curId]
                total += e.importance
                # for subId in e.subordinates:
                #     q.append(subId)
                q.extend(e.subordinates)
            
            return total
    

    1034. 边界着色

    Leetcode
    在这里插入图片描述
    数字代表相邻格子颜色相同的数量,只有中间的 4 不是边界,其他有数字的格子都为边界。有点扫雷游戏的感觉。

    class Solution:
        def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]:
    
            def dfs(i, j):
                # 没有出界、颜色同 c、没有访问过,四面递归。
                if 0 <= i < m and 0 <= j < n and grid[i][j] == c and (i, j) not in t:                
                    t.add((i, j))             
                    dfs(i + 1, j)
                    dfs(i - 1, j)
                    dfs(i, j + 1)
                    dfs(i, j - 1)
    
            # 用于判断四面同色
            def b(i, j): 
                return 0 <= i < m and 0 <= j < n and grid[i][j] == c
    
            # 连通分量网格坐标 t、颜色 c  
            m, n, t, c = len(grid), len(grid[0]), set(), grid[row][col]
            
            dfs(row, col)
    
            # 四周同色将被去掉
            x = set()
            for i, j in t:
                if b(i + 1, j) and b(i - 1, j) and b(i, j + 1) and b(i, j - 1): x.add((i, j))
           
            # 边界着色
            for i, j in t - x: grid[i][j] = color
    
            return grid
    
    # 通过递归函数返回值着色
    class Solution:
        def colorBorder(self, grid: List[List[int]], row: int, col: int, color: int) -> List[List[int]]:
    
            def dfs(i, j):
                # 出界 说明上层为边界
                if i < 0 or j < 0 or i == m or j == n:  return True
                if (i, j) in t: return False # 访问过
                if grid[i][j] != c: return True # 说明上层与不同色网格相邻,是边界
                t.add((i, j)) # 界内、没有访问过、同色
                
                for dx,dy in (0,1),(1,0),(0,-1),(-1,0):
                    if dfs(i + dx, j + dy):
                        grid[i][j] = color # 边界着色
    
                return False
            
            m, n, t, c = len(grid), len(grid[0]), set(), grid[row][col]
            dfs(row, col)
            return grid
    

    419. 甲板上的战舰

    Leetcode
    横排或竖排连续的 ‘x’ 为一个战舰,和经典题目[岛屿数量]相同。

    class Solution:
        def countBattleships(self, board: List[List[str]]) -> int:
            res, m, n = 0, len(board), len(board[0])
            for i in range(m):
                for j in range(n):
                    ## 方法一: 以下使用 DFS
                    # res += self.dfs(board, i, j)
    
                    ## 方法二: 以下使用 BFS
                    res += self.bfs(board, i, j)
    
            return res
    
        ## 方法一: DFS
        def dfs (self, board, i, j):
            if i < 0 or i >= len(board) or j < 0 or j >= len(board[0]) or board[i][j] == '.':  return 0
            board[i][j] = '.'
            for x, y in (0, 1), (1, 0), (0, -1), (-1, 0):
                self.dfs(board, i + x, j + y)
    
            return 1
        
        ## 方法二: BFS
        def bfs (self, board, i, j):
            m, n = len(board), len(board[0])
            if board[i][j] == '.': return 0        
            q = deque([(i, j)])
            while q:           
                i, j = q.popleft()          
                board[i][j] = '.'
                for x, y in (i, j + 1), (i, j - 1), (i + 1, j), (i - 1, j):
                    if x < 0 or y < 0 or x >= m or y >= n or board[x][y] == '.': continue
                    q.append((x, y))
    
            return 1
            
        # 方法三:如果此单元格是战舰的头部,则它的左边和上边一定是空或边界。
            # return sum(ch == 'X' and not (i > 0 and board[i - 1][j] == 'X' or j > 0 and board[i][j - 1] == 'X') for i, row in enumerate(board) for j, ch in enumerate(row))
    

    ★200. 岛屿数量

    Leetcode

    方法一:深度优先搜索

    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            def dfs(r, c):
                grid[r][c] = 0 # 标记为水            
                for x, y in [(r - 1, c), (r + 1, c), (r, c - 1), (r, c + 1)]:
                    if 0 <= x < m and 0 <= y < n and grid[x][y] == "1":
                        dfs(x, y)
    
            m, n, lands = len(grid), len(grid[0]), 0 # lands 岛屿的数量
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == "1": # 把岛变成水
                        lands += 1
                        dfs(i, j)
            
            return lands
    

    方法二:广度优先搜索

    class Solution:
        def numIslands(self, grid: List[List[str]]) -> int:
            m, n, lands = len(grid), len(grid[0]), 0 # lands 岛屿的数量
            for i in range(m):
                for j in range(n):
                    if grid[i][j] == "1":
                        lands += 1
                        grid[i][j] = "0"
                        neighbors = collections.deque([(i, j)])
                        while neighbors:
                            row, col = neighbors.popleft()
                            for x, y in [(row - 1, col), (row + 1, col), (row, col - 1), (row, col + 1)]:
                                if 0 <= x < m and 0 <= y < n and grid[x][y] == "1":
                                    neighbors.append((x, y))
                                    grid[x][y] = "0"
            
            return lands
    

    463. 岛屿的周长

    695. 岛屿的最大面积

    827. 最大人工岛

    1036. 逃离大迷宫

    Leetcode
    从 source 出发向四周呈菱形状扩展,可以走出 n 步以外。说明 source 没有被局部包围,如果 target 同样没有被局部包围返回真。也就是最大搜索范围是 n <= 200 步。

    class Solution:
        def isEscapePossible(self, blocked: List[List[int]], source: List[int], target: List[int]) -> bool:
          
            def bfs(s, t):
                # vis, q = set(), [s]
                vis, q = defaultdict(set), [s]
                
                while q:
                    x, y = q.pop()
                    for a, b in ((x, y + 1), (x, y - 1), (x + 1, y), (x - 1, y)):
                        # if 0 <= a < 1000000 and 0 <= b < 1000000 and [a, b] not in blocked and (a, b) not in vis:
                        if 0 <= a < 1000000 and 0 <= b < 1000000 and b not in d[a] and b not in vis[a]:                        
                            if [a, b] == t or abs(a - s[0]) + abs(b - s[1]) > n:
                                return True
                           
                            q.append([a, b])
                            # vis.add((a, b))
                            vis[a].add(b)
    
                return False
                
            # n = len(blocked)        
            d, n = defaultdict(set), len(blocked) 
            for x, y in blocked: d[x].add(y)  
    
            return bfs(source, target) and bfs(target, source)
    

    395. 至少有 K 个重复字符的最长子串

    class Solution(object):
        # @lru_cache()
        def longestSubstring(self, s, k):
            if len(s) < k: return 0
            cnt = Counter(s)
            for c, v in cnt.items():
                if v < k:
                    return max(self.longestSubstring(t, k) for t in s.split(c))
            return len(s)
    
    class Solution {
        public int longestSubstring(String s, int k) {
            if (s.length() < k) return 0;
            int[] cnt = new int[26];
            for (char c : s.toCharArray()) cnt[c-'a']++;
            for (int i = 0; i < 26; i++){
                if (cnt[i] > 0 && cnt[i] < k){
                    int ans = 0;
                    String regex = String.valueOf((char)(i+'a'));
                    for (String t : s.split(regex)){
                        ans = Math.max(ans, longestSubstring(t, k));
                    }
                    return ans;
                }
            }
            return s.length();
        }
    }
    
    展开全文
  • 八数码的3种解法 八数码难题 3×3九宫棋盘,放置数码为1-8的8个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布局。 输入示例: 2 0 3 1 8 4 7 6 5 1 2 3 ...根据题意,需要求解的问题是:给定初始布局...

    八数码的3种解法

    八数码难题

    3×3九宫棋盘,放置数码为1-8的8个棋牌,剩下一个空格,只能通过棋牌向空格的移动来改变棋盘的布局。

    输入示例:

    2 0 3
    1 8 4
    7 6 5
    
    1 2 3
    8 0 4
    7 6 5
    
    8 3 4
    2 6 5
    1 7 0
    
    1 2 3
    8 0 4
    7 6 5
    
    2 1 6
    4 0 8
    7 5 3
    
    1 2 3
    8 0 4
    7 6 5
    
    3 1 8
    7 6 4
    0 2 5
    
    1 2 3
    8 0 4
    7 6 5
    

    根据题意,需要求解的问题是:给定初始布局(即初始状态)和目标布局(即目标状态),如何移动棋牌才能从初始布局到达目标布局

    方法1 宽度优先搜索

    宽度优先搜索属于盲目搜索的一种,不需进行OPEN表的排序

    基本思路:

    1. 设置一个OPEN表,来存放打算访问的矩阵,一个CLOSE表,存放已经访问过的矩阵
    2. 将初始矩阵加入OPEN表
    3. 取出OPEN表中首元素,判断其是否等于目标矩阵,否则找到初始矩阵中0的位置
    4. 将0分别和上、下、左、右的元素交换位置,首先判断是否越界,不越界的话,交换后生成的矩阵是否已在CLOSE表或OPEN表中,如果均不在,则将其加入OPEN表中
    5. 将OPEN表中的首元素从OPEN中删除,并加入CLOSE表,重复3-5步骤直至OPEN表为空或OPEN表中首元素等于目标矩阵

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef struct node{
    	int m[3][3],parent = -1;
    	// m是状态矩阵,parent是父节点 
    }M;
    int equal(M a, M b){
    	// 比较两个矩阵是否相等,相等返回1,不相等返回0 
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			if(a.m[i][j] != b.m[i][j])
    				return 0;
    	return 1;
    }
    void printM(M a){
    	// 打印M中的矩阵 
    	for(int i = 0; i < 3; i++){
    		for(int j = 0; j < 3; j++)
    			cout << a.m[i][j];
    		cout << endl; 
    	}
    	cout << endl;
    }
    vector <M> OPEN,CLOSE; // 使用动态数组充当OPEN表、CLOSE表 
    int ifFindAnswer = 0,cnt; // cnt为OPEN的指针,控制搜索的进度 
    int opt[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 
         //操作集      上    下    左     右 
    int main(){
    	M *s0,*sg,front;// s0:初始状态,sg:最终状态,front:每次OPEN中取出队首元素 
    	int x,y; // 用来记录0(即空格)的位置 
    	s0 = (struct node *)malloc(sizeof(struct node));
    	sg = (struct node *)malloc(sizeof(struct node));
    	cout << "请输入初始状态:(请用0代替空格)" << endl;
    	// 读入初始状态和最终状态 
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> s0->m[i][j];
    	cout << "请输入目标状态:(请用0代替空格)" << endl;
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> sg->m[i][j];
    	// 将s[0]放入队列第一个 
    	OPEN.push_back(*s0);
    	while(OPEN.size() != cnt){
    		front = OPEN[cnt];
    		// 判断是否找到答案 ,如果是,退出循环 
    		if(equal(front,*sg) == 1){
    			ifFindAnswer = 1;
    			break;
    		}
    		// 1.查找0(也就是空格)位置并记录 
    		for(int i = 0; i < 3; i++){
    			for(int j = 0; j < 3; j++){
    				if(front.m[i][j] == 0){
    					x = i;
    					y = j;
    					break;
    				}
    			}
    		}
    		for(int i = 0; i < 4; i++){
    			int flag = 1;
    			// 先判断移动后是否合法 
    			if(x+opt[i][0] > -1 && x+opt[i][0] < 3 && y+opt[i][1] > -1 && y+opt[i][0] < 3){
    				M temp = front;
    				swap(temp.m[x][y],temp.m[x+opt[i][0]][y+opt[i][1]]);
    				// 判断是否已探索过该节点 
    				for(int j = 0; j < CLOSE.size(); j++){
    					if(equal(CLOSE[j],temp) == 1) {
    						flag = 0;
    						break;	
    					}
    				}
    				// 判断是否是已加入OPEN,但未探索的结点 
    				for(int i = x+1; i < OPEN.size(); i++){
    					if(equal(OPEN[i],temp) == 1){
    						flag = 0;
    						break; 
    					}
    				}
    				if(flag == 1){
    					// flag==1,未探索过该节点,将结点加入OPEN
    					temp.parent = cnt;
    					OPEN.push_back(temp); 
    				}
    			}else continue; 
    		}
    		CLOSE.push_back(OPEN[cnt++]);
    	}
    	if(ifFindAnswer == 0){
    		cout << "找不到路径!!";
    	}else{
    		// 找到答案,递归输出路径
    		cout << "找到路径,路径是:" << endl; 
    		stack <M> ans;
    		ans.push(OPEN[cnt]);
    		while(ans.top().parent != 0){
    			ans.push(OPEN[ans.top().parent]);
    		} 
    		ans.push(OPEN[0]);
    		while(ans.size() != 0){
    			printM(ans.top());
    			ans.pop();
    		} 
    	}
    }
    

    方法2 深度优先搜索

    深度优先搜索也属于盲目搜索的一种,不需进行OPEN表的排序

    基本思路:经典DFS暴力搜索,访问前通过遍历OPEN表判断是否访问过该矩阵,没访问则将其加入OPEN表并进行访问,访问过则不再进行访问,知道找到解为止。

    #include<bits/stdc++.h>
    using namespace std;
    typedef struct node{
    	int m[3][3],parent = -1;
    	// m是状态矩阵,parent是父节点 
    }M;
    int equal(M a, M b){
    	// 比较两个矩阵是否相等,相等返回1,不相等返回0 
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			if(a.m[i][j] != b.m[i][j])
    				return 0;
    	return 1;
    }
    void printM(M a){
    	// 打印M中的矩阵 
    	for(int i = 0; i < 3; i++){
    		for(int j = 0; j < 3; j++)
    			cout << a.m[i][j];
    		cout << endl; 
    	}
    	cout << endl;
    }
    vector <M> OPEN,CLOSE; // 使用动态数组充当OPEN表、CLOSE表 
    M *s0,*sg;// s0:初始状态,sg:最终状态,front:每次OPEN中取出队首元素 
    int ifFindAnswer = 0,cnt,ans; // cnt为OPEN的指针,控制搜索的进度 ans记录答案的位置 
    int opt[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 
         //操作集      上    下    左     右 
    void dfs(M a){
    	// 如果已找到答案,就结束 
    	if(ifFindAnswer == 1) return;
    	OPEN.push_back(a);
    	int tempcnt = cnt++;
    	if(equal(a,*sg) == 0){
    		int x,y;// x,y记录0的位置 
    		for(int i = 0; i < 3; i++)
    			for(int j = 0; j < 3; j++)
    				if(a.m[i][j] == 0){
    					x = i;
    					y = j;
    					break;
    				}
    		for(int i = 0; i < 4; i++){
    			int flag = 1;
    			// 先判断是否越界
    			if(x+opt[i][0] > -1 && x+opt[i][0] < 3 && y+opt[i][1] > -1 && y+opt[i][0] < 3){
    				M temp = a;
    				swap(temp.m[x][y],temp.m[x+opt[i][0]][y+opt[i][1]]);
    				// 判断是否已访问 
    				for(int j = 0; j < OPEN.size(); j++){
    					if(equal(OPEN[j],temp) == 1){
    						flag = 0;
    						break;
    					}
    				} 
    				// 如果没访问过,直接访问
    				if(flag == 1){
    					cout << tempcnt << endl;
    					temp.parent = tempcnt;
    					dfs(temp);
    				} 
    			}
    		} 
    	}else{
    		ifFindAnswer = 1;
    		ans = tempcnt;
    	} 
    } 
    int main(){
    	s0 = (struct node *)malloc(sizeof(struct node));
    	sg = (struct node *)malloc(sizeof(struct node));
    	cout << "请输入初始状态:(请用0代替空格)" << endl;
    	// 读入初始状态和最终状态 
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> s0->m[i][j];
    	cout << "请输入目标状态:(请用0代替空格)" << endl;
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> sg->m[i][j];
    	dfs(*s0);
    	if(ifFindAnswer == 1){
    		// 找到答案,递归输出路径
    		stack <M> path;
    		path.push(OPEN[ans]);
    		while(path.top().parent != 0){
    			path.push(OPEN[path.top().parent]);
    		} 
    		path.push(OPEN[0]);
    		while(path.size() != 0){
    			printM(path.top());
    			path.pop();
    		} 
    	}else{
    		cout << "路径不存在!!" << endl; 
    	}
    }
    

    方法3 A*算法

    A*算法属于启发式搜索,想要看懂它,需要先科普一些背景知识:

    启发式信息:用来加速搜索过程的有关问题领域的特征信息,用于决定要拓展的我下一个节点的信息,用于决定某些应该从搜索树种抛弃或修剪的节点的信息

    启发式搜索:使用启发式信息的搜索方法称为启发式搜索方法

    估价函数:用来估算节点处于最佳求解路径上的希望程度的函数,是启发式搜索用来搜索书中

    f(n) = g(n) + h(n)

    • n:搜索图中的某个当前被拓展的节点
    • f(n):从初始状态节点s,经由节点n到达目标节点ng,估计的最小路径代价
    • g(n):从s到n的实际路径代价
    • h(n):从n到ng估计的最小路径代价

    本题的估价函数:f(n) = d(n) + w(n)

    其中:d(n)为n的深度

    w(n)为不在位的棋子数

    本体思路:基于宽度优先搜索的基础上,每次将新节点加入OPEN表后,将OPEN表根据f(n)进行排序,然后再重复宽度优先搜索的思路。

    #include<bits/stdc++.h>
    using namespace std;
    typedef struct node{
    	int m[3][3]; // 状态矩阵 
    	int d; // 深度 
    	int w; // 不在位的棋子数 
    	int f; // = d + w
    	struct node * parent; // 父节点 
    }M; 
    
    bool cmp(M a,M b);
    int f(M a);
    int equal(M a,M b);
    
    int ifFindAnswer = 0,cnt; // cnt为OPEN的正在搜索的点下标,控制搜索的进度 
    vector <M> OPEN,CLOSE;
    M *s0,*sg;
    int opt[4][2] = {{-1,0},{1,0},{0,-1},{0,1}}; 
        //操作集       上    下    左     右 
    bool cmp(M a,M b){
    	// 根据f(n)进行排序 
    	if(a.f != b.f) return a.f < b.f; 
    	return a.d > b.d;
    }
    int f(M a){
    	// 计算估价函数 f(n) = d(n) + w(n) 
    	int f = 0;
    	a.w = 0;
    	for(int i = 0; i < 3; i++){
    		for(int j = 0; j < 3; j++){
    			if(a.m[i][j] != sg->m[i][j]){
    				a.w++;
    			}
    		}
    	}
    	f = a.w + a.d;
    	return f;
    } 
    int equal(M a,M b){
    	// 判断两个矩阵是否相等 
    	for(int i = 0; i < 3; i++){
    		for(int j = 0; j < 3; j++){
    			if(a.m[i][j] != b.m[i][j])	return 0;
    		}
    	}
    	return 1;
    }
    int main(){
    	int x,y;
    	M front;
    	s0 = (struct node *)malloc(sizeof(struct node));
    	sg = (struct node *)malloc(sizeof(struct node));
    	cout << "请输入初始状态:(请用0代替空格)" << endl;
    	// 读入初始状态和最终状态 
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> s0->m[i][j];
    	cout << "请输入目标状态:(请用0代替空格)" << endl;
    	for(int i = 0; i < 3; i++)
    		for(int j = 0; j < 3; j++)
    			cin >> sg->m[i][j];
    	s0->d = 999;
    	s0->f = f(*s0);
    	s0->parent = NULL;
    	OPEN.push_back(*s0);
    	while(OPEN.size() != 0){
    		front = OPEN[0];
    		for(int i = 0; i < 3; i++){
    			for(int j = 0; j < 3; j++){
    				printf("%d ",front.m[i][j]);
    			}
    			printf("\n");
    		}
    		printf("----------------------\n");
    		CLOSE.push_back(OPEN[0]);
    		if(equal(front,*sg) == 1){
    			ifFindAnswer = 1;
    			break;
    		}
    		for(int i = 0; i < 3; i++){
    			for(int j = 0; j < 3; j++){
    				if(front.m[i][j] == 0){
    					x = i;
    					y = j;
    					break;
    				}
    			}
    		}
    		for(int i = 0; i < 4; i++){
    			int flag = 1;
    			if(x+opt[i][0] > -1 && x+opt[i][0] < 3 && y+opt[i][1] > -1 && y+opt[i][1] < 3){
    				M temp = front;
    				swap(temp.m[x][y],temp.m[x+opt[i][0]][y+opt[i][1]]);
    				// 判断是否已探索过该节点 
    				for(int j = 0; j < CLOSE.size(); j++){
    					if(equal(CLOSE[j],temp) == 1) {
    						flag = 0;
    						break;	
    					}
    				}
    				// 判断是否是已加入OPEN,但未探索的结点 
    				for(int i = 0; i < OPEN.size(); i++){
    					if(equal(OPEN[i],temp) == 1){
    						flag = 0;
    						break; 
    					}
    				}
    				if(flag == 1){
    					// flag==1,未探索过该节点,将结点加入OPEN
    					temp.d = front.d+1;
    					temp.f = f(temp);
    					temp.parent = &CLOSE[CLOSE.size()-1];
    					OPEN.push_back(temp); 
    				}
    			}else continue; 
    		}
    		OPEN.erase(OPEN.begin());
    		sort(OPEN.begin(),OPEN.end(),cmp);
    	}
    	if(ifFindAnswer == 0){
    		cout << "找不到路径!!";
    	}else{
    		cout << "存在路径!!" << endl; 
    	}
    }
    
    展开全文
  • DFSBFS算法

    千次阅读 2022-02-05 09:09:47
    深度优先遍历简称DFS(Depth First Search),广度优先遍历简称BFS(Breadth First Search),它们是遍历图当中所有顶点的两种方式。下面分别介绍两种基本的搜索算法。
  • 这是山东大学可视化课程项目,用js实现的BFSDFS,详细的展示了BFSDFS的运行过程,网页可交互。
  • 复习DFS BFS和快排

    2021-02-15 11:37:32
    如何新建队列 Queue (Node) queue = new Linkedlist <>() 快排的章法有迹可循 DFSBFS先写中文思路会好做 记住如果栈或队列不为空,用cur接住弹出的头
  • java DFSBFS算法

    2022-04-17 13:24:20
    广度优先算法(BFS) BFS算法,它会对树或图进行"逐层"的遍历,也就是层序遍历,相较于DFS算法而言,BFS它不会直接到末尾,而是到下一层之前,会将节点的所有兄弟节点遍历完之后,再进入下一层。 ...
  • DFSBFS简述

    2022-02-01 15:34:50
    DFSBFS
  • ALGraph DFS BFS DIJ toposort keyPath
  • BFSDFS模板总结

    2022-08-09 17:53:16
    1.常规BFS模板 最典型的BFS场景之一为二叉树的层次遍历。如果我们不需要得到当前层数,可以采用如下模板样式。 while queue not empty cur = queue.pop() visit cur for node in cur.neighbors if node valid ...
  • 在二叉树上进行 DFS 遍历和 BFS 遍历的代码比较 DFS 遍历使用递归: void dfs(TreeNode root) { if (root == null) { return; } dfs(root.left); dfs(root.right); } BFS 遍历使用队列数据结构: void bfs...
  • DFSBFS(c语言实现)

    2021-07-07 10:03:32
    1.DFS(深度优先搜索) 深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义,深度优先,则是以深度为准则,先一条路走到底,直到达到目标。这里称之为递归下去。否则既没有达到目标又无路可走了,那么则退回...
  • 迷宫问题(DFS + BFS

    2022-04-19 09:31:23
    DFSBFS解决走迷宫之最短路径问题
  • DFSBFS的区别

    千次阅读 2022-02-20 22:41:04
    BFS的基本步骤 1. 将初始点(一个或多个)加入一个集合尾 2. 从集合头取出点,判断初始点的周边点,将符合条件的点加入队列 3. 重复 2 操作,直至集合为空。 ( 一般 每个点只加入队列一 次 ) 一般来说...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 101,398
精华内容 40,559
关键字:

DFS bfs