深度優先搜索與廣度優先搜索(含算法例題講解)


搜索算法

搜索算法有很多種類型,一般來說就是深度優先搜索廣度優先搜索A*搜索IDA*搜索這四種類型的搜索,而本篇講述的就是其中最核心,最簡單的搜索深度優先搜索和廣度優先搜索。

DFS算法簡述

深度優先搜索是一種適用於樹形結構的搜索,它和數據結構棧緊密相連。對於這種算法而言,它的主要步驟大致如下:

  1. 找到當前可以拓展的點,那么立即走入這個分支點。
  2. 如果當前搜索分支,無效或者已經找到目標,那么退回到上一步,即回溯。
  3. 每一個點最多會被訪問兩次。訪問含義是入棧一次,出棧一次。

DFS性質

DFS序列就是深度優先搜索最為重要的性質,它可以將一個子樹變成一個區間。

代碼實現(遞歸)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_N 10000

typedef struct Node {
    int vertex;
    struct Node *next;
} Node, *LinkedList;

LinkedList insert_node(LinkedList head, int index) {
    Node *node = (Node *)malloc(sizeof(Node));
    node->vertex = index;
    node->next = head;
    head = node;
    return head;
}

typedef struct Graph {
    LinkedList edges[MAX_N];
    int n;
    int visited[MAX_N];
} Graph;

void init(Graph * g, int n) {
    g->n = n;
    for (int i = 0; i < g->n; ++i) {
        g->edges[i] = NULL;
    }
    
}

void insert(Graph *g, int x, int y) {
    if (x < 0 || x >= g->n || y < 0 || y >= g->n) {
        return ;
    }
    g->edges[x] = insert_node(g->edges[x], y);
    g->edges[y] = insert_node(g->edges[y], x);
}

void clear(Graph *g) {
    for (int i = 0; i < g->n; ++i) {
        Node *head = g->edges[i];
        while (head != NULL) {
            Node *delete_node = head;
            head = head->next;
            free(delete_node);
        }
    }
    free(g);
}

void dfs(Graph *g, int index) {
    printf("%d\n", index);
    g->visited[index] = 1;
    for (Node *tmp = g->edges[index]; tmp != NULL; tmp = tmp->next) {
        if (!g->visited[tmp->vertex]) {
            dfs(g, tmp->vertex);
        }
    }
    return;
}

int main() {
    int n, m, k;
    scanf("%d %d", &n, &m);
    Graph *graph = (Graph *)malloc(sizeof(Graph));
    init(graph, n);
    for (int i = 0; i < m; ++i) {
        int x, y;
        scanf("%d %d", &x, &y);
        insert(graph, x, y);
    }
    scanf("%d", &k);
    dfs(graph, k);
    clear(graph);
    return 0;
}

算法例題

 

BFS算法簡述

廣度優先搜索和深度優先搜索恰恰相反,它是一種適用於圖型結構的搜索,它和數據結構隊列緊密向量。對於這種算法而言,它主要的步驟大致如下所示:

  1. 找到當前可以拓展的點,將它放入候選隊列之中。
  2. 每次選取候選隊列的隊頭,作為當前狀態。

BFS性質

對於廣度優先搜索而言,它是一種逐層遍歷的算法,所有狀態按照入隊的先后順序具有層次單調性,也就是所謂的步數單調性質,如果每一次拓展恰好對應一部,那么當第一個狀態第一次被訪問(入隊),就會得到從起始狀態到達該狀態的最少步數。

代碼實現(隊列)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_N 10000

typedef struct Queue {
    int *data;
    int head, tail, length;
} Queue;

void init_queue(Queue *q, int length_input) {
    q->data = (int *)malloc(sizeof(int) * length_input);
    q->length = length_input;
    q->head = 0;
    q->tail = -1;
}

void push(Queue *q, int element) {
    if (q->tail + 1 < q->length) {
        q->tail++;
        q->data[q->tail] = element;
    }
}

int front(Queue *q) {
        return q->data[q->head];
}

void pop(Queue *q) {
        q->head++;
}

int empty(Queue *q) {
    if (q->head > q->tail) {
        return 1;
    } else {
        return 0;
    }
}

void clear_queue(Queue *q) {
    free(q->data);
    free(q);
}

typedef struct Node {
    int vertex;
    struct Node *next;
}Node, *LinkedList;

LinkedList insert_node(LinkedList head, int index) {
    Node *node = (Node *)malloc(sizeof(Node));
    node->vertex = index;
    node->next = head;
    head = node;
    return head;
}

typedef struct Graph {
    LinkedList edges[MAX_N];
    int visited[MAX_N];
    int n;
}Graph;

void init_graph(Graph *g, int n) {
    g->n = n;
    memset(g->visited, 0, sizeof(g->visited));
    for (int i = 0; i < g->n; ++i) {
        g->edges[i] = NULL;
    }
}

void insert(Graph *g, int x, int y) {
    if (x < 0 || x >= g->n || y < 0 || y >= g->n) {
        return ;
    }
    g->edges[x] = insert_node(g->edges[x], y);
    g->edges[y] = insert_node(g->edges[y], x);
}

void clear_graph(Graph *g) {
    for (int i = 0; i < g->n; ++i) {
        Node *head = g->edges[i];
        while (head != NULL) {
            Node *delete_node = head;
            head = head->next;
            free(delete_node);
        }
    }
    free(g);
}

void bfs(Graph *g, int id) {
    Queue *queue = (Queue *)malloc(sizeof(Queue));
    init_queue(queue, g->n);
    printf("%d\n", id);
    g->visited[id] = 1;
    push(queue, id);
    while (!empty(queue)) {
        int tmp = front(queue);
        pop(queue);
        for (Node *adj = g->edges[tmp]; adj != NULL; adj = adj->next) {
            if (!g->visited[adj->vertex]) {
                printf("%d\n", adj->vertex);
                push(queue, adj->vertex);
                g->visited[adj->vertex] = 1;
            }
        }
    }
    clear_queue(queue);
}

int main() {
    int n, m, k;
    scanf("%d %d", &n, &m);
    Graph *graph = (Graph *)malloc(sizeof(Graph));
    init_graph(graph, n);
    for (int i = 0; i < m; ++i) {
        int x, y;
        scanf("%d %d", &x, &y);
        insert(graph, x, y);
    }
    scanf("%d", &k);
    bfs(graph, k);
    clear_graph(graph);
    return 0;
}

算法例題

 

更多算法例題(迷宮問題與棋盤馬走日問題)

參考資料:

https://www.acwing.com/blog/content/173/

https://www.acwing.com/blog/content/173/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM