首页 > 编程语言 >数据结构之图(java语言版)

数据结构之图(java语言版)

时间:2024-04-11 11:57:04浏览次数:26  
标签:优先 遍历 java int public 输入 顶点 之图 语言版

图是比树更复杂的结构,树是一对多的关系,图是多对多的关系。

一、基本概念

1、定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。

2、根据边是否有方向,将图可以划分为:无向图有向图

3、度,在无向图中,某个顶点的度是邻接到该顶点的边(或弧)的数目。

在有向图中,度还有"入度"和"出度"之分。
某个顶点的入度,是指以该顶点为终点的边的数目。而顶点的出度,则是指以该顶点为起点的边的数目。
顶点的度=入度+出度。

4、弧头和弧尾

有向图中:用<A,B>,<B,C>,<B,F>,A->B,A是弧尾,B是 弧头。

无向图中:用(A,B),(A,C),(B,C),弧头和弧尾没有区别。

5、权

弧如果有值的话,称为权。

二、图的存储结构

图的存储结构有许多种,有邻接矩阵,邻接表,十字链表等。

邻接矩阵

用矩阵表示,用线性表存储数据,直观简单,但是浪费空间。

在这里插入图片描述

邻接表

用数组和链表表示结构,节省空间,可伸缩。

数组中存储了链表,链表的头节点代表定点,存储着数据及下一个顶点的引用,后面的节点存储着下标和下一个顶点的引用。

在这里插入图片描述

在这里插入图片描述

图来自《大话数据结构》

邻接表实现图

顶点及索引结构

顶点存储着数据和索引。

索引结构存储着顶点在数组中的下标。

class VexNode{//顶点
    String data;//顶点的数据
    EdgeNode fnext;//指向下一个顶点
    public VexNode(){}
    public VexNode(String data){
        this.data=data;
    }

}

class EdgeNode{//存储索引的节点
    int index;//索引
    //int weight;//权重
    EdgeNode next;//指向下一个顶点
}

建立图

使用数组存储链表。

构造方法传入顶点数和弧数。

public class Graph{

    public VexNode[] vexTable;//表
    private int numNode;//顶点数量
    private int size;//弧数
    
    public Graph(int numNode,int size){//构造函数,确定顶点数量
        this.numNode=numNode;
        this.size=size;
        vexTable=new VexNode[numNode];
    }

    public VexNode[] init(){//建立邻接表

        Scanner scanner =new Scanner(System.in);
        for(int i=0;i<numNode;i++){
            System.out.println("输入顶点数据");
            String data=scanner.next();
            VexNode node=new VexNode(data);
            vexTable[i]=node;
        }

        System.out.println("输入弧数据:");
        for(int k=0;k<size;k++){
            EdgeNode eNode=new EdgeNode();
            System.out.println("输入弧头:");
            int x=scanner.nextInt();
            System.out.println("输入弧尾:");
            int y=scanner.nextInt();

            eNode.index=y;
            eNode.next=vexTable[x].fnext;//头插法插入链中
            vexTable[x].fnext=eNode;

             eNode=new EdgeNode();//无向表需要弧头弧尾相同
             eNode.index=x;//如果建立的是有向表,将这四行代码去除即可
             eNode.next=vexTable[y].fnext;
             vexTable[y].fnext=eNode;

        }

        return vexTable;
    } 
}

查看弧

图的遍历比较复杂,我们可以先通过弧来查看图是否正确。

public void display(VexNode[] vexTable){
        System.out.println("打印表:");
        for(int i=0;i<numNode;i++){
            EdgeNode v=vexTable[i].fnext;
            while(v!=null){
                System.out.printf("(%s %s) ",vexTable[i].data,vexTable[v.index].data);
                v=v.next;
            }
            System.out.println();
        }
    }

用以下图来表示,数据输入顺序代表数据在数组中的位置。所以顶点输入顺序是

abcde

弧的输入顺序代表方向,不过我们建立的表是无向图,所以无所谓。

在这里插入图片描述

主函数中测试:

    public static void main(String[] args) {
        Graph graph=new Graph(5,5);
        VexNode[] vexTable=graph.init();
        graph.display(vexTable);
        
    }

输入数据:

输入顶点数据
a
输入顶点数据
b
输入顶点数据
c
输入顶点数据
d
输入顶点数据
e
输入弧数据:
输入弧头:
0
输入弧尾:
1
输入弧头:
1
输入弧尾:
3
输入弧头:
3
输入弧尾:
4
输入弧头:
4
输入弧尾:
2
输入弧头:
2
输入弧尾:
0

得到结果:

无向图会打印两次,有向图只有一个指向只会打印一次。

打印表:
(a c) (a b)
(b d) (b a)
(c a) (c e)
(d e) (d b)
(e c) (e d)

遍历

图的遍历有深度优先和广度优先。

深度优先遍历是从图中某个顶点出发,访问此顶点,然后从它未被访问到的邻接点出发深度优先遍历图,直到图中所有和它有路径相通的顶点都被访问到.,类似树的先序遍历。

广度优先遍历从某个顶点出发,访问其所有相邻元素,再从某个相邻元素开始广度优先遍历,类似树的层级遍历。

深度优先遍历

对遍历过的点需要标记,以免重复遍历。

同样可以递归来遍历。

	private int[] visit;//标记顶点是否遍历,1为已遍历,0为未遍历

    //深度优先遍历
    public void DFS(VexNode[] vexTable){
        this.visit=new int[numNode];//默认所有顶点都未遍历,数组中值都为0
        System.out.println("深度优先遍历:");
        for(int i=0;i<numNode;i++){
            if(visit[i]==0){    
                DFSVisit(vexTable, i);
            }
        }
        System.out.println();
    }

   public void DFSVisit(VexNode[] vexTable,int i){//对为遍历的数据输出
        EdgeNode eNode;
        visit[i]=1;//该顶点已经遍历
        System.out.print(" "+vexTable[i].data);
        eNode=vexTable[i].fnext;

        while(eNode!=null){
            if(visit[eNode.index]==0){
                DFSVisit(vexTable, eNode.index);
            }
            eNode=eNode.next;
        }
    }

广度优先遍历

广度优先遍历的特点是我们需要一个先进先出的容器来保存顶点。

使用队列即可,这里的队列可以采用之前的队列,也可以通过java已经提供好的LinkedList类来实现。

我们采用之前的链表队列:

public class Queue{
    SingleLinkList list;
    public Queue(){
        list=new SingleLinkList();
    }

    public void enQueue(Object e) {
        list.add(e);
        System.out.println("入队");
	}
	
	public Object deQueue() {
        Object e=list.get(1);
        list.remove(1);
        System.out.println("出队");
		return e;
	}
	
	public void display() {
		list.display();
    }
    
    public int getSize(){
        return list.getSize();
    }
}

广度优先如下:

    private Queue queue;
    //广度优先遍历
    public void BFS(){
        this.visit=new int[numNode];//默认所有顶点都未遍历,数组中值都为0
        System.out.println("广度优先遍历:");
        this.queue=new Queue();//建立队列

        for(int i=0;i<numNode;i++){
            if(visit[i]==0){
                BFSVisit();
            }
        }
        System.out.println();
    }

    public void BFSVisit(){//遍历操作
        for(int i=0;i<numNode;i++){
            if(visit[i]==0){
                visit[i]=1;
                System.out.print(" "+vexTable[i].data);
                queue.enQueue(i);//入队

                EdgeNode eNode;
                while(!queue.isEmpty()){
                    queue.deQueue();//出队
                    eNode=vexTable[i].fnext;
                    while(eNode!=null){
                        if(visit[eNode.index]==0){
                            visit[eNode.index]=1;
                            System.out.print(" "+vexTable[eNode.index].data);
                            queue.enQueue(eNode.index);
                        }
                        eNode=eNode.next;
                    }
                }
            }
        }
    }




还是采用这个图:使用同样的输入数据在主方法中测试:

在这里插入图片描述

public static void main(String[] args) {
        Graph graph=new Graph(5,5);
        VexNode[] vexTable=graph.init();
       
        graph.DFS(vexTable);
        graph.BFS();
        
    }
深度优先遍历:
 a c e d b
广度优先遍历:
 a c b d e

标签:优先,遍历,java,int,public,输入,顶点,之图,语言版
From: https://www.cnblogs.com/cgl-dong/p/18128727

相关文章

  • 数据结构之Hash(java语言版)
    Hash表Hash也叫散列、哈希,是一种根据key-value对进行存储的数据结构。每个value对应一个key,这样查找的时候就无需遍历。Hash表使用数组作为底层结构,数组中每个区域都存储着Hash,这就是Hash表。列表、数组、树这些数据结构在查询数据时的时间复杂度通常为O(n),而Hash的时间复杂......
  • 数据结构之二叉搜索树(java语言版)
    之前介绍了树,主要实现了二叉树的代码。在二叉树的基础上有许多衍生的树,如二叉搜索树、哈夫曼树等,今天学习一下二叉搜索树。二叉搜索树二叉搜索树是二叉树的一种,是应用非常广泛的一种二叉树,英文简称为BST又被称为:二叉查找树、二叉排序树特点任意一个节点的值都大于其左子树所......
  • 数据结构之链表(c语言版)
    链表是线性表,链表的特点就是可以动态增减元素。种类有单向链表、双向链表,循环链表。一、单链表单链表的储存思想使用指针表示节点之间的逻辑关系,它的储存单元可以连续也可以不连续,每个储存单元需要储存信息和储存与后继节点的地址信息,储存单元又称之为节点。单链表由头指针唯......
  • 数据结构之栈(c语言版)
    栈(stack):在逻辑上是一种线性存储结构,它有以下几个特点:1、栈中数据是按照"后进先出(LIFO,LastInFirstOut)"方式进出栈的。2、向栈中添加/删除数据时,只能从栈顶进行操作。栈通常包括的三种操作:push、peek、pop。push--向栈中添加元素。peek--返回栈顶元素。pop--返......
  • 数据结构之队列(c语言版)
    队列(Queue):在逻辑上是一种线性存储结构。它有以下几个特点:1、队列中数据是按照"先进先出(FIFO,First-In-First-Out)"方式进出队列的。2、队列只允许在"队首"进行删除操作,而在"队尾"进行插入操作。队列通常包括的两种操作:入队列和出队列。队列的种类也很多,单向队列,双向队列,循......
  • 数据结构之二叉树(c语言版)
    之前的都是线性结构,而树结构在计算机应用中的应用更加广泛。linux中的目录结构,某些数据库的底层存储等,都是采用树结构进行构架的。树的概念线性表是一对一的关系,而树是一对多的关系。树的结点:包含一个数据元素及若干指向子树的分支;孩子结点:结点的子树的根称为该结点的孩子;双......
  • 数据结构之图(c语言版)
    图是比树更复杂的结构,树是一对多的关系,图是多对多的关系。一、基本概念1、定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。2、根据边是否有方向,将图可以划分......
  • 链表(java语言版)
    链表(Linkedlist)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。使用链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域......
  • 排序算法(c语言版)
    排序算法(c语言版)1、插入排序#include<stdio.h>//插入排序,升序voidinsertion_sort(intarr[],intlen){inti,j,key;for(i=1;i<len;i++){key=arr[i];//arr[i]为待插入的元素,保存在key中j=i-1;wh......
  • 深入浅出 妙用Javascript中apply、call、bind
    这篇文章实在是很难下笔,因为网上相关文章不胜枚举。巧合的是前些天看到阮老师的一篇文章的一句话:“对我来说,博客首先是一种知识管理工具,其次才是传播工具。我的技术文章,主要用来整理我还不懂的知识。我只写那些我还没有完全掌握的东西,那些我精通的东西,往往没有动力写。炫耀从来......