把这几天学的搜索做一个初步总结。
一、 深度优先搜索(DFS):从起点出发,走过的点要做标记,发现有没走过的点,就随意挑一个往前走,走不动了就回退。不能走已经走过的点(需要判重)。
举几个栗子:
1.判断从V出发是否能走到终点:
bool Dfs(V) { if( V 为终点) return true; if( V 为旧点) return false; 将V标记为旧点; 对和V相邻的每个节点U { if( Dfs(U) == true) return true; } return false; }
判断从v出发是否能走到终点时,返回值有意义。
因为有回溯的过程,所以会把k出发能走到的点都走一遍。
2.判断从v出发是否能走到终点,如果能,要记录路径。
bool Dfs(V) { if( V为终点) { path[depth] = V; return true; } if( V 为旧点) return false; 将V标记为旧点; path[depth]=V; ++depth; 对和V相邻的每个节点U { if( Dfs(U) == true) return true; } --depth; return false; }
--depth的过程就是回溯的过程。
3.在图中寻找最优(步数最少路径)
void Dfs(V) { if( V为终点) { path[depth] = V; if( depth < minSteps ) { minSteps = depth; 拷贝path到bestPath; } return; } if( V 为旧点) return; if( depth >= minSteps ) return ; //最优性剪枝 将V标记为旧点; path[depth]=V; ++depth; 对和V相邻的每个节点U { Dfs(U); } --depth; 将V恢复为新点 }
求最优路时,要把走过的不同的路保留下来。
为什么将v恢复为新点:为了之后的兄弟可能会绕回v。
找最优路时返回值无意义。
图的存储方式对遍历v的相邻节点u过程的影响:
邻接矩阵遍历:O(n²)
邻接表遍历:O(n+e)
稀疏图用邻接表存储效率更高。
剪枝:最优性剪枝,可行性剪枝。(待更新)
二、 广度优先搜索(BFS):依层次顺序,从小到大扩展节点。把层次低的点全部扩展出来后,才会扩展层次高的点。扩展时,不能扩展已经走过的节点(要判重)。
广搜最优解一般是求操作步骤最少,注意判重(可以用set来判重)。
三、双向广搜(DBFS):待更新。
四、A*算法在BFS算法中,若对每个状态n都设定估价函数f(n)=g(n)+h(n),并且每次从Open表中选节点进行扩展时,都选取f值最小的节点,则该搜索算法为启发式搜索算法,又称A算法。
g(n) : 从起始状态到当前状态n的代价
h(n) : 从当前状态n到目标状态的估计代价
广搜可以看作:f(n)=g(n)(无h(n)这项),A*的关键是如何设计估计函数h(n),如何对估计函数做限制,确保能找到最优解。
由于是有根据地去估计,因此不可能估计出比真实值还小的数,即真实代价≥估计代价。
估计函数实际上是一种乐观的估计,在这种乐观估计的前提下,越悲观越靠近真实值。即估计值在满足≤真实值的前提下,估计函数的值越大越好。
五、迭代加深搜索算法:总体上按照深度优先算法方法进行。多次从起点出发做深搜,每次规定一个深度限制dm。劣势:会有重复搜索,从时间效率上不如广搜,好处是因为有步数的限制所以不用判重。
广搜有可能比深搜号,但广搜扩展出来的情况可能更多。
六、Alpha-Beta剪枝
极大极小搜索法(待更新)