数据结构-图

无向图

无向图的实现

使用邻接表实现:

public class Graph {
    //记录顶点数量
    private int V;
    //记录边数量
    private int E;
    //领接表
    private Queue<Integer>[] adj;

    public Graph(int V){
         this.V=V;
         this.E=0;
         this.adj=new Queue[V];
         for(int i=0;i<adj.length;i++){
             adj[i]=new LinkedList<Integer>();
         }
    }

    //获取图中顶点的数量
    public int V(){
       return V;
    }

    //获取图中边的数量
    public int E(){
        return E;
    }
    //向图中添加一条边v-w
    public void addEdge(int v,int w){
       //把w添加到v的链表中,这样顶点v就多了一个相邻点w
        adj[v].offer(w);
        //把v添加到w的链表中,这样顶点w就多了一个相邻点v
        adj[w].offer(v);
        //边的数目+1
        E++;
    }

    //获取和顶点v相邻的所有顶点
    public Queue<Integer> adj(int v){
         return adj[v];
    }
}

无向图的搜索

深度优先搜索

1.原理

深度优先搜索,指的是在搜索时,如果遇到一个结点既有子结点,又有兄弟结点,那么先找子结点,然后找兄弟结点。 

在由于边是没有方向的,所以,如果4和5顶点相连,那么4会出现在5的相邻链表中,5也会出现在4的相邻链表中,那么为了不对顶点进行重复搜索,应该要有相应的标记来表示当前顶点有没有搜索过,可以使用一个布尔类型的数组 boolean[V] marked,索引代表顶点,值代表当前顶点是否已经搜索,如果已经搜索,标记为true,如果没有搜索,标记为false;

2.代码实现

public class DepthFirstSearch {
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //记录有多少个顶点与s顶点相通
    private int count;
    //构造深度优先搜索对象,使用深度优先搜索找出G图中s顶点的所有相通顶点
    public DepthFirstSearch(Graph G,int s){
        //创建一个与图的顶点数一样大小的布尔数组
        marked=new boolean[G.V()];
        //搜索G图中与s顶点相通的所有顶点
        count=0;
        dfs(G,s);
    }
    //使用深度优先搜索找出G图中v顶点的所有相通顶点
    private void dfs(Graph G,int v){
         //把当前顶点标记为已搜索
        marked[v]=true;
        //遍历v顶点的领接表,得到每一个顶点w
        for(Integer w: G.adj(v)){
            //如果当前顶点w没有被搜索过,则递归搜索与w顶点相通的其他顶点
              if(!marked(w)){
                  dfs(G,w);
              }
        }
        //相遇的顶点数量+1
        count++;
    }
    //判断w顶点与s顶点是否相通
    public boolean marked(int w){
        return marked[w];
    }
    //获取与顶点s相通的所有顶点的总数
    public int count(){
        return count;
    }
}
//测试代码
public class DepthFirstSearchTest {
    public static void main(String[] args){
        //创建Graph对象
        Graph G=new Graph(13);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,5);
        G.addEdge(0,6);
        G.addEdge(3,5);
        G.addEdge(3,4);
        G.addEdge(4,5);
        G.addEdge(4,6);
        G.addEdge(7,8);
        G.addEdge(9,10);
        G.addEdge(9,11);
        G.addEdge(9,12);
        G.addEdge(11,12);
        DepthFirstSearch search=new DepthFirstSearch(G,0);
        int count = search.count();
        System.out.println("与0相通的顶点的数量:"+count);
        boolean marked = search.marked(7);
        System.out.println("顶点7与是否与0相通:"+marked);

    }
}

广度优先搜索

1.原理 

2.代码实现

ublic class BreadthFirstSearch {
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //记录有多少个顶点与s顶点相遇
    private int count;
    //用来存储待搜索领接表的点
    private Queue<Integer> waitSearch;
    //构造广度优先搜索对象,使用广度优先搜索找出G图中s顶点的所有相邻顶点
    public BreadthFirstSearch(Graph G,int s){
        marked=new boolean[G.V()];
        count=0;
        waitSearch=new LinkedList<>();
        bfs(G,s);
    }

    //使用广度优先搜索找出G图中v顶点的所有相邻顶点
    private void bfs(Graph G,int v){
        //把当前顶点v标记为已搜索
          marked[v]=true;
          //把当前顶点v放入到队列中,等待搜索它的领接表
          waitSearch.offer(v);
          //使用while循环从队列中拿出待搜索的顶点wait,进行搜索邻接表
          while(!waitSearch.isEmpty()){
              Integer wait = waitSearch.poll();
              //遍历wait顶点的邻接表,得到每一个顶点w
              Queue<Integer> adj = G.adj(wait);
              while(adj.size()>0){
                  Integer w=adj.poll();
                  if(!marked[w]){
                      marked[w]=true;
                      waitSearch.offer(w);
                  }
              }
              count++;
          }

    }
    //判断w顶点与s顶点是否相通
    public boolean marked(int w){
       return marked[w];
    }
    //获取与顶点s相通的所有顶点的总数
    public int count(){
        return count;
    }
}
//测试代码
public class BreadthFirstSearchTest {
    public static void main(String[] args){
        Graph G=new Graph(13);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,5);
        G.addEdge(0,6);
        G.addEdge(3,5);
        G.addEdge(3,4);
        G.addEdge(4,5);
        G.addEdge(4,6);
        G.addEdge(7,8);
        G.addEdge(9,10);
        G.addEdge(9,11);
        G.addEdge(9,12);
        G.addEdge(11,12);
        BreadthFirstSearch search=new BreadthFirstSearch(G,9);
        int count = search.count();
        System.out.println("与9相通的顶点的数量:"+count);
        boolean marked = search.marked(7);
        System.out.println("顶点7与是否与0相通:"+marked);
    }
}

路径查找

1.实现介绍

我们实现路径查找,最基本的操作还是得遍历并搜索图,所以,我们的实现暂且基于深度优先搜索来完成。其搜索的过程是比较简单的。我们添加了edgeTo[]整型数组,这个整型数组会记录从每个顶点回到起点s的路径。如果我们把顶点设定为0,那么它的搜索可以表示为下图:

2.代码实现

public class DepthFirstPaths {
    //索引代表顶点,值表示当前顶点是否已经被搜索
    private boolean[] marked;
    //起点
    private int s;
    //索引代表顶点,值代表从起点s到当前顶点路径上的最后一个顶点
    private int[] edgeTo;
    //构造深度优先搜索对象,使用深度优先搜索找出G图中起点为s的所有路径
    public DepthFirstPaths(Graph G,int s){
        marked=new boolean[G.V()];
        this.s=s;
        edgeTo=new int[G.V()];
        dfs(G,s);
    }
    //使用深度优先先搜索找出G图中v顶点的所有相邻顶点
    private void dfs(Graph G,int v){
        marked[v]=true;
        for(Integer w:G.adj(v)){
            if(!marked[w]){
                edgeTo[w]=v;
                dfs(G,w);
            }
        }
    }
    //判断v顶点与s顶点是否存在路径
    public boolean hasPathTo(int v){
        return marked[v];
    }
    //找出从起点s到顶点v的路径(就是该路径经过的顶点)
    public Stack pathTo(int v){
        //当前v顶点与s顶点不连通,所以直接返回null,没有路径
       if(!hasPathTo(v)){
           return null;
       }
       //创建路径中经过的顶点的容器
       Stack<Integer> path=new Stack<>();
       //第一次把当前顶点存进去,然后将x变换为到达当前顶点的前一个顶点edgeTo[x],在把前一个顶点存进去,
        // 继续将x变化为到达前一个顶点的前一个顶点,继续存,一直到x的值为s为止,相当于逆推法,最后把s放进去
       for(int x=v;x!=s;x=edgeTo[x]){
           path.push(x);
       }
       //把起点s放入容器中
       path.push(s);
       return path;
    }
}
//测试代码
public class DepthFirstPathsTest {
    public static void main(String[] args){
        Graph G=new Graph(6);
        G.addEdge(0,1);
        G.addEdge(0,2);
        G.addEdge(0,5);
        G.addEdge(1,2);
        G.addEdge(2,3);
        G.addEdge(2,4);
        G.addEdge(3,4);
        G.addEdge(3,5);
        DepthFirstPaths paths=new DepthFirstPaths(G,0);
        Stack<Integer> stack = paths.pathTo(2);
        while(!stack.isEmpty()){
            Integer pop = stack.pop();
            System.out.print(pop+"-");
        }

    }
}

有向图

有向图的实现

public class Digraph {
    //记录顶点数量
    private int V;
    //记录边数量
    private int E;
    //邻接表
    private Queue<Integer>[] adj;
    //创建一个包含V个顶点但不包含边的有向图
    public Digraph(int V){
        this.V=V;
        this.E=0;
        adj=new Queue[V];
        for(int i=0;i<adj.length;i++){
            adj[i]=new LinkedList<Integer>();
        }
    }
    //获取图中顶点的数量
    public int V(){
        return V;
    }
    //获取图中边的数量
    public int E(){
        return E;
    }
    //向有向图中添加一条边v-w
    public void addEdge(int v,int w){
        adj[v].offer(w);
        E++;
    }
    //获取由v指出的边所连接的所有顶点
    public Queue<Integer> adj(int v){
        return adj[v];
    }
    //该图的反向图
    public Digraph reverse(){
        //创建新的有向图对象
        Digraph r=new Digraph(V);
        //遍历0-V-1的所有顶点,拿到每一个顶点v
        for(int v=0;v<V;v++){
            //得到原图中的v顶点对应的领接表,原图中的v-w,则反向图为w-v
            for(Integer w:adj(v)){
                r.addEdge(w, v);
            }
        }
        return r;
    }
}

加权无向图

加权无向图中边的表示

加权无向图中的边我们就不能简单的使用v-w两个顶点表示了,而必须要给边关联一个权重值,因此我们可以使用对象来描述一条边

public class Edge implements Comparable<Edge>{
    //顶点1
    private int v;
    //顶点2
    private int w;
    //当前边的权重值
    private double weight;
    //通过顶点v和w,以及权重weight值构造一个边对象
    public Edge(int v,int w,double weight){
        this.v=v;
        this.w=w;
        this.weight=weight;
    }
    //获取边上的权重值
    public double weight(){
        return weight;
    }
    //获取边上的一个点
    public int either(){
        return v;
    }
    //获取边上除了顶点vertex外的另外一个顶点
    public int other(int vertex){
         if(vertex==v){
             return w;
         }else{
             return v;
         }
    }
    //比较当前边和参数that边的权重,如果当前边权重大,则返回1
    //如果一样大,返回0 如果当前边权重小,返回-1
    public int compareTo(Edge that){
        int cmp;
        if(this.weight()>that.weight()){
            cmp=1;
        }else if(this.weight()<that.weight()){
            cmp=-1;
        }else{
            cmp=0;
        }
        return cmp;
    }
}

加权无向图的实现

public class EdgeWeightedGraph {
    //记录顶点数量
    private int V;
    //记录边数量
    private int E;
    //邻接表
    private Queue<Edge>[] adj;

    //创建一个含有V个顶点的空加权无向图
    public EdgeWeightedGraph(int V){
          this.V=V;
          this.E=0;
          this.adj=new Queue[V];
          for(int i=0;i<adj.length;i++){
              adj[i]=new LinkedList<Edge>();
          }
    }
    //获取图中顶点的数量
    public int V(){
      return V;
    }
    //获取图中边的数量
    public int E(){
        return E;
    }
    //向加权无向图中添加一条边e
    public void addEdge(Edge e){
        //获取边上的一个顶点
        int v=e.either();
        //获取边上的另外一个顶点
        int w=e.other(v);
        //因为是无向图,所以边e需要同时出现在两个顶点的邻接表中
        adj[v].offer(e);
        adj[w].offer(e);
        //边的数量+1
        E++;

    }
    //获取和顶点v关联的所有边
    public Queue<Edge> adj(int v){
         return adj[v];
    }
    //获取加权无向图的所有边
    public Queue<Edge> edges(){
        //创建一个队列,存储所有的边
        Queue<Edge> allEdge=new LinkedList<>();
        //遍历顶点,拿到每个顶点的邻接表
        for(int v=0;v<V;v++){
            //遍历邻接表,拿到邻接表中每条边
            for(Edge e:adj(v)){
                /*
无向图中,每条边对象Edge都会在两个顶点的邻接表中各出现一次,为了不重复获取,暂定:
除了当前顶点v,再获取边e中的另外一个顶点w,如果v<w则添加,这样可以保证同一条边
只会被统计一次
*/
                if(e.other(v)<v){
                    allEdge.offer(e);
                }
            }
        }
        return allEdge;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值