waysun一路陽光

          不輕易服輸,不輕言放棄.--心是夢的舞臺,心有多大,舞臺有多大。踏踏實實做事,認認真真做人。

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 ::  :: 管理 ::
            167 隨筆 :: 1 文章 :: 64 評論 :: 0 Trackbacks
          轉自:http://www.aygfsteel.com/javacap/archive/2007/12/14/167764.html
          很早就想總結一下了,一直沒有時間,OK,進入正題。

          一 圖的基本概念及存儲結構
          圖G是由頂點的有窮集合,以及頂點之間的關系組成,頂點的集合記為V,頂點之間的關系構成邊的集合E
          G=(V,E).
          說一條邊從v1,連接到v2,那么有v1Ev2(E是V上的一個關系)《=》<v1,v2>∈E.
          圖有有向圖,無向圖之分,無向圖的一條邊相當于有向圖的中兩條邊,即如果無向圖的頂點v1和v2之間有一條邊
          ,那么既有從v1連接到v2的邊,也有從v2連接到v1的邊,<v1,v2>∈E 并且<v2,v1>∈E,而有向圖是嚴格區分方向的。

          一般圖的存儲有兩種方式
          1)相鄰矩陣,用一個矩陣來保持邊的情況,<v1,v2>∈E則Matrix[v1][v2]=Weight.
          2)鄰接表,需要保存一個順序存儲的頂點表和每個頂點上的邊的鏈接表。
          這里的實現采用第二種方案,另外圖又復雜圖,簡單圖之分,復雜圖可能在兩點之間同一個方向有多條邊,我們考慮的都是無環簡單圖,無環簡單圖是指頂點沒有自己指向自己的邊的簡單圖,即任取vi屬于V => <vi,vi>不屬于E并且沒有重復邊。

          我們先給出圖的ADT:
          package algorithms.graph;

          /**
           * The Graph ADT
           * 
          @author yovn
           *
           
          */
          public interface Graph {
              
              
          //mark
              public static interface Edge{
                  
          public int getWeight();
              }
              
              
          int vertexesNum();
              
              
          int edgeNum();
              
          boolean isEdge(Edge edge);
              
          void setEdge(int from,int to, int weight);
              Edge firstEdge(
          int vertex);
              Edge nextEdge(Edge edge);
              
          int toVertex(Edge edge);
              
          int fromVertex(Edge edge);
              String getVertexLabel(
          int vertex);
              
          void assignLabels(String[] labels);
              
          void deepFirstTravel(GraphVisitor visitor);
              
          void breathFirstTravel(GraphVisitor visitor);
              
              

          }
          其中的方法大多數比較一目了然,
          其中
          1)Edge firstEdge(int vertex)返回指定節點的邊的鏈表里存的第一條邊
          2)
          Edge nextEdge(Edge edge),順著邊鏈表返回下一條邊
          3)fromVertex,toVertex很簡單返回該邊的起始頂點和終結頂點
          4)
          getVertexLabel返回該定點對應的標號,assignLabels給所有頂點標上號

          GraphVisitor是一個很簡單的接口:
          package algorithms.graph;

          /**
           * 
          @author yovn
           *
           
          */
          public interface GraphVisitor {
              
              
          void visit(Graph g,int vertex);

          }
          OK,下面是該部分實現:
          package algorithms.graph;

          import java.util.Arrays;


          /**
           * 
          @author yovn
           *
           
          */
          public class DefaultGraph implements Graph {
              

              
          private static class _Edge implements Edge{
                  
                  
                  
          private static final _Edge NullEdge=new _Edge();
                  
                  
          int from;
                  
          int to;
                  
          int weight;
                  
                  _Edge nextEdge;
                  
                  
          private _Edge()
                  {
                      weight
          =Integer.MAX_VALUE;
                  }
                  
                  _Edge(
          int from, int to, int weight)
                  {
                      
                      
          this.from=from;
                      
          this.to=to;
                      
          this.weight=weight;
                  }
                  
          public int getWeight()
                  {
                      
          return weight;
                  }
                  
                  
              }
              
              
          private static class _EdgeStaticQueue
              {
                  _Edge first;
                  _Edge last;
              }
              
              
          private int numVertexes;
              
          private String[] labels;
              
          private int numEdges;
              
              
              
          private _EdgeStaticQueue[] edgeQueues;
              
              
          //tag the specified vertex be visited or not
              private boolean[] visitTags;

              
          /**
               * 
               
          */
              
          public DefaultGraph(int numVertexes) {
                  
          if(numVertexes<1)
                  {
                      
          throw new IllegalArgumentException();
                  }
                  
          this.numVertexes=numVertexes;
                  
          this.visitTags=new boolean[numVertexes];
                  
          this.labels=new String[numVertexes];
                  
          for(int i=0;i<numVertexes;i++)
                  {
                      labels[i]
          =i+"";
                      
                      
                  }
                  
          this.edgeQueues=new _EdgeStaticQueue[numVertexes];
                  
          for(int i=0;i<numVertexes;i++)
                  {
                      edgeQueues[i]
          =new _EdgeStaticQueue();
                      edgeQueues[i].first
          =edgeQueues[i].last=_Edge.NullEdge;
                      
                  }
                  
          this.numEdges=0;
              }

              

              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#edgeNum()
               
          */
              @Override
              
          public int edgeNum() {
                  
                  
          return numEdges;
              }

              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#firstEdge(int)
               
          */
              @Override
              
          public Edge firstEdge(int vertex) {
                  
          if(vertex>=numVertexes)    throw new IllegalArgumentException();
                  
          return edgeQueues[vertex].first;
                  
              }

              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#isEdge(algorithms.graph.Graph.Edge)
               
          */
              @Override
              
          public boolean isEdge(Edge edge) {
                  
                  
          return (edge!=_Edge.NullEdge);
              }

              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#nextEdge(algorithms.graph.Graph.Edge)
               
          */
              @Override
              
          public Edge nextEdge(Edge edge) {
                  
                  
          return ((_Edge)edge).nextEdge;
              }

              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#vertexesNum()
               
          */
              @Override
              
          public int vertexesNum() {
                  
                  
          return numVertexes;
              }


              @Override
              
          public int fromVertex(Edge edge) {
                  
                  
          return ((_Edge)edge).from;
              }

              @Override
              
          public void setEdge(int from, int to, int weight) {
                  
          //we don't allow ring exist 
                  if(from<0||from>=numVertexes||to<0||to>=numVertexes||weight<0||from==to)throw new IllegalArgumentException();
                  _Edge edge
          =new _Edge(from,to,weight);
                  edge.nextEdge
          =_Edge.NullEdge;
                  
          if(edgeQueues[from].first==_Edge.NullEdge)
                      edgeQueues[from].first
          =edge;
                  
          else 
                      edgeQueues[from].last.nextEdge
          =edge;
                  edgeQueues[from].last
          =edge;
                  
              }

              @Override
              
          public int toVertex(Edge edge) {

                  
          return ((_Edge)edge).to;
              }

              @Override
              
          public String getVertexLabel(int vertex) {
                  
          return labels[vertex];
              }

              @Override
              
          public void assignLabels(String[] labels) {
                  System.arraycopy(labels, 
          0this.labels, 0, labels.length);
                  
              }

                 
          //to be continue

          }

          二 深度優先周游
          即從從某一點開始能繼續往前就往前不能則回退到某一個還有邊沒訪問的頂點,沿這條邊看該邊指向的點是否已訪問,如果沒有訪問,那么從該指向的點繼續操作。

          那么什么時候結束呢,這里我們在圖的ADT實現里加上一個標志數組。該數組記錄某一頂點是否已訪問,如果找不到不到能繼續往前訪問的未訪問點,則結束。

          你可能會問,如果指定圖不是連通圖(既有2個以上的連通分量)呢?
          OK,解決這個問題,我們可以讓每一個頂點都有機會從它開始周游。
          下面看deepFirstTravel的實現:

           /* (non-Javadoc)
               * @see algorithms.graph.Graph#deepFirstTravel(algorithms.graph.GraphVisitor)
               
          */
              @Override
              
          public void deepFirstTravel(GraphVisitor visitor) {
                  Arrays.fill(visitTags, 
          false);//reset all visit tags
                  for(int i=0;i<numVertexes;i++)
                  {
                      
          if(!visitTags[i])do_DFS(i,visitor);
                  }

              }
              

              
          private final void do_DFS(int v, GraphVisitor visitor) {
                  
          //first visit this vertex
                  visitor.visit(this, v);
                  visitTags[v]
          =true;
                  
                  
          //for each edge from this vertex, we do one time
                  
          //and this for loop is very classical in all graph algorithms
                  for(Edge e=firstEdge(v);isEdge(e);e=nextEdge(e))
                  {
                      
          if(!visitTags[toVertex(e)])
                      {
                          do_DFS(toVertex(e),visitor);
                      }
                  }
                  
                  
              }

          三 廣度優先周游
          廣度優先周游從每個指定頂點開始,自頂向下一層一層的訪問。上一層所有頂點訪問完了才繼續下一層的訪問??梢园讯鏄涞膹V度優先周游看成圖的廣度優先周游的特例。
          (二叉樹是連通的無回路的有向圖,也是一棵根樹)
          同樣,廣度優先也要借助與一個隊列來存儲待訪問頂點

          下面是breathFirstTravel的實現,為了減小Java庫的影響,我自己實現一個很簡單的隊列。(你也可以使用
          ArrayList,但是記住隊列的定義,只能在頭刪除,在尾插入):
          private static class _IntQueue
              {
                  
          private static class _IntQueueNode
                  {
                      _IntQueueNode next;
                      
          int value;
                  }
                  _IntQueueNode first;
                  _IntQueueNode last;
                  
                  
          //queue can only insert to the tail
                  void add(int i)
                  {
                      _IntQueueNode node
          =new _IntQueueNode();
                      node.value
          =i;
                      node.next
          =null;
                      
          if(first==null)first=node;
                      
          else last.next=node;
                      last
          =node;
                  }
                  
                  
                  
          boolean isEmpty()
                  {
                      
          return first==null;
                  }
                  
          //queue can only remove from the head
                  int remove()
                  {
                      
          int val=first.value;
                      
          if(first==last)
                          first
          =last=null;
                      
          else
                          first
          =first.next;
                      
          return val;
                  }
                  
              }
              
          /* (non-Javadoc)
               * @see algorithms.graph.Graph#breathFirstTravel(algorithms.graph.GraphVisitor)
               
          */
              @Override
              
          public void breathFirstTravel(GraphVisitor visitor) {
                  Arrays.fill(visitTags, 
          false);//reset all visit tags
                  
                  
                  
          for(int i=0;i<numVertexes;i++)
                  {
                      
          if(!visitTags[i])
                      {
                              
                          do_BFS(i,visitor);
                      }
                  }

              }

              
          private void do_BFS(int v, GraphVisitor visitor) {
                  
          //and BFS will use an queue to keep the unvisited vertexes
                  
          // we  can also just use java.util.ArrayList
                  _IntQueue queue=new _IntQueue();
                  queue.add(v);
                  
          while(!queue.isEmpty())
                  {
                      
          int fromV=queue.remove();
                      visitor.visit(
          this, fromV);
                      visitTags[fromV]
          =true;
                      
          for(Edge e=firstEdge(fromV);isEdge(e);e=nextEdge(e))
                      {
                          
          if(!visitTags[toVertex(e)])
                          {
                              queue.add(toVertex(e));
                          }
                      }
                  }
              }

          OK,最后我們測試一下:
          /**
               * 
          @param args
               
          */
              
          public static void main(String[] args) {
                  
          /**
                   * For example, we have a graph:
                   * 0→1→2
                   * ↓ ↑↓
                   * 3  4 5
                   * ↓ ↑↓
                   * 6→7←8
                   * 
                   *  here ,all weight is 0, its a DAG(Directed Acyclic Graph) 
                   
          */

                  DefaultGraph g
          =new DefaultGraph(9);
                  g.setEdge(
          010);
                  g.setEdge(
          030);
                  g.setEdge(
          120);
                  g.setEdge(
          410);
                  g.setEdge(
          250);
                  
                  g.setEdge(
          360);
                  g.setEdge(
          740);
                  g.setEdge(
          580);
                  g.setEdge(
          670);
                  g.setEdge(
          870);
                  
                  
          //now we visit it
                  GraphVisitor visitor=new GraphVisitor()
                  {
                      @Override
                      
          public void visit(Graph g, int vertex) {
                          System.out.print(g.getVertexLabel(vertex)
          +" ");
                          
                      }
                      
                  };
                  System.out.println(
          "DFS==============:");
                  g.deepFirstTravel(visitor);
                  System.out.println();
                  System.out.println(
          "BFS==============:");
                  g.breathFirstTravel(visitor);
                  System.out.println();
              }
          posted on 2009-04-15 22:11 weesun一米陽光 閱讀(1514) 評論(0)  編輯  收藏 所屬分類: JAVA源碼總結備用
          主站蜘蛛池模板: 凤台县| 大连市| 沭阳县| 临泉县| 昌黎县| 晴隆县| 南开区| 伊通| 长顺县| 民勤县| 祥云县| 山东省| 金门县| 徐水县| 永仁县| 资阳市| 南雄市| 永和县| 府谷县| 永修县| 鄯善县| 房产| 团风县| 青浦区| 合山市| 唐山市| 中牟县| 澄城县| 密云县| 磐安县| 武鸣县| 江门市| 封开县| 三穗县| 高邑县| 黄陵县| 津市市| 嘉定区| 鸡东县| 固安县| 大姚县|