DFS(深搜)和BFS(廣搜)
DFS
深度優先搜索屬於圖算法的一種,英文縮寫爲DFS即Depth First Search.其過程簡要來說是對每一個可能的分支路徑深入到不能再深入爲止,而且每個節點只能訪問一次.
舉例說明:
上圖是無向圖,從A節點開始進行深度優先搜索(以下的訪問次序並不是唯一的,第二個點既可以是B也可以是C,D),則我們可能得到如下的一個訪問過程:
A->B->E(沒有路了!回溯到B,發現還是沒路,回溯到A)->C->F->H->G->D(沒有路,最終回溯到A,發現A也沒路了)本次搜索結束。
注意:每次到最後一個沒有路的節點時,標記已經走過,上圖例子中第一個沒有路的節點時E,然後回溯到B,發現B也沒有路了,標記走過,再回溯到A,以此類推。
舉例
題目描述
在一個 w∗h的矩形廣場上,每一塊 1∗1的地面都鋪設了紅色或黑色的瓷磚。小明現在站在某一塊黑色的瓷磚上,他可以從此處出發,移動到上下左右四個相鄰的且是黑色的瓷磚上。現在,他想知道,通過重複上述移動所能經過的黑色瓷磚數。
輸入
第一行兩個正整數 h,w。(2≤h,w≤50)
接下來輸入一個二維字符矩陣,每個字符爲 “.”,"#","@",分別代表黑色瓷磚,紅色瓷磚,初始位置。
輸出
輸出一個整數,表示可以到達的瓷磚數。
樣例輸入
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
.........
樣例輸出
59
數據規模與約定
時間限制:1 s
內存限制:256 M
100% 的數據保證 2≤h,w≤50
思路:從一個起點找到可走路徑,標記已走過,遞歸調用
注意:需將此處的行列與座標紙上的x,y區分開
#include <iostream>
using namespace std;
int h,w,tx,ty;
char mmap[55][55] = {0};
int ans = 1;
int dir[4][2] = {
0,1,
1,0,
0,-1,
-1,0
};
void func(int tx,int ty){
if(tx < 0 ||tx >= w || ty < 0 || ty >= h){ //越界條件
return ;
}
for(int i = 0; i < 4; i++){ //四個方向
int x = tx + dir[i][0];
int y = ty + dir[i][1];
if(mmap[x][y] == '.'){
mmap[x][y] = 0;
ans++;
func(x,y);
}
}
}
int main(){
cin >> h >>w;
for(int i = 0; i < w; i++){
for(int j = 0; j < h; j++){
cin >> mmap[i][j];
if(mmap[i][j] == '@'){
tx = i;
ty = j;
}
}
}
func(tx,ty);
cout << ans << endl;
return 0;
}
上述題目的解法一般用在求圖中黑色的數量,波數等問題。
題目描述
給出 n 件物品,每個物品有一個體積 Vi,從中取出若干件物品能夠組成的不同的體積和有多少種可能。例如,n=3 , Vi={1,3,4},那麼輸出 6 種不同的體積和爲 1,3,4,5,7,8。
輸入
第一行一個正整數 n。(n≤20)
第二行 n 個整數,表示 Vi。(1≤Vi≤50)
輸出
輸出一個整數,表示不同體積的組合數。
樣例輸入
3
1 3 4
樣例輸出
6
數據規模與約定
時間限制:1 s
內存限制:256 M
100% 的數據保證 n≤20
#include <iostream>
using namespace std;
int n,num[25],ans = 0;
int check[1005] = {1};
void func(int s,int sum){//從s開始選數字,和爲sum
if(check[sum] == 0){
check[sum] = 1;
ans++;
}
for(int i = s; i <= n; i++){
sum += num[i];
func(i + 1, sum);
sum -= num[i]; //回溯
}
}
int main(){
cin >> n;
for(int i = 0; i < n; i++){
cin >> num[i];
}
func(0,0);
cout << ans;
}
DFS可以解決集合問題
題目描述
有很多人在門口排隊,每個人將會被髮到一個有效的通行密碼作爲門票。一個有效的密碼由 L 個小寫字母組成,至少有一個元音 (a,e,i,o,u)和兩個輔音,並且是按字母表順序出現的,例如 abc 是有效的,而 cba 不是。
現在給定一個期望長度 L 和 C 個小寫字母,輸出所有有效密碼。
輸入
第一行兩個正整數 L,C。(3≤L≤15,C≤26)
接下來一行輸入 C個小寫字母。
輸出
按照字母表順序輸出所有密碼,一行一個,若密碼超過 2500025000 時,只輸出前 2500025000 個密碼。
樣例輸入
4 6
a t c i s w
樣例輸出
acis
acit
aciw
acst
acsw
actw
aist
aisw
aitw
astw
cist
cisw
citw
istw
數據規模與約定
時間限制:1 s
內存限制:256 M
100% 的數據保證 3≤L≤15,C≤26
#include <iostream>
#include <algorithm>
using namespace std;
int L,C;
char num[26];
char ans[20];
int cnt = 0;
int fu,yuan,num2;
int func(int s, int left){ //從s開始還剩left個
if(left == 0){
if(yuan >= 1 && fu >= 2){
for(int i = 0; i < L; i++){
cout << ans[i];
}
cout << endl;
num2++;
if(num2 == 25000){
return -1;
}
}
}
for(int i = s; i < C; i++){
ans[cnt++] = num[i];
int f = 0;
if(num[i] == 'a' || num[i] == 'e' || num[i] == 'i' || num[i] == 'o' || num[i] == 'u'){
yuan++;
f = 1;
}else{
fu++;
}
if(func(i+1,left-1) == -1){
return -1;
}
if(f == 1){ //回溯
yuan--;
}else{
fu--;
}
cnt--;
}
return 0;
}
int main(){
cin >> L >> C;
for(int i = 0; i < C; i++){
cin >> num[i];
}
sort(num,num+C);
func(0,L);
}
題目描述
讀入一個用鄰接矩陣存儲的無向圖,輸出它的深度優先遍歷序列。(以 1爲起點,按照編號越小權值越大的規則)
輸入
第一行一個正整數 NN,表示節點個數。(5≤N≤20)
接下來輸入一個鄰接矩陣,a[i,j]=0表示 i,j之間不存在邊,=1 說明存在邊。
輸出
格式參照樣例
樣例輸入
8
0 1 1 0 0 0 0 0
1 0 0 1 1 0 0 0
1 0 0 0 0 0 1 1
0 1 0 0 0 1 0 0
0 1 0 0 0 1 0 0
0 0 0 1 1 0 0 0
0 0 1 0 0 0 0 1
0 0 1 0 0 0 1 0
樣例輸出
1-2-4-6-5-3-7-8
#include <iostream>
using namespace std;
int n;
int num[25][25],check[25],flag;
void func(int s){
if(flag == 1){
cout << '-';
}
flag = 1;
cout << s;
for(int i = 1; i <= n; i++){
if(num[s][i] == 1 && check[i] == 0){
check[i] = 1;
func(i);
}
}
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n ; j++){
cin >> num[i][j];
}
}
for(int i = 1; i <= n; i++) {
if(check[i] == 0){
check[i] = 1;
func(i);
}
}
return 0;
}
BFS
廣度優先搜索(BFS) 廣度優先搜索在進一步遍歷圖中頂點之前,先訪問當前頂點的所有鄰接結點。 a .首先選擇一個頂點作爲起始結點,並將其染成灰色,其餘結點爲白色。 b. 將起始結點放入隊列中。 c. 從隊列首部選出一個頂點,並找出所有與之鄰接的結點,將找到的鄰接結點放入隊列尾部,將已訪問過結點塗成黑色,沒訪問過的結點是白色。如果頂點的顏色是灰色,表示已經發現並且放入了隊列,如果頂點的顏色是白色,表示還沒有發現 d. 按照同樣的方法處理隊列中的下一個結點。
模板
#include<iostream>
#include<queue>
using namespace std;
#define max_n 500
struct Node{
int x,y,num;
};
char mmap[max_n + 5][max_n + 5];
queue<Node> q;
int n,m;
int dir[4][2] = {0,1,0,-1,1,0,-1,0};
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){ //從1開始防止越界
for(int j = 1; j <= m; j++){
cin >> mmap[i][j];
if(mmap[i][j] == 's'){
q.push({i,j,0});
}
}
}
while(!q.empty()){
Node temp = q.front();
q.pop();
for(int i = 0; i < 4; i++){
int x = temp.x + dir[i][0];
int y = temp.y + dir[i][1];
//判斷是否越界
/*if(x<=0 || y<= 0|| x>n || y>m){
continue;
}*/
if(mmap[x][y] == 'g'){ //終點
cout << temp.num + 1 << endl;
return 0;
}
if(mmap[x][y] == '.'){ //可以通過
mmap[x][y] = 0;
q.push({x,y,temp.num+1});
}
}
}
cout << "No" << endl;
return 0;
}
題目描述
小明剛剛參加完期中考試,“這次又能得班級第一了”,他沾沾自喜,想起之前一直努力學習的自己,他決定去西城紅場看個電影放鬆一下。現在小明想從學校走到電影院,因爲市政大力修路和挖地鐵,有些道路不允許步行,請判斷小明能否走到電影院(只能朝上下左右四個方向行走),如果能到,則輸出最短步數,如果不能到,則輸出 No.
輸入
第 1 行兩個數 n 和 m 表示地圖有 n 行 m 列 2≤n,m≤500 第 2 行至第 n+1 行爲地圖,其中 s 表示學校 g表示電影院 . 爲步行可以通過的點 # 爲步行不可通過的點
輸出
能走到電影院輸出最少步數 不能走到電影院輸出 No
樣例輸入
4 4
…s
…##
…
.g…
樣例輸出
5
數據規模與約定
時間限制:1 s
內存限制:256 M
100% 的數據保證 2≤n,m≤500
#include <iostream>
#include <queue>
using namespace std;
char mmap[505][505];
int n,m,sx,sy;
int dir[4][2]={
0,1,
1,0,
-1,0,
0,-1
};
struct Node{
int x,y,cnt;
};
queue<Node> que;
int main(){
cin >> n >> m;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
cin >> mmap[i][j];
if(mmap[i][j] == 's'){
Node node;
node.x= i;
node.y = j;
node.cnt = 0;
que.push(node);
}
}
}
while(!que.empty()){
Node temp = que.front();
que.pop();
for(int i = 0; i < 4; i++){
int tx = temp.x + dir[i][0];
int ty = temp.y + dir[i][1];
if(tx < 0 || ty < 0 || tx >= n || ty >= m){
continue;
}
if(mmap[tx][ty] == 'g'){
cout << temp.cnt + 1 << endl;
return 0;
}
if(mmap[tx][ty] == '.'){
mmap[tx][ty] = 0;
que.push({tx,ty,temp.cnt + 1});
}
}
}
cout << "No" << endl;
return 0;
}
帶有條件的廣搜
題目描述
小明看完了電影,是時候回家了,可是這時他突然得知小米之家的小米9現貨開賣了,這款手機小明已經想了一個多月,知道這個消息後的他十分興奮,一定要在回家之前先去小米之家買手機(城市中有一個或多個小米之家),請計算小明從電影院到任意一個小米之家買手機後回家的最短距離(只能朝上下左右四個方向行走,除了障礙物外,其他地方都可以通過),數據保證可以買完手機後回家。
輸入
第 1 行兩個數 n 和 m 表示地圖有 n 行 m 列 2≤n,m≤2000 第 2 行至第 n+1 行爲地圖 其中 S 表示電影院 T 表示家 P 表示小米之家 . 爲可以通過的點 # 爲障礙物
輸出
一個整數 表示小明從電影院去小米之家再回家的總距離
樣例輸入
5 5
.S…
###…
…T
.P##.
P…
樣例輸出
11
數據規模與約定
時間限制:5 s
內存限制:256 M
100% 的數據保證 2≤n,m≤2000
#include <iostream>
#include <queue>
using namespace std;
struct node {
int x, y, s, f; //s代表幾步,f代表有沒有手機
};
int n, m, check[2005][2005];//查重數組,1代表沒手機經過,2代表有手機經過,3沒有手機和有手機都經過
char mmap[2005][2005];
int dir[4][2] = {0, 1, 1, 0, 0, -1, -1, 0};
int main() {
queue<node> que;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> mmap[i][j];
if (mmap[i][j] == 'S') {
que.push({i, j, 0, 0});
check[i][j] = 1;
}
}
}
while (!que.empty()) {
node temp = que.front();
que.pop();
for (int i = 0; i < 4; i++) {
int x = temp.x + dir[i][0];
int y = temp.y + dir[i][1];
if (temp.f == 0 && check[x][y] & 1) continue;//沒有手機
if (temp.f == 1 && check[x][y] & 2) continue;//有手機
if (temp.f == 1 && mmap[x][y] == 'T') {//有手機到家
cout << temp.s + 1 << endl;
return 0;
}
if (temp.f==0&& mmap[x][y] == '.' || mmap[x][y] == 'S' || mmap[x][y] == 'T') {//沒手機可以經過的路徑
que.push({x, y, temp.s + 1, temp.f});
check[x][y] += 1;
}
if (temp.f==1&& mmap[x][y] == '.' || mmap[x][y] == 'S' ) {//有手機可以經過的路徑
que.push({x, y, temp.s + 1, temp.f});
check[x][y] += 2;
}
if (mmap[x][y] == 'P') {
que.push({x, y, temp.s + 1, 1});
check[x][y] = 3;
}
}
}
return 0;
}
廣搜搜索樹
559. N叉樹的最大深度
/*
// Definition for a Node.
class Node {
public:
int val;
vector<Node*> children;
Node() {}
Node(int _val) {
val = _val;
}
Node(int _val, vector<Node*> _children) {
val = _val;
children = _children;
}
};
*/
//廣搜
/*class Solution {
public:
int maxDepth(Node* root) {
if(!root) return 0;
queue<Node *> q;
q.push(root);
int deep_max = 0;
while(!q.empty()){
deep_max++;
for(int i = q.size(); i > 0; i--){
Node * temp = q.front();
q.pop();
for(auto a : temp->children){
q.push(a);
}
}
}
return deep_max;
}
};*/
//深搜
class Solution {
public:
int maxDepth(Node* root) {
if(root == NULL) return 0;
int deep_max = 0;
for(auto i : root->children){
int temp = maxDepth(i);
deep_max = max(deep_max,temp);
}
return deep_max+1;
}
};
Leetcode 劍指 Offer 32 - II. 從上到下打印二叉樹 II
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
vector<vector<int>> v;
vector<vector<int>> levelOrder(TreeNode* root) {
if(root == nullptr) return v;
queue<TreeNode * > q;
q.push(root);
vector<int> vec;
while(!q.empty()){
for(int i = q.size(); i; i--){
TreeNode * temp = q.front();
vec.push_back(temp->val);
q.pop();
if(temp->left){
q.push(temp->left);
}
if(temp->right){
q.push(temp->right);
}
}
v.push_back(vec);
vec.clear();
}
return v;
}
};