20202320 實驗九《資料結構與面向物件程式設計——圖》實驗報告
阿新 • • 發佈:2021-12-23
課程:《程式設計與資料結構》
班級: 2023
姓名: 陳歡
學號:20202320
實驗教師:王志強
實驗日期:2021年12月23日
必修/選修: 必修
##1.實驗內容
(1) 初始化:根據螢幕提示(例如:輸入1為無向圖,輸入2為有向圖)初始化無向圖和有向圖(可用鄰接矩陣,也可用鄰接表),圖需要自己定義(頂點個數、邊個數,建議先在草稿紙上畫出圖,然後再輸入頂點和邊數)(2分)
(2) 圖的遍歷:完成有向圖和無向圖的遍歷(深度和廣度優先遍歷)(4分)
(3) 完成有向圖的拓撲排序,並輸出拓撲排序序列或者輸出該圖存在環(3分)
(4) 完成無向圖的最小生成樹(Prim演算法或Kruscal演算法均可),並輸出(3分)
(5) 完成有向圖的單源最短路徑求解(迪傑斯特拉演算法)(3分)
##2.實驗過程及結果
(1)初始化:根據螢幕提示(例如:輸入1為無向圖,輸入2為有向圖)初始化無向圖和有向圖(可用鄰接矩陣,也可用鄰接表),圖需要自己定義(頂點個數、邊個數,建議先在草稿紙上畫出圖,然後再輸入頂點和邊數)(2分)
(2) 圖的遍歷:完成有向圖和無向圖的遍歷(深度和廣度優先遍歷)(4分)
如上圖所示:其中鄰接矩陣表示的有向圖是(由於作圖原因,其中頂點0直接與頂點5相連通):
無向圖的遍歷:
完整程式碼如下:
public void Depth(int i){
//標記第i個節點以遍歷
visited[i]=true;
//列印當前已經遍歷的節點
visit(i);
//遍歷鄰接矩陣中第i個節點的直接連通節點
for(int j=0;j<nodenum;j++){
if(Adjacency[i][j]==1&&visited[j]==false){
Depth(j);
}
}
}
//深度優先遍歷
public void dfs(){
//初始化節點訪問標記
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
//從沒有被遍歷的節點開始深度優先遍歷
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
Depth(i);
}
}
System.out.println();
}
//廣度遍歷
public void bfs(){
//初始化節點訪問標記
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
Queue<Integer> queue=new LinkedList<Integer>();
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
visited[i]=true;
//列印當前已經遍歷的節點
visit(i);
//新增到佇列裡
queue.add(i);
//只要佇列不為空
while(!queue.isEmpty()){
//出隊節點,也就是這一層的節點
int k=queue.poll();
//遍歷所有未被訪問的鄰接節點,放入佇列
for(int j=0;j<nodenum;j++){
//也就是訪問這一層剩下的未被訪問的節點
if(Adjacency[k][j]==1&&visited[j]==false){
visited[j]=true;
visit(j);
queue.add(j);
}
}
}
}
}
System.out.println();
}
(3)完成有向圖的拓撲排序,並輸出拓撲排序序列或者輸出該圖存在環(3分)
有向圖的拓撲排序如實驗內容(1)所示。
完整程式碼如下:
public void Topo(int i){ int sum=0;
public void Topo(int i){
itn sum=0;
for(int j=0;j<nodenum;j++){
sum=sum+rear[j][i];//行
}
if(sum==0){//第i個節點沒有前驅
visited[i]=true;
visit(i);
for(int k=0;k<nodenum;k++){
rear[i][k]=0;//列
}
}
}
public void Topological(){
for(int i=0;i<visited.length;i++){
visited[i]=false;
}
int sum0=0;
do {
int i;
for (i = 0; i < nodenum; i++) {
if (visited[i] == false) {
Topo(i);
}
}
if(i==nodenum&&visited[i-1]==false) {
System.out.println("該圖存在環!");
sum0=nodenum;
}else {
for (int j = 0; j < nodenum; j++) {//判斷是否全部遍歷
if (visited[j] == true) {
sum0++;
} else {
sum0 = 0;
break;
}
}
}
}while(sum0!=nodenum);
System.out.println();
}(4) 完成無向圖的最小生成樹(Prim演算法或Kruscal演算法均可),並輸出(3分)
使用的是Prime 演算法,完整程式碼如下:
public void Prime(){
for(int i=0;i<nodenum;i++){
visited[i]=false;
}
for(int i=0;i<nodenum;i++){
if(visited[i]==false){
Primehelp(i);
}
}
rear=Adjacency;
}//生成最小生成樹
public void Primehelp(int i) {//無向圖使用
int key = 0;
int a = 0, b = 0;
if (visited[i] == false) {
for (int j = 0; j < nodenum; j++) {
if (rear[i][j] > key)
key = rear[i][j];//第i行的最大值
}
for (int j = 0; j < nodenum; j++) {
if (rear[i][j] < key && rear[i][j] != 0) {
key = rear[i][j];//第i行的最小值
a = i;
b = j;
}
}
for (int num = 0; num < nodenum; num++) {
if (visited[num] == true) {
for (int p = 0; p < nodenum; p++) {
if (rear[num][p] < key && rear[num][p] != 0) {
key = rear[num][p];
a = num;
b = p;
}
}
}
}
}
if (visited[b] == false) {
rear[a][b] = 0;
rear[b][a] = 0;
visited[i] = true;
visit(i);
System.out.print("(" + key + ")");
Primehelp(b);
} else {
rear[a][b] = 0;
rear[b][a] = 0;
int sum = 0;
for (int ni = 0; ni < nodenum; ni++) {
if (visited[ni] == true) {
sum++;
}
}
if (sum != nodenum)
if (sum == nodenum - 1) {
for (int ni = 0; ni < nodenum; ni++) {
if (visited[ni] == false)
System.out.println(vertices[ni]);
}
} else
Primehelp(i);
}
}
(5) 完成有向圖的單源最短路徑求解(迪傑斯特拉演算法)(3分)
暫未完成
##3.實驗中遇到的問題和解決方法
--問題一:進行拓撲排序時總是不能夠在最後輸出正確的鄰接矩陣。
--解決方法:給鄰接矩陣做一個“分身”,在進行拓撲排序時,只對“分身”進行操作,而要輸出的鄰接矩陣並不做改變。
同時該“分身”不能夠定義為rear=Adjacency,因為這樣定義出的“分身”矩陣和Adjacency矩陣是同一個“儲存空間”(應該是這麼叫吧)。
--問題二:遞迴是難以退出。
--解決方法:通過除錯,找出能夠退出的唯一條件,這個條件需要做到所謂“當且僅當”(maybe)。
##4.實驗心得與體會
首先是後悔沒有早點動手,到了考試周才匆匆忙忙的做。
其次是遞迴的運用,在這次的實驗中,遞迴的重要作用比之前的更加突出,遞迴非常考驗人的邏輯(我覺得)。
最後,最後一次實驗值得好好……
##5.參考資料
--資料結構和演算法動態視覺化 (Chinese) - VisuAlgo
--(3條訊息) JAVA實現圖的深度優先遍歷._今天又是充滿希望的一天-CSDN部落格_java實現深度優先搜尋
--《Java程式設計教程(第九版)》
--《Java軟體結構與資料結構(第四版)》