Java无向图链表、邻接表实现以及深度优先遍历广度优先遍历

概述

图的存储形式中链表是通过数组加LinkedList(不一定是LinkedList,可以自己写链,也可以选择其他的集合数据结构),邻接表采用的是二维数组的结构。

链表存储形式的相关实现

数据存储结构与基础操作

初始化数据存储结构

private static LinkedList[] arrayLists;
public static void createDatastruct(int count){
        /**
         *@description:根据图中点的数量初始化一个链表形式的数据结构
         */
        arrayLists = new LinkedList[count];
        for (int i = 0; i < count; i++) {
            arrayLists[i] = new LinkedList<Integer>();
        }
    }

添加边信息:

private static Comparator  arrayListsSort = new Comparator<Integer>(){

        @Override
        public int compare(Integer o1, Integer o2) {
            if(o1.intValue()>o2.intValue()){
                return 1;
            }else if(o1.intValue()<o2.intValue()){
                return -1;
            }else{
                return 0;
            }
        }
    }; 


public static void insertEdge(int v1,int v2){
        /**
         *@description:添加边并且保持每个节点关联节点的顺序性
         */
        arrayLists[v1].add(v2);
        arrayLists[v1].sort(arrayListsSort);
        arrayLists[v2].add(v1);
        arrayLists[v2].sort(arrayListsSort);
    }

展示数据结构:

public static void showDatas(LinkedList<Integer>[] linkedLists){
        /**
         *@description: 输出图链表形式的存储,以及将链表型的数据转换为邻接表的形式并输出
         *@params: 图链表形式的存储
         */
        System.out.println("图的链接表输出形式:");
        for (int i = 0; i < arrayLists.length; i++) {
            System.out.printf(i+" ");
            for (Object temp : arrayLists[i]){
                System.out.printf("==>"+temp.toString());
            }
            System.out.println();
        }

        //链表型的数据转换为邻接表
        int[][] linkedListsToArrays = new int[linkedLists.length][linkedLists.length];
        for (int i = 0; i < arrayLists.length; i++) {
            for (Object temp : arrayLists[i]){
                linkedListsToArrays[i][(Integer)temp] = 1;
            }
        }

        System.out.println("图的邻接表输出形式:");
        for (int i = 0; i < linkedListsToArrays.length; i++) {
            System.out.println(Arrays.toString(linkedListsToArrays[i]));
        }
    }

链表形式无向图实现深度优先遍历:

相信这里一定是大家懵逼的地方,这里也是我懵逼的地方但是好赖自己磨了出来,大概的实现思路是这样的:

 这个数据结构,设置了自动排序,每个节点添加一个新的连接那么就会在linkedLists[i]的这个集合中做一个优先级的排序,我们没访问一个节点都会将这个节点做一个输出,并且将这个节点与其他节点相关联的关系进行一个删除,这也是delectCheckedNodeEvery()函数的作用。每一次的回溯就是这条路无路可走的时候,这时候我们还是要删除掉这个已经访问过了的节点的相关节点,并且通过return这个关键字进行一个回溯。

public static void delectCheckedNodeEvery(LinkedList<Integer>[] linkedLists,int startNode){
        for(LinkedList linkedList:linkedLists){
            for(Iterator<Integer> linkedListIterator = linkedList.iterator(); linkedListIterator.hasNext();){
                if(linkedListIterator.next().equals(startNode)){
                    linkedListIterator.remove();
                }
            }
        }
    }

    public static void dfs(LinkedList<Integer>[] linkedLists,int startNode){
        /**
         *@description: 对链表形式数据的深度优先排序;当进入当前的这个方法时候说明上一个startNode已经执行完了
         */
        System.out.printf(startNode+"->");
        while (linkedLists[startNode].size()>0){
            delectCheckedNodeEvery(linkedLists,startNode);
            dfs(linkedLists,linkedLists[startNode].removeFirst());
        }
        delectCheckedNodeEvery(linkedLists,startNode);
        return;
    }

链表形式无向图实现广度优先遍历:

不管是链表形式的还是连接图形式的数据结构,广度优先的遍历都是比较简单的,简单的说 就是我们是一层一层的数据遍历,bfs(LinkedList<Integer>[] linkedLists,int startNode)中的startNode相当于是根节点,对他的的处理我们调用了一次checkedNodeDetail,如果抛开根节点,那接下来我们进入到一层层的数据,这一层层的数据我们是通过while来实现的。对于第一层数据的遍历那实际就是linkedLists[0]中的所有节点,当第一层结束后那就开始第二层,第二层又应该是怎么开始呢,这时候

public static void checkedNodeDetail(LinkedList<Integer>[] linkedLists,LinkedList<Integer> checkedNode,int startNode){
        /**
         *@description: 将startNode存入到checkNode,删除数组中所有链表中的这个点,并将这个数值输出
         *@params: 待处理的链表数组
         * @params: 执行的辅助优先队列,存储的是已经访问过了的节点
         * @params: 已经访问过了的节点
         */
        checkedNode.addLast(startNode);
        delectCheckedNodeEvery(linkedLists,startNode);
        System.out.println(startNode+"==>");
    }


    public static void bfs(LinkedList<Integer>[] linkedLists,int startNode){
        /**
         *@description: 对链表形式的数据进行广度优先排序,
         */
        //数据初始化
        LinkedList<Integer> checkedNode = new LinkedList<>();
        //对初始节点的一个处理
        checkedNodeDetail(linkedLists,checkedNode,startNode);
        while(!checkedNode.isEmpty()){
            Integer indexNode = checkedNode.removeFirst();
            while (!linkedLists[indexNode].isEmpty()){
                checkedNodeDetail(linkedLists,checkedNode,linkedLists[indexNode].removeFirst());
            }
        }
    }

类中用来测试的主方法:

public static void main(String[] args) {
        //定义图中点的个数
        final  int elementsCount =6;
        createDatastruct(elementsCount);
        //初始化图
        insertEdge(0,1);
        insertEdge(0,2);
        insertEdge(0,5);
        insertEdge(1,3);
        insertEdge(1,4);
        insertEdge(2,3);
        insertEdge(4,5);
        //数据展示
//        int temp =delectDoubleNode(arrayLists,0);
        showDatas(arrayLists);
//        System.out.println("现在正在进行的是dfs遍历:");
//        dfs(arrayLists,0);
        System.out.println("现在进行的是bfs遍历:");
        bfs(arrayLists,0);

    }

邻接表存储形式的相关实现

上面的链表的实现完全是自己实现的,下边的这个是网上老师的一个实现,他对现有的数据结构没有修改,采用的是另外用一个标记数组来标记一个节点是否被访问了,在上面的实现中我才用的是置零的方式。这种方式会修改元数据结构,可以采用的就是把原来的数组拷贝一份,我们用拷贝的操作。

如果我们采用直接复制的形式的话,那拷贝依旧是无效的,因为他们引用的依旧是同一个地方的变量,这时候需要进行克隆,当然也可以采用一个一个复制进去的方式。有关克隆的文章是:

https://blog.csdn.net/lmlzww/article/details/105718674

package map;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;

public class Graph {

    private static ArrayList<String> varTextList;
    private static int[][] edges;
    private static int numOfEdges;
    //定义给数组boolean[],记录某个节点是否被访问
    private static boolean isVisited[];

    public static void main(String[] args) {

        int n =5;
        String VerTexValue [] ={"A","B","C","D","E"};
        //创建图对象
        Graph graph = new Graph(n);
        //循环添加顶点
        for (String temp:VerTexValue){
            graph.insertVertex(temp);
        }
        //添加边
        graph.insertEdge(0,1,1);
        graph.insertEdge(0,2,1);
        graph.insertEdge(1,2,1);
        graph.insertEdge(1,3,1);
        graph.insertEdge(0,4,1);

        graph.showGraph();

        System.out.println("深度遍历测试");
        dfs();
        System.out.println("广度优先遍历");
        bfs();

    }

    //初始化构造器
    public Graph(int n){
        //初始化我们的矩阵和varTextList
        edges = new int[n][n];
        //用来表示、存储节点
        varTextList = new ArrayList<>(n);
        numOfEdges =0;
        isVisited = new boolean[5];
    }

    //返回节点的个数
    public static int getNumOfVertex(){
        return varTextList.size();
    }

    //返回边的数目
    public static int getNumOfEdges(){
        return numOfEdges;
    }

    //放回节点i对应的数据
    public static String getValueByIndex(int i){
        return varTextList.get(i);
    }

    //获得对应点的权重
    public static int getWeight(int v1,int v2){
        return edges[v1][v2];
    }

    //显示图对应的矩阵
    public static void showGraph(){
        for (int[] link:edges){
            System.out.println(Arrays.toString(link));
        }
    }



    //插入节点
    public static void insertVertex(String vertex){
        varTextList.add(vertex);
    }

    public static void insertEdge(int v1,int v2,int weight){
        edges[v1][v2]=weight;
        edges[v2][v1]=weight;
        numOfEdges++;
    }

    //得到第一个链接节点的下标
    public static int getFirstNeighbor(int index){
        for (int i = 0; i < varTextList.size(); i++) {
            if(edges[index][i]>0){
                return i;
            }
        }
        return -1;
    }

    //根据前一个林接节点的下标来获取下一个临界点
    public static int getNextNeighbor(int v1,int v2){
        for (int j = v2+1; j <varTextList.size() ; j++) {
            if(edges[v1][j]>0){
                return j;
            }
        }
        return -1;
    }

    //Depth First Search
    private static void dfs(boolean[] isVisited,int i){
        //首先我们访问该节点并输出
        System.out.print(getValueByIndex(i)+"->");
        //设置该节点已经访问过
        isVisited[i] = true;
        //查找节点i的第一个临界点
        int w = getFirstNeighbor(i);
        while (w!=-1){
            if(!isVisited[w]){
                //没有被访问过
                dfs(isVisited,w);
            }
            //如果w的这个节点已经被访问过那么就去找下一个邻接点
            w = getNextNeighbor(i,w);
        }
    }

    //对dfs进行一个重载,遍历我们所有的节点并进行dfs
    public static void dfs(){
        //遍历所有的节点,进行dfs
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]){
                dfs(isVisited,i);
            }
        }
    }

    //对一个节点进行广度优先遍历的算法
    private static void bfs(boolean[] isVisited,int i){
        int u;//表示队列的头结点对应的下标
        int w;//邻接点w
        LinkedList queue = new LinkedList();
        //访问这个节点
        System.out.println(getValueByIndex(i)+"=>");
        //标记为已经访问过
        isVisited[i] = true;
        //将节点加入队列
        queue.addLast(i);

        while( !queue.isEmpty()){
            //取出队列头结点的下标
            u = (int) queue.removeFirst();
            //得到第一个邻接点的下标
            w = getFirstNeighbor(u);
            while (w!=-1){
                //是否访问过
                if(!isVisited[w]){
                    System.out.print(getValueByIndex(w)+"=>");
                    //标记已经访问过
                    isVisited[w] = true;
                    //入队
                    queue.addLast(w);
                }
                //以u为前驱节点,找w后面的下一个邻接点
                w = getNextNeighbor(u,w);
            }
        }

    }


    //遍历所有的节点,都进行广度优先搜索
    public static void bfs(){
        isVisited = new boolean[varTextList.size()];
        for (int i = 0; i < getNumOfVertex(); i++) {
            if(!isVisited[i]){
                bfs(isVisited,i);
            }
        }
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值