美文网首页
03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵

03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵

作者: 一直流浪 | 来源:发表于2022-09-08 07:54 被阅读0次

03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵)

图论是一个很重要的工具,这节主要是图的创建和遍历的Java代码,不讲理论,只撸代码,理论网上很多,具体一步步该怎么走,其他的贴子也都给全了,但是都是c语言,我们用Java实现和模拟图论。

内容有点多,给个目录:

目录:

1. 图结点的创建

2. 图的创建

  **2.1 图的结构和初始化**     

  **2.2 邻接矩阵**   

  **2.3 邻接表**

3. 邻接表的两种遍历

 **3.1 深度优先遍历(邻接表)**

       **3.1.1 递归算法(邻接表)**

       **3.1.2 非递归算法(邻接表)**

 **3.2 广度优先遍历(邻接表)**

4. 邻接矩阵的两种遍历

  **4.1 深度优先遍历(邻接矩阵)**

       **4.1.1 递归算法(邻接矩阵)**

        **4.1.2 非递归算法(邻接矩阵)**

  **4.2  广度优先搜索(邻接矩阵)**

5. 项目完整代码

1. 图结点的创建

public class Node {
    public int name;
    public Node next;
    public Boolean flag;
    
    public Node() {
        // TODO Auto-generated constructor stub
        this.name = 0;
        this.next = null;
        this.flag = false;
    }
    public Node(int name) {
        
        this.name = name;
        this.next = null;
        this.flag = false;
    }
    
}

2. 图的创建

2.1 图的结构和初始化

import java.util.Scanner;

public class Graph {
    public Node[] nodes;
    public int[][] A;
    public int Sidenum;
    public Seqqueue queue;
    public Seqstack stack;
    Scanner scanner;

    public Graph(int Nodenum, int Sidenum) {
        this.nodes = new Node[Nodenum];
        for (int i = 0; i < Nodenum; i++) {
            this.nodes[i] = new Node(i);
        }
        this.A = new int[Nodenum][Nodenum];
        this.Sidenum = Sidenum;
        for (int i = 0; i < Nodenum; i++) {
            for (int j = 0; j < Nodenum; j++) {
                this.A[i][j] = 0;
            }
        }
        scanner = new Scanner(System.in);
        this.queue = new Seqqueue();
        this.queue.initQueue(20);
        this.stack = new Seqstack();
        this.stack.InitStack(20);

    }
}

2.2 邻接矩阵

    // 邻接矩阵创建(有向图)
    void creat1() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            this.A[a][b] = 1;
        }
        System.out.println("图创建成功");
    }

    // 邻接矩阵创建(无向图)
    void creat2() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            this.A[a][b] = 1;
            this.A[b][a] = 1;
        }
        System.out.println("图创建成功");
    }

    // 遍历输出邻接矩阵
    void traver1() {
        for (int i = 0; i < nodes.length; i++) {

            for (int j = 0; j < nodes.length; j++) {
                System.out.print(this.A[i][j] + " ");
            }
            System.out.println("");
        }
    }

2.3 邻接表

    // 邻接表创建
    void creat3() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            Node node = this.nodes[a];

            // 防止把同一条边加入多次
            while (node.next != null && node.next.name != b) {
                node = node.next;
            }
            if (node.next == null) {
                Node n = new Node(b);
                n.next = nodes[a].next;
                nodes[a].next = n;
            }
        }
        System.out.println("图创建成功");
    }

    // 遍历输出邻接表
    void traver2() {
        Node node;
        for (int i = 0; i < nodes.length; i++) {
            node = nodes[i];
            System.out.print(node.name + ":");
            while (node.next != null) {
                System.out.print(node.next.name + " ");
                node = node.next;
            }
            System.out.println("");
        }
    }

3. 邻接表的两种遍历

flag属性用来标记结点有没有被访问过,所以需要初始化。

    // 初始化访问标记
    void initFlag() {
        // 初始化
        for (int i = 0; i < this.nodes.length; i++) {
            this.nodes[i].flag = false;
        }
    }

3.1 深度优先遍历(邻接表)

3.1.1 递归算法(邻接表)

    // 深度优先遍历(从head结点开始) 递归算法 邻接表
    void DFS1(Node head) {
        Node pNode;
        if (!this.nodes[head.name].flag) {
            System.out.print(head.name + " ");
            this.nodes[head.name].flag = true;
        }

        pNode = this.nodes[head.name].next;
        while (pNode != null) {
            if (!this.nodes[pNode.name].flag) {
                DFS1(pNode);
            } else {
                pNode = pNode.next;
            }

        }
    }

3.1.2 非递归算法(邻接表)

用到的栈,定义在文章最后完整项目代码中有,这块只是引用。

    // 深度优先遍历(从head结点开始) 非递归算法 邻接表
    void DFS2(Node head) {
        // 初始化访问标记
        initFlag();
        // 初始化栈
        this.stack.InitStack(20);
        Node pNode, wNode;
        this.stack.push(head);
        System.out.print(head.name + " ");
        this.nodes[head.name].flag = true;
        while (!this.stack.IsEmpty()) {
            pNode = this.stack.pop();
            wNode = pNode.next;
            while (wNode != null) {
                if (!this.nodes[wNode.name].flag) {
                    this.stack.push(wNode);
                    this.nodes[wNode.name].flag = true;
                    System.out.print(wNode.name + " ");
                    wNode = this.nodes[wNode.name].next;
                } else {
                    wNode = wNode.next;
                }
            }
        }
    }

3.2 广度优先遍历(邻接表)

因为广度优先遍历需要用到队列,所以不能递归,递归只能是用到栈时才能。

用到的队列,定义在文章最后完整项目代码中有,这块只是引用。

    void BFS1(Node head) {
        // 初始化访问标记
        initFlag();
        // 初始化队列
        this.queue.initQueue(20);
        Node pNode;
        this.queue.enterQueue(head);
        this.nodes[head.name].flag = true;
        while (!this.queue.isEmpty()) {
            pNode = this.queue.deleteQueue();
            pNode = this.nodes[pNode.name];
            System.out.print(pNode.name + " ");
            while (pNode != null) {
                pNode = pNode.next;
                if (pNode != null && !this.nodes[pNode.name].flag) {
                    this.queue.enterQueue(pNode);
                    this.nodes[pNode.name].flag = true;
                }
            }
        }

    }

4. 邻接矩阵的两种遍历

4.1 深度优先遍历(邻接矩阵)

4.1.1 递归算法(邻接矩阵)

    // 深度优先遍历(从0号结点开始) 递归算法 邻接矩阵
    void DFSM(int i) {
        int j = 0;
        System.out.print(this.nodes[i].name + " ");
        this.nodes[i].flag = true;
        for (j = 0; j < this.Sidenum; j++) {
            if (this.A[i][j] == 1 && !this.nodes[j].flag) {
                DFSM(j);
            }
        }
    }

    void DFS3() {
        for (int i = 0; i < this.Sidenum; i++) {
            if (!this.nodes[i].flag) {
                DFSM(i);
            }
        }
    }

4.1.2 非递归算法(邻接矩阵)

用到的栈,定义在文章最后完整项目代码中有,这块只是引用。

    // 深度优先遍历(从0号结点开始) 非递归算法 邻接矩阵
    void DFS4(Node head) {
        // 初始化访问标记
        initFlag();
        int i = 0;
        // 初始化栈
        this.stack.InitStack(20);
        Node pNode, wNode = null;
        this.stack.push(head);
        System.out.print(head.name + " ");
        this.nodes[head.name].flag = true;
        while (!this.stack.IsEmpty()) {
            pNode = this.stack.getTop();
            for (i = 0; i < this.Sidenum; i++) {
                if (this.A[pNode.name][i] == 1 && !this.nodes[i].flag) {
                    System.out.print(this.nodes[i].name + " ");
                    this.nodes[i].flag = true;
                    this.stack.push(this.nodes[i]);
                    break;
                }
            }
            if (i == this.Sidenum) {
                this.stack.pop();
            }

        }
    }

4.2 广度优先搜索(邻接矩阵)

因为广度优先遍历需要用到队列,所以不能递归,递归只能是用到栈时才能。

用到的队列,定义在文章最后完整项目代码中有,这块只是引用。

    // 广度优先遍历(从0号结点开始) 非递归算法 邻接矩阵
    void BFS2(int k) {
        // 初始化被访问标记
        initFlag();
        // 初始化队列
        this.queue.initQueue(20);
        Node node;
        System.out.print(this.nodes[k].name + " ");
        this.nodes[k].flag = true;
        this.queue.enterQueue(this.nodes[k]);
        while (!this.queue.isEmpty()) {
            node = this.queue.deleteQueue();
            for (int i = 0; i < this.Sidenum; i++) {
                if (this.A[node.name][i] == 1 && !this.nodes[i].flag) {
                    System.out.print(this.nodes[i].name + " ");
                    this.nodes[i].flag = true;
                    this.queue.enterQueue(this.nodes[i]);
                }
            }
        }
    }

5. 项目完整代码

项目的代码比较多,主要有下面几个类,防止查找麻烦,目录如下:

目录:

  1. class Node 结点定义
  2. class Seqqueue 队列定义
  3. class Seqstack 栈的定义
  4. class Graph 图的定义及操作
  5. class Client 测试类

内容:

  1. //class Node 结点定义
package com.company.project.graph;

public class Node {
    public int name;
    public Node next;
    public Boolean flag;
    
    public Node() {
        // TODO Auto-generated constructor stub
        this.name = 0;
        this.next = null;
        this.flag = false;
    }
    public Node(int name) {
        
        this.name = name;
        this.next = null;
        this.flag = false;
    }   
}

\2. //class Seqqueue 队列定义

package com.company.project.graph;

public class Seqqueue {
    public Node[] queue;
    public int QUEUE_SIZE;
    public int front;
    public int rear;
    
    //初始化队列
        public void initQueue(int size) {
            this.QUEUE_SIZE = size;
            this.queue = new Node[size];
            this.front = 0;
            this.rear = 0;      
        }
        
        //判断队列是否为空
        public boolean isEmpty() {
            if(this.front  == this.rear ) {
                return true;
            }
            else {
                return false;
            }
        }
        
        //判断队列是否为满
        public boolean isFull() {
            if((this.front % this.QUEUE_SIZE) == ((this.rear+1) % this.QUEUE_SIZE)) {
                return true;
            }
            else {
                return false;
            }
        }
        
        //入队
        public void enterQueue(Node node) {
            if(isFull()) {
                System.out.println("队列已经满了,入队失败");
                return;     
            }
            this.queue[this.rear] = node;
            this.rear ++;
            this.rear = this.rear % this.QUEUE_SIZE;
            
        }
        //出队
        public Node deleteQueue() {
            if(isEmpty()){
                System.out.println("队列为空,出队失败");
                return null;
            }
            this.front = this.front % this.QUEUE_SIZE;
            Node node = this.queue[this.front];
            
            this.front++;
            return node;
        }
}

\3. //class Seqstack 栈的定义

package com.company.project.graph;

public class Seqstack {
    // 节点数组(模拟顺序栈)
    public Node[] nodes;

    // 栈顶下标
    public int top;

    public Seqstack() {
        // TODO Auto-generated constructor stub
        this.top = -1;

    }

    // 初始化栈
    public void InitStack(int size) {
        this.nodes = new Node[size];
        this.top = -1;

    }

    // 判断栈空
    public boolean IsEmpty() {
        if (this.top == -1) {
            return true;
        }

        return false;
    }

    // 判断栈满
    public boolean IsFull() {
        if (this.top == this.nodes.length - 1) {
            return true;
        }

        return false;
    }

    // 入栈
    public void push(Node node) {
        if (IsFull()) {
            System.out.println("栈已经满了,入栈失败");
            return;
        }
        this.top++;
        this.nodes[this.top] = node;
    }

    // 出栈
    public Node pop() {
        if (IsEmpty()) {
            System.out.println("栈已经空了,出栈失败");
            return null;
        }
        Node node = this.nodes[this.top];
        this.top--;
        return node;

    }

    // 清空栈
    public void ClearStack() {
        // 如果不为空,就一直出栈
        while (!IsEmpty()) {
            Node node = pop();
        }

    }

    // 拿到栈顶元素
    public Node getTop() {
        if (IsEmpty()) {
            System.out.println("栈中没有元素");
            return null;
        }
        return this.nodes[this.top];
    }
}

\4. //class Graph 图的定义及操作

package com.company.project.graph;

import java.util.Scanner;

public class Graph {
    public Node[] nodes;
    public int[][] A;
    public int Sidenum;
    public Seqqueue queue;
    public Seqstack stack;
    Scanner scanner;

    public Graph(int Nodenum, int Sidenum) {
        this.nodes = new Node[Nodenum];
        for (int i = 0; i < Nodenum; i++) {
            this.nodes[i] = new Node(i);
        }
        this.A = new int[Nodenum][Nodenum];
        this.Sidenum = Sidenum;
        for (int i = 0; i < Nodenum; i++) {
            for (int j = 0; j < Nodenum; j++) {
                this.A[i][j] = 0;
            }
        }
        scanner = new Scanner(System.in);
        this.queue = new Seqqueue();
        this.queue.initQueue(20);
        this.stack = new Seqstack();
        this.stack.InitStack(20);

    }

    // 邻接矩阵创建(有向图)
    void creat1() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            this.A[a][b] = 1;
        }
        System.out.println("图创建成功");
    }

    // 邻接矩阵创建(无向图)
    void creat2() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            this.A[a][b] = 1;
            this.A[b][a] = 1;
        }
        System.out.println("图创建成功");
    }

    // 邻接表创建
    void creat3() {
        int a, b;
        for (int i = 0; i < this.Sidenum; i++) {
            a = scanner.nextInt();
            b = scanner.nextInt();
            Node node = this.nodes[a];

            // 防止把同一条边加入多次
            while (node.next != null && node.next.name != b) {
                node = node.next;
            }
            if (node.next == null) {
                Node n = new Node(b);
                n.next = nodes[a].next;
                nodes[a].next = n;
            }
        }
        System.out.println("图创建成功");
    }

    // 遍历输出邻接矩阵
    void traver1() {
        for (int i = 0; i < nodes.length; i++) {

            for (int j = 0; j < nodes.length; j++) {
                System.out.print(this.A[i][j] + " ");
            }
            System.out.println("");
        }
    }

    // 遍历输出邻接表
    void traver2() {
        Node node;
        for (int i = 0; i < nodes.length; i++) {
            node = nodes[i];
            System.out.print(node.name + ":");
            while (node.next != null) {
                System.out.print(node.next.name + " ");
                node = node.next;
            }
            System.out.println("");
        }
    }

//邻接表的两种遍历(递归和非递归)
    // 初始化访问标记
    void initFlag() {
        // 初始化
        for (int i = 0; i < this.nodes.length; i++) {
            this.nodes[i].flag = false;
        }
    }

    // 深度优先遍历(从head结点开始) 递归算法 邻接表
    void DFS1(Node head) {
        Node pNode;
        if (!this.nodes[head.name].flag) {
            System.out.print(head.name + " ");
            this.nodes[head.name].flag = true;
        }

        pNode = this.nodes[head.name].next;
        while (pNode != null) {
            if (!this.nodes[pNode.name].flag) {
                DFS1(pNode);
            } else {
                pNode = pNode.next;
            }

        }
    }

    // 深度优先遍历(从head结点开始) 非递归算法 邻接表
    void DFS2(Node head) {
        // 初始化访问标记
        initFlag();
        // 初始化栈
        this.stack.InitStack(20);
        Node pNode, wNode;
        this.stack.push(head);
        System.out.print(head.name + " ");
        this.nodes[head.name].flag = true;
        while (!this.stack.IsEmpty()) {
            pNode = this.stack.pop();
            wNode = pNode.next;
            while (wNode != null) {
                if (!this.nodes[wNode.name].flag) {
                    this.stack.push(wNode);
                    this.nodes[wNode.name].flag = true;
                    System.out.print(wNode.name + " ");
                    wNode = this.nodes[wNode.name].next;
                } else {
                    wNode = wNode.next;
                }
            }
        }
    }

    // 广度优先遍历(从head结点开始)(非递归) 邻接表
    // 因为广度优先遍历需要用到队列,所以不能递归,递归只能是用到栈时才能
    void BFS1(Node head) {
        // 初始化访问标记
        initFlag();
        // 初始化队列
        this.queue.initQueue(20);
        Node pNode;
        this.queue.enterQueue(head);
        this.nodes[head.name].flag = true;
        while (!this.queue.isEmpty()) {
            pNode = this.queue.deleteQueue();
            pNode = this.nodes[pNode.name];
            System.out.print(pNode.name + " ");
            while (pNode != null) {
                pNode = pNode.next;
                if (pNode != null && !this.nodes[pNode.name].flag) {
                    this.queue.enterQueue(pNode);
                    this.nodes[pNode.name].flag = true;
                }
            }
        }

    }

//邻接矩阵的两种遍历(递归和非递归)邻接矩阵 

    // 深度优先遍历(从0号结点开始) 递归算法 邻接矩阵
    void DFSM(int i) {
        int j = 0;
        System.out.print(this.nodes[i].name + " ");
        this.nodes[i].flag = true;
        for (j = 0; j < this.Sidenum; j++) {
            if (this.A[i][j] == 1 && !this.nodes[j].flag) {
                DFSM(j);
            }
        }
    }

    void DFS3() {
        for (int i = 0; i < this.Sidenum; i++) {
            if (!this.nodes[i].flag) {
                DFSM(i);
            }
        }
    }

    // 深度优先遍历(从0号结点开始) 非递归算法 邻接矩阵
    void DFS4(Node head) {
        // 初始化访问标记
        initFlag();
        int i = 0;
        // 初始化栈
        this.stack.InitStack(20);
        Node pNode, wNode = null;
        this.stack.push(head);
        System.out.print(head.name + " ");
        this.nodes[head.name].flag = true;
        while (!this.stack.IsEmpty()) {
            pNode = this.stack.getTop();
            for (i = 0; i < this.Sidenum; i++) {
                if (this.A[pNode.name][i] == 1 && !this.nodes[i].flag) {
                    System.out.print(this.nodes[i].name + " ");
                    this.nodes[i].flag = true;
                    this.stack.push(this.nodes[i]);
                    break;
                }
            }
            if (i == this.Sidenum) {
                this.stack.pop();
            }

        }
    }

    // 广度优先遍历(从0号结点开始) 非递归算法 邻接矩阵
    void BFS2(int k) {
        // 初始化被访问标记
        initFlag();
        // 初始化队列
        this.queue.initQueue(20);
        Node node;
        System.out.print(this.nodes[k].name + " ");
        this.nodes[k].flag = true;
        this.queue.enterQueue(this.nodes[k]);
        while (!this.queue.isEmpty()) {
            node = this.queue.deleteQueue();
            for (int i = 0; i < this.Sidenum; i++) {
                if (this.A[node.name][i] == 1 && !this.nodes[i].flag) {
                    System.out.print(this.nodes[i].name + " ");
                    this.nodes[i].flag = true;
                    this.queue.enterQueue(this.nodes[i]);
                }
            }
        }
    }
}

\5. //class Client 测试类(里面有邻接表和邻接矩阵 的全部操作测试,需要那个,把剩余的注释就行)

package com.company.project.graph;

import java.util.Scanner;

public class Client {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入顶点数:");
        int Nodenum = scanner.nextInt();
        System.out.println("输入边数:");
        int Sidenum = scanner.nextInt();
        Graph graph = new Graph(Nodenum, Sidenum);
// 邻接矩阵创建
        // 邻接矩阵创建
        System.out.println("输入边的两个顶点:");
        graph.creat1();
        graph.traver1();
// 邻接表创建
        // 邻接表创建
        System.out.println("输入边的两个顶点:");
        graph.creat3();
        System.out.println("邻接表:");
        graph.traver2();

//邻接表的搜索
        // 深度优先搜索
        System.out.println("深度优先搜索(递归)");
        graph.initFlag();
        graph.DFS1(graph.nodes[0]);
        System.out.println("");

        // 深度优先搜索
        System.out.println("深度优先搜索(非递归)");
        graph.DFS2(graph.nodes[0]);
        System.out.println("");

        // 广度优先遍历
        System.out.println("广度优先搜索(非递归)");
        graph.BFS1(graph.nodes[0]);

//邻接矩阵的搜索
        // 深度优先搜索
        System.out.println("深度优先搜索(递归)");
        graph.initFlag();
        graph.DFS3();
        System.out.println("");
        
        // 深度优先搜索
        System.out.println("深度优先搜索(非递归)");
        graph.initFlag();
        graph.DFS4(graph.nodes[0]);
        System.out.println("");
        
        // 广度优先搜索
        System.out.println("广度优先搜索(非递归)");
        graph.initFlag();
        graph.BFS2(0);
        System.out.println("");

    }
}

相关文章

  • 03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵

    03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵) 图论是一个很重要的工具,这节主要是图的创建和遍...

  • 1、邻接矩阵 2、邻接表 3、广度优先遍历 4、深度优先遍历①递归 ②非递归 Dijkstra算法 Floyd-W...

  • 图的遍历

    1.采用深度优先搜索(DFS)遍历图 邻接矩阵: 邻接表: 2.采用广度优先搜索(BFS)遍历图 邻接矩阵: 邻接...

  • 数据结构—图的遍历

    根据图的存储方式可分为邻接矩阵的深度优先遍历和邻接表的深度优先遍历。 一、深度优先遍历 1、邻接矩阵的深度优先遍历...

  • 5. 深度优先、广度优先

    1. 二叉树的深度优先遍历和广度优先遍历2. 深度优先搜索递归和非递归实现 深度优先(DFS):前序遍历 广度优先...

  • 深度优先遍历和广度优先遍历

    以html节点为列进行深度优先和广度优先遍历 1. 深度优先遍历 递归 非递归,栈表示法 2. 广度优先遍历 队列表示法

  • 图存存储 邻接矩阵 无向图会比较浪费空间稀疏图也会 邻接表存储 逆邻接表存储 深度和广度优先搜索 广度优先搜索

  • 【转】js数组和树结构数据相互转换

    数组转树结构采取递归和非递归两种方式,树结构转扁平化数组采取深度优先遍历(递归和非递归两种方式)和广度优先遍历实现...

  • 数据结构-图

    邻接矩阵中的两个最短路径算法,Djkstra,Floyd 以邻接表存储的两种遍历,深度优先遍历和广度优先遍历,类比...

  • 前端面试考点之数据结构

    1、深度优先和广度优先的区别 1) 二叉树的深度优先遍历的非递归的通用做法是采用栈,广度优先遍历的非递归的通用做法...

网友评论

      本文标题:03.图的深度和广度优先遍历(递归和非递归都有,邻接表和邻接矩阵

      本文链接:https://www.haomeiwen.com/subject/pjbqwrtx.html