遞迴與回溯,DFS及BFS的演算法
阿新 • • 發佈:2019-01-06
遞迴:就是出現這種情況的程式碼: (或者說是用到了棧)
解答樹角度:在dfs遍歷一棵解答樹
優點:結構簡潔
缺點:效率低,可能棧溢位
遞迴的一般結構:
void f()
{
if(符合邊界條件)
{
///////
return;
}
//某種形式的呼叫
f();
}
回溯:遞迴的一種,或者說是通過遞迴這種程式碼結構來實現回溯這個目的。回溯法可以被認為是一個有過剪枝的DFS過程。
解答樹角度:帶回溯的dfs遍歷一棵解答樹
回溯的一般結構:
void dfs(int 當前狀態)
{
if(當前狀態為邊界狀態)
{
記錄或輸出
return;
}
for(i=0;i<n;i++) //橫向遍歷解答樹所有子節點
{
//擴展出一個子狀態。
修改了全域性變數
if(子狀態滿足約束條件)
{
dfs(子狀態)
}
恢復全域性變數//回溯部分
}
}
經典例題,比如解數獨問題。
BFS與 DFS
BFS的佔用的是佇列的空間,DFS 佔用的是棧的空間(因為遞迴)。BFS和DFS的空間複雜度恰好相反。對鏈狀圖,BFS最好(佇列中最多隻有1個元素),DFS最差(所有節點都在根節點的遞迴內)。對起點與其他所有點相鄰的圖,DFS最好(遞迴深度為1),BFS最差(佇列中放滿了所有與起點相鄰的圖)。
BFS一般結構:
queue<type> q;
q.push(初始狀態);
while (!q.empty())
{
type t = q.front() ;
q.pop();
遍歷 t 的各個Next狀態 next
{
if (next is legal)
q.push(next); 計數或維護等;
}
}
但是BFS的狀態數一多,需要的空間就會較大。
DFS 一般結構與回溯相似
DFS(頂點)
{
處理當前頂點,記錄為已訪問
遍歷與當前頂點相鄰的所有未訪問頂點
{
標記更改;
DFS( 下一子狀態);
恢復更改;
}
}