數據結構之圖(java語言版)

數據結構之圖(java語言版)

圖是比樹更復雜的結構,樹是一對多的關係,圖是多對多的關係。

數據結構與算法學習目錄

一、基本概念

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

數據結構與算法學習目錄

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章