数据结构与算法--图的深度优先遍历和广度优先遍历

286 阅读13分钟

1、图的深度优先遍历

1.1 邻接矩阵

注意点:

  1. 将图的顶点和边信息输⼊入到图结构中;
  2. 创建⼀一个visited 数组,⽤用来标识顶点是否已经被遍历过.
  3. 初始化visited 数组,将数组中元素置为FALSE
  4. 选择顶点开始遍历.(注意⾮非连通图的情况)
  5. 进⼊入递归; 打印i 对应的顶点信息. 并将该顶点标识为已遍历.
  6. 循环遍历边表,判断当前arc[i][j] 是否等于1,并且当前该顶点没有被遍历过,则继续递归 DFS;
#define OK        1
#define ERROR     0
#define TRUE      1
#define FALSE     0

typedef int    Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int    Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef char   VertexType; /* 顶点类型应由用户定义 */
typedef int    EdgeType; /* 边上的权值类型应由用户定义 */

#define MAXSIZE   9 /* 存储空间初始分配量 */
#define MAXEDGE   15
#define MAXVEX    9
#define INFINITYC 65535

typedef struct {
    VertexType vexs[MAXVEX]; /* 顶点表 */
    EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
    int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
/* DFS遍历*/
Boolean visited[MAXVEX]; /* 访问标志的数组 */
//1. 标识顶点是否被标记过;
//2. 选择从某一个顶点开始(注意:非连通图的情况)
//3. 进入递归,打印i点信息,标识; 边表
//4. [i][j] 是否等于1,没有变遍历过visted
void DFS(MGraph G, int i) {
    //1.
    visited[i] = TRUE;
    printf("%c", G.vexs[i]);

    //2.0~numVertexes
    for (int j = 0; j < G.numVertexes; j++) {
        if (G.arc[i][j] == 1 && !visited[j]) DFS(G, j);
    }
}

void DFSTravese(MGraph G) {
    //1.初始化
    for (int i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE;
    }

    //2.某一个顶点
    for (int i = 0; i < G.numVertexes; i++) {
        if (!visited[i]) {
            DFS(G, i);
        }
    }
}

swift

struct MGraph {
    var vexs: Array<String> = Array<String>()
    var arc: Array<Array<Int>> = Array<Array<Int>>()
    var numNodes: Int = 0, numEdges: Int = 0
    /* DFS遍历*/
    var visited = Array<Bool>() /* 访问标志的数组 */
    mutating func DFSTravese() -> Void {
        visited.removeAll()
        for _ in 0 ..< numNodes {
            visited.append(false)
        }
        for i in 0 ..< numNodes {
            if visited[i] == false {
                DFS(i)
            }
        }
        print("")
    }
    //1. 标识顶点是否被标记过;
    //2. 选择从某一个顶点开始(注意:非连通图的情况)
    //3. 进入递归,打印i点信息,标识; 边表
    //4. [i][j] 是否等于1,没有变遍历过visted
    mutating func DFS(_ i:Int) -> Void {
        //1.标识顶点是否被标记过;
        visited[i] = true;
        print("\(vexs[i])", terminator: "")

        //2.0~numVertexes
        for j in 0 ..< numNodes {
            if arc[i][j] == 1 && visited[j] == false {
                DFS(j)
            }
        }
    }
}

1.2邻接表

注意点:

  1. 利利⽤用邻接矩阵将信息存储到邻接表中
  2. 创建⼀一个visited 数组,⽤用来标识顶点是否已经被遍历过.
  3. 初始化visited 数组,将数组中元素置为FALSE
  4. 选择顶点开始遍历.(注意⾮非连通图的情况)
  5. 进⼊入递归; 打印i 对应的顶点信息. 并将该顶点标识为已遍历.
  6. 循环遍历边表,判断当前顶点 是否等于1,并且当前该顶点没有被遍历过,则继续递归 DFS;
#define OK        1
#define ERROR     0
#define TRUE      1
#define FALSE     0

#define MAXSIZE   9 /* 存储空间初始分配量 */
#define MAXEDGE   15
#define MAXVEX    9
#define INFINITYC 65535

typedef int    Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int    Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef char   VertexType; /* 顶点类型应由用户定义 */
typedef int    EdgeType; /* 边上的权值类型应由用户定义 */
/* 邻接表结构****************** */
typedef struct EdgeNode /* 边表结点 */
{
    int adjvex;    /* 邻接点域,存储该顶点对应的下标 */
    int weight;        /* 用于存储权值,对于非网图可以不需要 */
    struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;

typedef struct VertexNode /* 顶点表结点 */
{
    int in;    /* 顶点入度 */
    char data; /* 顶点域,存储顶点信息 */
    EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];

typedef struct {
    AdjList adjList;
    int numVertexes, numEdges; /* 图中当前顶点数和边数 */
}graphAdjList, *GraphAdjList;

Boolean visited[MAXSIZE]; /* 访问标志的数组 */
/* 邻接表的深度优先递归算法 */
void DFS(GraphAdjList GL, int i) {
    EdgeNode *p;
    visited[i] = TRUE;

    //2.打印顶点 A
    printf("%c ", GL->adjList[i].data);

    p = GL->adjList[i].firstedge;

    //3.
    while (p) {
        if (!visited[p->adjvex]) DFS(GL, p->adjvex);

        p = p->next;
    }
}

/* 邻接表的深度遍历操作 */
void DFSTraverse(GraphAdjList GL) {
    //1. 将访问记录数组默认置为FALSE
    for (int i = 0; i < GL->numVertexes; i++) {
        /*初始化所有顶点状态都是未访问过的状态*/
        visited[i] = FALSE;
    }

    //2. 选择一个顶点开始DFS遍历. 例如A
    for (int i = 0; i < GL->numVertexes; i++) {
        //对未访问过的顶点调用DFS, 若是连通图则只会执行一次.
        if (!visited[i]) DFS(GL, i);
    }
}

swift

// 邻接表的节点
class Node {
    var adj_vex_index: Int // 弧头的下标,也就是被指向的下标
    var data: Int // 权重值
    var next: Node?
    init(adj_vex_index: Int, data: Int) {
        self.adj_vex_index = adj_vex_index
        self.data = data
    }
}

extension Node: CustomStringConvertible {
    var description: String {
        if next == nil {
            return "adj_vex_index:\(adj_vex_index),data:\(data)"
        }
        return "adj_vex_index:\(adj_vex_index),data:\(data),next->" + String(describing: next)
    }
}

// 顶点节点表
class VNode {
    var data: String // 顶点的值
    var firstedge: Node? // 顶点下一个是谁?
    init(data: String) {
        self.data = data
    }
}

extension VNode: CustomStringConvertible {
    var description: String {
        return "data:\(data),firstedge:" + String(describing: firstedge)
    }
}
// 图的一些信息
class Graph {
    var adjlist = Array<VNode>() // 顶点表
    var arc_num = 0 // 边的个数
    var node_num = 0 // 节点个数
    /* DFS遍历*/
    var visited = Array<Bool>() /* 访问标志的数组 */
    func DFSTravese() -> Void {
        visited.removeAll()
        for _ in 0 ..< node_num {
            visited.append(false)
        }
        for i in 0 ..< node_num {
            if visited[i] == false {
                DFS(i)
            }
        }
        print("")
    }
    //1. 标识顶点是否被标记过;
    //2. 选择从某一个顶点开始(注意:非连通图的情况)
    //3. 进入递归,打印i点信息,标识; 边表
    //4. [i][j] 是否等于1,没有变遍历过visted
    func DFS(_ i:Int) -> Void {
        //1.标识顶点是否被标记过;
        visited[i] = true;
        print("\(adjlist[i].data)", terminator: "")

        //2.0~numVertexes
        var p = adjlist[i].firstedge
        while p != nil {
            if visited[p!.adj_vex_index] == false {
                DFS(p!.adj_vex_index)
            }
            p = p?.next
        }
    }
}

2、图的广度优先遍历

注意点:

  1. 把根节点放到队列列的末尾。
  2. 每次从队列列的头部取出⼀一个元素,查看这个元素所有的下⼀一级元素,把它们放到队 列列的末尾。并把这个元素记为它下⼀一级元素的前驱。
  3. 找到所要找的元素时结束程序。
  4. 如果遍历整个树还没有找到,结束程序.

2.1 邻接矩阵

#define OK        1
#define ERROR     0
#define TRUE      1
#define FALSE     0

typedef int    Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int    Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef char   VertexType; /* 顶点类型应由用户定义 */
typedef int    EdgeType; /* 边上的权值类型应由用户定义 */

#define MAXSIZE   9 /* 存储空间初始分配量 */
#define MAXEDGE   15
#define MAXVEX    9
#define INFINITYC 65535

typedef struct {
    VertexType vexs[MAXVEX]; /* 顶点表 */
    EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
    int numVertexes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
/*
 ***需要用到的队列结构与相关功能函数***
 */

/* 循环队列的顺序存储结构 */
typedef struct {
    int data[MAXSIZE];
    int front;       /* 头指针 */
    int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}Queue;

/* 初始化一个空队列Q */
Status InitQueue(Queue *Q) {
    Q->front = 0;
    Q->rear = 0;
    return OK;
}

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(Queue Q) {
    if (Q.front == Q.rear) /* 队列空的标志 */
        return TRUE;
    else return FALSE;
}

/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(Queue *Q, int e) {
    if ((Q->rear + 1) % MAXSIZE == Q->front) /* 队列满的判断 */
        return ERROR;
    Q->data[Q->rear] = e;                    /* 将元素e赋值给队尾 */
    Q->rear = (Q->rear + 1) % MAXSIZE;/* rear指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return OK;
}

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q, int *e) {
    if (Q->front == Q->rear)            /* 队列空的判断 */
        return ERROR;
    *e = Q->data[Q->front];             /* 将队头元素赋值给e */
    Q->front = (Q->front + 1) % MAXSIZE;    /* front指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return OK;
}

/******** Queue End **************/
/* 邻接矩阵广度优先遍历-代码实现*/
Boolean visited[MAXVEX]; /* 访问标志的数组 */
void BFSTraverse(MGraph G) {
    int temp = 0;

    //1.
    Queue Q;
    InitQueue(&Q);

    //2.将访问标志数组全部置为"未访问状态FALSE"
    for (int i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE;
    }

    //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
    for (int i = 0; i < G.numVertexes; i++) {
        if (!visited[i]) {
            visited[i] = TRUE;
            printf("%c  ", G.vexs[i]);

            //4. 入队
            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                //出队
                DeQueue(&Q, &i);
                for (int j = 0; j < G.numVertexes; j++) {
                    if (G.arc[i][j] == 1 && !visited[j]) {
                        visited[j] = TRUE;
                        printf("%c   ", G.vexs[j]);
                        EnQueue(&Q, j);
                    }
                }
            }
        }
    }
}

swift

let MAXSIZE = 20;
struct SqQueue<T> {
    var data = [T?].init(repeating: nil, count: MAXSIZE)
    var front:Int = 0; /* 头位置 */
    var rear:Int = 0;  /* 尾位置,若队列不空,指向队列尾元素的下一个位置 */
    
    // 将队列清空
    mutating func clear() -> Bool {
        front = 0
        rear = 0
        return true;
    }
    
    // 若队列Q为空队列,则返回TRUR,否则返回FALSE;
    func isEmpty() -> Bool {
        return front == rear;
    }
    
    // 返回Q的元素个数,也就是队列的当前长度
    func length() -> Int {
        return ((rear - front) + MAXSIZE) % MAXSIZE ;
    }
    
    // 若队列不空,则返回Q的队头元素,否则返回nil;
    func getHead() -> T? {
        return front == rear ? nil : data[front]
    }
    
    // 若队列未满,则插入元素e为新队尾元素
    mutating func insert(_ e:T) -> Bool {
        //队列已满
        if ((rear + 1) % MAXSIZE) == front {
            return false;
        }
        //将元素e赋值给队尾
        data[rear] = e;
        //rear指针向后移动一位,若到最后则转到数组头部;
        rear = (rear + 1) % MAXSIZE;
        return true;
    }
    
    // 若队列不空,则删除队头的元素,返回删除值
    mutating func delete() -> T? {
        //判断队列是否为空
        if front == rear {
            return nil;
        }
        //将队头元素赋值给temp
        let temp = data[front]
        //front 指针向后移动一位,若到最后则转到数组头部
        front = (front + 1) % MAXSIZE;
        return temp;
    }
    
    func traverse() -> Void {
        var i = front
        while i != rear {
            print("\(String(describing:data[i]))", terminator:"");
            i = (i + 1) % MAXSIZE
        }
        print("");
    }
}
struct MGraph {
    var vexs: Array<String> = Array<String>()
    var arc: Array<Array<Int>> = Array<Array<Int>>()
    var numNodes: Int = 0, numEdges: Int = 0
    /* 邻接矩阵广度优先遍历-代码实现*/
    mutating func BFSTraverse() -> Void {
        visited.removeAll()
        for _ in 0 ..< numNodes {
            visited.append(false)
        }
        var q = SqQueue<Int>()
        //对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
        for i in 0 ..< numNodes {
            if visited[i] == false {
                visited[i] = true;
                print("\(vexs[i])", terminator: "")
                let _ = q.insert(i)
                while !q.isEmpty() {
                    if let a = q.delete() {
                        for j in 0 ..< numNodes {
                            if arc[a][j] == 1 && visited[j] == false {
                                visited[j] = true;
                                print("\(vexs[j])", terminator: "")
                                let _ = q.insert(j)
                            }
                        }
                    }
                }
            }
        }
        print("")
    }
}

2.2邻接表

#define OK        1
#define ERROR     0
#define TRUE      1
#define FALSE     0

#define MAXSIZE   9 /* 存储空间初始分配量 */
#define MAXEDGE   15
#define MAXVEX    9
#define INFINITYC 65535

typedef int    Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef int    Boolean; /* Boolean是布尔类型,其值是TRUE或FALSE */
typedef char   VertexType; /* 顶点类型应由用户定义 */
typedef int    EdgeType; /* 边上的权值类型应由用户定义 */

/* 邻接表结构 */
typedef struct EdgeNode {/* 边表结点 */
    int adjvex;    /* 邻接点域,存储该顶点对应的下标 */
    int weight;        /* 用于存储权值,对于非网图可以不需要 */
    struct EdgeNode *next; /* 链域,指向下一个邻接点 */
}EdgeNode;

typedef struct VertexNode {/* 顶点表结点 */
    int in;    /* 顶点入度 */
    char data; /* 顶点域,存储顶点信息 */
    EdgeNode *firstedge;/* 边表头指针 */
}VertexNode, AdjList[MAXVEX];

typedef struct {
    AdjList adjList;
    int numVertexes, numEdges; /* 图中当前顶点数和边数 */
}graphAdjList, *GraphAdjList;
/*
 ***需要用到的队列结构与相关功能函数***
 */
/* 循环队列的顺序存储结构 */
typedef struct {
    int data[MAXSIZE];
    int front;       /* 头指针 */
    int rear;        /* 尾指针,若队列不空,指向队列尾元素的下一个位置 */
}Queue;

/* 初始化一个空队列Q */
Status InitQueue(Queue *Q) {
    Q->front = 0;
    Q->rear = 0;
    return OK;
}

/* 若队列Q为空队列,则返回TRUE,否则返回FALSE */
Status QueueEmpty(Queue Q) {
    if (Q.front == Q.rear) /* 队列空的标志 */
        return TRUE;
    else return FALSE;
}

/* 若队列未满,则插入元素e为Q新的队尾元素 */
Status EnQueue(Queue *Q, int e) {
    if ((Q->rear + 1) % MAXSIZE == Q->front) /* 队列满的判断 */
        return ERROR;
    Q->data[Q->rear] = e;                    /* 将元素e赋值给队尾 */
    Q->rear = (Q->rear + 1) % MAXSIZE;/* rear指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return OK;
}

/* 若队列不空,则删除Q中队头元素,用e返回其值 */
Status DeQueue(Queue *Q, int *e) {
    if (Q->front == Q->rear)            /* 队列空的判断 */
        return ERROR;
    *e = Q->data[Q->front];             /* 将队头元素赋值给e */
    Q->front = (Q->front + 1) % MAXSIZE;    /* front指针向后移一位置, */
    /* 若到最后则转到数组头部 */
    return OK;
}

/* *********************** Queue End ******************************* */
/*3 邻接表广度优先遍历*/
Boolean visited[MAXSIZE]; /* 访问标志的数组 */
void BFSTraverse(GraphAdjList GL) {
    //1.创建结点
    EdgeNode *p;

    Queue Q;
    InitQueue(&Q);

    //2.将访问标志数组全部置为"未访问状态FALSE"
    for (int i = 0; i < GL->numVertexes; i++) {
        visited[i] = FALSE;
    }

    //3.对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
    for (int i = 0; i < GL->numVertexes; i++) {
        //4.判断当前结点是否被访问过.
        if (!visited[i]) {
            visited[i] = TRUE;
            //打印顶点
            printf("%c ", GL->adjList[i].data);

            EnQueue(&Q, i);
            while (!QueueEmpty(Q)) {
                DeQueue(&Q, &i);
                p = GL->adjList[i].firstedge;
                while (p) {
                    //判断
                    if (!visited[p->adjvex]) {
                        visited[p->adjvex] = TRUE;
                        printf("%c ", GL->adjList[p->adjvex].data);
                        EnQueue(&Q, p->adjvex);
                    }
                    p = p->next;
                }
            }
        }
    }
}

swift

let MAXSIZE = 20;
struct SqQueue<T> {
    var data = [T?].init(repeating: nil, count: MAXSIZE)
    var front:Int = 0; /* 头位置 */
    var rear:Int = 0;  /* 尾位置,若队列不空,指向队列尾元素的下一个位置 */
    
    // 将队列清空
    mutating func clear() -> Bool {
        front = 0
        rear = 0
        return true;
    }
    
    // 若队列Q为空队列,则返回TRUR,否则返回FALSE;
    func isEmpty() -> Bool {
        return front == rear;
    }
    
    // 返回Q的元素个数,也就是队列的当前长度
    func length() -> Int {
        return ((rear - front) + MAXSIZE) % MAXSIZE ;
    }
    
    // 若队列不空,则返回Q的队头元素,否则返回nil;
    func getHead() -> T? {
        return front == rear ? nil : data[front]
    }
    
    // 若队列未满,则插入元素e为新队尾元素
    mutating func insert(_ e:T) -> Bool {
        //队列已满
        if ((rear + 1) % MAXSIZE) == front {
            return false;
        }
        //将元素e赋值给队尾
        data[rear] = e;
        //rear指针向后移动一位,若到最后则转到数组头部;
        rear = (rear + 1) % MAXSIZE;
        return true;
    }
    
    // 若队列不空,则删除队头的元素,返回删除值
    mutating func delete() -> T? {
        //判断队列是否为空
        if front == rear {
            return nil;
        }
        //将队头元素赋值给temp
        let temp = data[front]
        //front 指针向后移动一位,若到最后则转到数组头部
        front = (front + 1) % MAXSIZE;
        return temp;
    }
    
    func traverse() -> Void {
        var i = front
        while i != rear {
            print("\(String(describing:data[i]))", terminator:"");
            i = (i + 1) % MAXSIZE
        }
        print("");
    }
}

// 邻接表的节点
class Node {
    var adj_vex_index: Int // 弧头的下标,也就是被指向的下标
    var data: Int // 权重值
    var next: Node?
    init(adj_vex_index: Int, data: Int) {
        self.adj_vex_index = adj_vex_index
        self.data = data
    }
}

extension Node: CustomStringConvertible {
    var description: String {
        if next == nil {
            return "adj_vex_index:\(adj_vex_index),data:\(data)"
        }
        return "adj_vex_index:\(adj_vex_index),data:\(data),next->" + String(describing: next)
    }
}

// 顶点节点表
class VNode {
    var data: String // 顶点的值
    var firstedge: Node? // 顶点下一个是谁?
    init(data: String) {
        self.data = data
    }
}

extension VNode: CustomStringConvertible {
    var description: String {
        return "data:\(data),firstedge:" + String(describing: firstedge)
    }
}
// 图的一些信息
class Graph {
    var adjlist = Array<VNode>() // 顶点表
    var arc_num = 0 // 边的个数
    var node_num = 0 // 节点个数
    func BFSTraverse() -> Void {
        visited.removeAll()
        for _ in 0 ..< node_num {
            visited.append(false)
        }
        var q = SqQueue<Int>()
        //对遍历邻接表中的每一个顶点(对于连通图只会执行1次,这个循环是针对非连通图)
        for i in 0 ..< node_num {
            if visited[i] == false {
                visited[i] = true;
                print("\(adjlist[i].data)", terminator: "")
                let _ = q.insert(i)
                while !q.isEmpty() {
                    if let a = q.delete() {
                        var p = adjlist[a].firstedge
                        while p != nil {
                            if visited[p!.adj_vex_index] == false {
                                visited[p!.adj_vex_index] = true
                                print("\(adjlist[p!.adj_vex_index].data)", terminator: "")
                                let _ = q.insert(p!.adj_vex_index)
                            }
                            p = p?.next
                        }
                    }
                }
            }
        }
        print("")
    }
}
extension Graph: CustomStringConvertible {
    var description: String {
        return "arc_num:\(arc_num),node_num:\(node_num),adjlist:" + String(describing: adjlist)
    }
}