欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java數(shù)據(jù)結構之圖(動力節(jié)點Java學院整理)

 更新時間:2017年04月14日 10:42:41   投稿:mrr  
本文章主要講解學習如何使用JAVA語言以鄰接表的方式實現(xiàn)了數(shù)據(jù)結構---圖(Graph)。對java數(shù)據(jù)結構之圖相關知識感興趣的朋友一起學習吧

1,摘要:

本文章主要講解學習如何使用JAVA語言以鄰接表的方式實現(xiàn)了數(shù)據(jù)結構---圖(Graph)。從數(shù)據(jù)的表示方法來說,有二種表示圖的方式:一種是鄰接矩陣,其實是一個二維數(shù)組;一種是鄰接表,其實是一個頂點表,每個頂點又擁有一個邊列表。下圖是圖的鄰接表表示。 

從圖中可以看出,圖的實現(xiàn)需要能夠表示頂點表,能夠表示邊表。鄰接表指是的哪部分呢?每個頂點都有一個鄰接表,一個指定頂點的鄰接表中,起始頂點表示邊的起點,其他頂點表示邊的終點。這樣,就可以用鄰接表來實現(xiàn)邊的表示了。如頂點V0的鄰接表如下: 

與V0關聯(lián)的邊有三條,因為V0的鄰接表中有三個頂點(不考慮V0)。 

2,具體分析

先來分析邊表:

在圖中如何來表示一條邊?很簡單,就是:起始頂點指向結束頂點、就是頂點對<startVertex, endVertex>。在這里,為了考慮邊帶有權值的情況,單獨設計一個類Edge.java,作為Vertex.java的內部類,Edge.java如下:

 protected class Edge implements java.io.Serializable {
     private VertexInterface<T> vertex;// 終點
    private double weight;//權值

Edge類中只有兩個屬性,vertex 用來表示頂點,該頂點是邊的終點。weight 表示邊的權值。若不考慮帶權的情況,就不需要weight屬性,那么可以直接定義一個頂點列表 來存放 終點 就可以表示邊了。這是因為:這些屬性是定義在Vertex.java中,而Vertex本身就表示頂點,如果在Vertex內部定義一個List存放終點,那么該List再加上Vertex所表示的頂點本身,就可以表示與起點鄰接的各個點了(稱之為這個 起點的鄰接表)。這樣的邊的特點是:邊的所有的起始點都相同。

但是為了表示帶權的邊,因此,新增加weight屬性,并用類Edge來封裝,這樣不管是帶權的邊還是不帶權的邊都可以用同一個Edge類來表示。不帶權的邊將weight賦值為0即可。

再分析頂點表:

定義接口VertexInterface<T>表示頂點的接口,所有的頂點都需要實現(xiàn)這個接口,該接口中定義了頂點的基本操作,如:判斷頂點是否有鄰接點,將頂點與另一個頂點連接起來...。其次,頂點表中的每個頂點有兩個域,一個是標識域:V0,V1,V2,V3 。一個是指針域,指針域指向一個"單鏈表"。綜上,設計一個類Vertex.java 用來表示頂點,其數(shù)據(jù)域如下:

class Vertex<T> implements VertexInterface<T>, java.io.Serializable {
  private T label;//標識標點,可以用不同類型來標識頂點如String,Integer....
  private List<Edge> edgeList;//到該頂點鄰接點的邊,實際以java.util.LinkedList存儲
  private boolean visited;//標識頂點是否已訪問
  private VertexInterface<T> previousVertex;//該頂點的前驅頂點
  private double cost;//頂點的權值,與邊的權值要區(qū)別開來

現(xiàn)在一一解釋Vertex類中定義的各個屬性:

label : 用來標識頂點,如圖中的 V0,V1,V2,V3,在實際代碼中,V0...V3 以字符串的形式表示,就可以用來標識不同的頂點了。

因此,需要在Vertex類中添加獲得頂點標識的方法---getLabel()

   public T getLabel() {
     return label;
   }

edgeList : 存放與該頂點關聯(lián)的邊。從上面Edge.java中可以看到,Edge的實質是“頂點”,因為,Edge類除去wight屬性,就只剩表示頂點的vertex屬性了。借助edgeList,當給定一個頂點時,就可以訪問該頂點的所有鄰接點。因此,Vertex.java中就需要實現(xiàn)根據(jù)edgeList中存放的邊來遍歷 某條邊的終點(也即相應頂點的各個鄰接點) 的迭代器了。

 public Iterator<VertexInterface<T>> getNeighborInterator() {
     return new NeighborIterator();
   }

迭代器的實現(xiàn)如下:

/**Task: 遍歷該頂點鄰接點的迭代器--為 getNeighborInterator()方法 提供迭代器
   * 由于頂點的鄰接點以邊的形式存儲在java.util.List中,因此借助List的迭代器來實現(xiàn)
   * 由于頂點的鄰接點由Edge類封裝起來了--見Edge.java的定義的第一個屬性
   * 因此,首先獲得遍歷Edge對象的迭代器,再根據(jù)獲得的Edge對象解析出鄰接點對象
   */
  private class NeighborIterator implements Iterator<VertexInterface<T>>{
    Iterator<Edge> edgesIterator;
    private NeighborIterator() {
      edgesIterator = edgeList.iterator();//獲得遍歷edgesList 的迭代器
    }
    @Override
    public boolean hasNext() {
      return edgesIterator.hasNext();
    }
    @Override
    public VertexInterface<T> next() {
      VertexInterface<T> nextNeighbor = null;
      if(edgesIterator.hasNext()){
        Edge edgeToNextNeighbor = edgesIterator.next();//LinkedList中存儲的是Edge
        nextNeighbor = edgeToNextNeighbor.getEndVertex();//從Edge對象中取出頂點
      }
      else
        throw new NoSuchElementException();
      return nextNeighbor;
    }
    @Override
    public void remove() {
      throw new UnsupportedOperationException();
    }
  }

visited : 之所以給每個頂點設置一個用來標記它是否被訪問的屬性,是因為:實現(xiàn)一個數(shù)據(jù)結構,是要用它去完成某些功能的,如遍歷、查找…… 而在圖的遍歷過程中,就需要標記某個頂點是否被訪問了,因此:設置該屬性以便實現(xiàn)這些功能。那么,也就需要定義獲取頂點是否被訪問的isVisited()方法了。

  public boolean isVisited() {
    return visited;
   }

previousVertex 屬性 ,在求圖中某兩個頂點之間的最短路徑時,在從起始頂點遍歷過程中,需要記錄下遍歷到某個頂點時的前驅頂點, previousVertex 屬性就派上用場了。因此,需要有判斷和獲取頂點的前驅頂點的方法:

   public boolean hasPredecessor() {//判斷頂點是否有前驅頂點
     return this.previousVertex != null;
   }
  public VertexInterface<T> getPredecessor() {//獲得前驅頂點
     return this.previousVertex;
  }

cost 屬性:用來表示頂點的權值。注意,頂點的權值與邊的權值是不同的。比如求無權圖(默認是邊不帶權值)的最短路徑時,如何求出頂點A到頂點B的最短的路徑?由定義,該最短路徑其實就是A走到B經(jīng)歷的最少邊數(shù)目。因此,就可以用 cost 屬性來記錄A到B之間的距離是多少了。比如說,A 先走到 C 再走到B;初始時,A的 cost = 0,由于 A 是 C 的前驅,A到B需要經(jīng)歷C,C 的 cost 就是 c.previousVertex.cost + 1,直至 B,就可以求出 A 到 B 的最短路徑了。詳細算法及實現(xiàn)將會在第二篇博客中給出。

因此,針對 cost 屬性,Vertex.java需要實現(xiàn)的方法如下:

 public void setCost(double newCost) {
     cost = newCost;
   }
 public double getCost() {
    return cost;
  } 

3,總結:

從上可以看出,設計一個數(shù)據(jù)結構時,該數(shù)據(jù)結構需要包含哪些屬性不是隨意的,而是先確定該數(shù)據(jù)結構需要完成哪些功能(如,圖的DFS、BFS、拓撲排序、最短路徑),這些功能的實現(xiàn)需要借助哪些屬性(如,求最短路徑需要記錄每個頂點的前驅頂點,就需要 previousVertex)。然后,去定義這些屬性以及關于該屬性的基本操作。設計一個合適的數(shù)據(jù)結構,當借助該數(shù)據(jù)結構來實現(xiàn)算法時,可以有效地降低算法的實現(xiàn)難度和復雜度!

Vertex.java的完整代碼如下:

package graph;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
class Vertex<T> implements VertexInterface<T>, java.io.Serializable {
  private T label;//標識標點,可以用不同類型來標識頂點如String,Integer....
  private List<Edge> edgeList;//到該頂點鄰接點的邊,實際以java.util.LinkedList存儲
  private boolean visited;//標識頂點是否已訪問
  private VertexInterface<T> previousVertex;//該頂點的前驅頂點
  private double cost;//頂點的權值,與邊的權值要區(qū)別開來
  public Vertex(T vertexLabel){
    label = vertexLabel;
    edgeList = new LinkedList<Edge>();//是Vertex的屬性,說明每個頂點都有一個edgeList用來存儲所有與該頂點關系的邊
    visited = false;
    previousVertex = null;
    cost = 0;
  }
  /**
   *Task: 這里用了一個單獨的類來表示邊,主要是考慮到帶權值的邊
   *可以看出,Edge類封裝了一個頂點和一個double類型變量 
   *若不需要考慮權值,可以不需要單獨創(chuàng)建一個Edge類來表示邊,只需要一個保存頂點的列表即可
   * @author hapjin
   */
  protected class Edge implements java.io.Serializable {
    private VertexInterface<T> vertex;// 終點
    private double weight;//權值
    //Vertex 類本身就代表頂點對象,因此在這里只需提供 endVertex,就可以表示一條邊了
    protected Edge(VertexInterface<T> endVertex, double edgeWeight){
      vertex = endVertex;
      weight = edgeWeight;
    }
    protected VertexInterface<T> getEndVertex(){
      return vertex;
    }
    protected double getWeight(){
      return weight;
    }
  }
  /**Task: 遍歷該頂點鄰接點的迭代器--為 getNeighborInterator()方法 提供迭代器
   * 由于頂點的鄰接點以邊的形式存儲在java.util.List中,因此借助List的迭代器來實現(xiàn)
   * 由于頂點的鄰接點由Edge類封裝起來了--見Edge.java的定義的第一個屬性
   * 因此,首先獲得遍歷Edge對象的迭代器,再根據(jù)獲得的Edge對象解析出鄰接點對象
   */
  private class NeighborIterator implements Iterator<VertexInterface<T>>{
    Iterator<Edge> edgesIterator;
    private NeighborIterator() {
      edgesIterator = edgeList.iterator();//獲得遍歷edgesList 的迭代器
    }
    @Override
    public boolean hasNext() {
      return edgesIterator.hasNext();
    }
    @Override
    public VertexInterface<T> next() {
      VertexInterface<T> nextNeighbor = null;
      if(edgesIterator.hasNext()){
        Edge edgeToNextNeighbor = edgesIterator.next();//LinkedList中存儲的是Edge
        nextNeighbor = edgeToNextNeighbor.getEndVertex();//從Edge對象中取出頂點
      }
      else
        throw new NoSuchElementException();
      return nextNeighbor;
    }
    @Override
    public void remove() {
      throw new UnsupportedOperationException();
    }
  }
  /**Task: 生成一個遍歷該頂點所有鄰接邊的權值的迭代器
   * 權值是Edge類的屬性,因此先獲得一個遍歷Edge對象的迭代器,取得Edge對象,再獲得權值
   * @author hapjin
   *
   * @param <Double> 權值的類型
   */
  private class WeightIterator implements Iterator{//這里不知道為什么,用泛型報編譯錯誤???
    private Iterator<Edge> edgesIterator;
    private WeightIterator(){
      edgesIterator = edgeList.iterator();
    }
    @Override
    public boolean hasNext() {
      return edgesIterator.hasNext();
    }
    @Override
    public Object next() {
      Double result;
      if(edgesIterator.hasNext()){
        Edge edge = edgesIterator.next();
        result = edge.getWeight();
      }
      else throw new NoSuchElementException();
      return (Object)result;//從迭代器中取得結果時,需要強制轉換成Double
    }
    @Override
    public void remove() {
      throw new UnsupportedOperationException();
    }
  }
  @Override
  public T getLabel() {
    return label;
  }
  @Override
  public void visit() {
    this.visited = true;
  }
  @Override
  public void unVisit() {
    this.visited = false;
  }
  @Override
  public boolean isVisited() {
    return visited;
  }
  @Override
  public boolean connect(VertexInterface<T> endVertex, double edgeWeight) {
    // 將"邊"(邊的實質是頂點)插入頂點的鄰接表
    boolean result = false;
    if(!this.equals(endVertex)){//頂點互不相同
      Iterator<VertexInterface<T>> neighbors = this.getNeighborInterator();
      boolean duplicateEdge = false;
      while(!duplicateEdge && neighbors.hasNext()){//保證不添加重復的邊
        VertexInterface<T> nextNeighbor = neighbors.next();
        if(endVertex.equals(nextNeighbor)){
          duplicateEdge = true;
          break;
        }
      }//end while
      if(!duplicateEdge){
        edgeList.add(new Edge(endVertex, edgeWeight));//添加一條新邊
        result = true;
      }//end if
    }//end if
    return result;
  }
  @Override
  public boolean connect(VertexInterface<T> endVertex) {
    return connect(endVertex, 0);
  }
  @Override
  public Iterator<VertexInterface<T>> getNeighborInterator() {
    return new NeighborIterator();
  }
  @Override
  public Iterator getWeightIterator() {
    return new WeightIterator();
  }
  @Override
  public boolean hasNeighbor() {
    return !(edgeList.isEmpty());//鄰接點實質是存儲是List中
  }
  @Override
  public VertexInterface<T> getUnvisitedNeighbor() {
    VertexInterface<T> result = null;
    Iterator<VertexInterface<T>> neighbors = getNeighborInterator();
    while(neighbors.hasNext() && result == null){//獲得該頂點的第一個未被訪問的鄰接點
      VertexInterface<T> nextNeighbor = neighbors.next();
      if(!nextNeighbor.isVisited())
        result = nextNeighbor;
    }
    return result;
  }
  @Override
  public void setPredecessor(VertexInterface<T> predecessor) {
    this.previousVertex = predecessor;
  }
  @Override
  public VertexInterface<T> getPredecessor() {
    return this.previousVertex;
  }
  @Override
  public boolean hasPredecessor() {
    return this.previousVertex != null;
  }
  @Override
  public void setCost(double newCost) {
    cost = newCost;
  }
  @Override
  public double getCost() {
    return cost;
  }
  //判斷兩個頂點是否相同
  public boolean equals(Object other){
    boolean result;
    if((other == null) || (getClass() != other.getClass()))
      result = false;
    else
    {
      Vertex<T> otherVertex = (Vertex<T>)other;
      result = label.equals(otherVertex.label);//節(jié)點是否相同最終還是由標識 節(jié)點類型的類的equals() 決定
    }
    return result;
  }
}

以上所述是小編給大家介紹的Java數(shù)據(jù)結構之圖(動力節(jié)點Java學院整理),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關文章

  • Java8?LocalDateTime時間日期類使用實例詳解

    Java8?LocalDateTime時間日期類使用實例詳解

    本文從 LocalDateTime 類的創(chuàng)建、轉換、格式化與解析、計算與比較以及其他操作幾個方面詳細介紹了 LocalDateTime 類在 Java 8 中的使用,感興趣的朋友跟隨小編一起看看吧
    2024-03-03
  • Java Swing仿QQ登錄界面效果

    Java Swing仿QQ登錄界面效果

    這篇文章主要為大家詳細介紹了Java Swing仿QQ登錄界面效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-05-05
  • Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換

    Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換

    在Java編程中,有時需要將字節(jié)類型(byte)轉換為整數(shù)類型(int),或者反過來轉換,本文主要介紹了Java 字節(jié)數(shù)組(byte[])和整型(int)的相互轉換,感興趣的可以了解一下
    2023-12-12
  • java isInterrupted()判斷線程的實例講解

    java isInterrupted()判斷線程的實例講解

    在本篇內容里小編給大家分享的是一篇關于java isInterrupted()判斷線程的實例講解內容,有興趣的朋友們可以學習下。
    2021-05-05
  • 測試springboot項目出現(xiàn)Test Ignored的解決

    測試springboot項目出現(xiàn)Test Ignored的解決

    這篇文章主要介紹了測試springboot項目出現(xiàn)Test Ignored的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Java this、final等關鍵字總結

    Java this、final等關鍵字總結

    這篇文章主要對java中this、final等關鍵字進行了總結,需要的朋友可以參考下
    2017-04-04
  • 淺談選擇、冒泡排序,二分查找法以及一些for循環(huán)的靈活運用

    淺談選擇、冒泡排序,二分查找法以及一些for循環(huán)的靈活運用

    下面小編就為大家?guī)硪黄獪\談選擇、冒泡排序,二分查找法以及一些for循環(huán)的靈活運用。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-06-06
  • RocketMQ集群消費與廣播消費模式

    RocketMQ集群消費與廣播消費模式

    這篇文章主要介紹了RocketMQ集群消費與廣播消費模式,消息隊列RocketMQ版支持集群消費和廣播消費,本文介紹集群消費和廣播消費的基本概念、適用場景、功能差異、注意事項以及設置方式
    2023-02-02
  • Java實現(xiàn)的計算最大下標距離算法示例

    Java實現(xiàn)的計算最大下標距離算法示例

    這篇文章主要介紹了Java實現(xiàn)的計算最大下標距離算法,涉及java針對數(shù)組的遍歷、運算等相關操作技巧,需要的朋友可以參考下
    2018-02-02
  • Spring配置多數(shù)據(jù)源切換

    Spring配置多數(shù)據(jù)源切換

    今天小編就為大家分享一篇關于Spring配置多數(shù)據(jù)源切換,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-01-01

最新評論