《數據結構與面向對象程序設計》第十週學習總結

學號20182304 2019-2020-1 《數據結構與面向對象程序設計》第十週學習總結

教材學習內容總結

  • 圖是跟通常的樹的概念所區分,它不約束除根節點外的每個頂點有且只能有一個父節點。圖中沒有根,每一個頂點都能與最多n-1個其它節點相連。
    • 鄰接:兩個頂點之間有一條邊,則稱這兩個頂點是鄰接的
    • 路徑:鏈接兩個頂點之間的一系列邊稱爲兩個頂點間的路徑,邊的條數稱爲路徑長度
    • 環路:首頂點與末頂點相同且路徑中沒有邊重複的路徑
    • 點的入度:以點爲終點的有向邊數
    • 點的出度:以點爲起點的有向邊數
  • 圖能夠根據是否具備方向分爲兩種:有向圖和無向圖
    • 無向圖:在圖中若任意兩個頂點的邊爲無向邊,則稱該圖爲無向圖
    • 有向圖:在圖中全部邊爲有向邊,即是有向圖
    • 有序一般用尖括號表示,無序一般用圓括號表示
  • 圖也能夠根據每條邊是否帶有權重分爲加權圖和網絡,能夠根據邊或弧的多少分爲稠密圖和稀疏圖,生成樹是一種特殊的圖
  • 生成樹:包含無向圖全部頂點的極小聯通子圖
  • 圖的表示:
    • 鄰接表:鄰接表,存儲方法跟樹的孩子鏈表示法相相似,是一種順序分配和鏈式分配相結合的存儲結構。如這個表頭結點所對應的頂點存在相鄰頂點,則把相鄰頂點依次存放於表頭結點所指向的單向鏈表中

  • 鄰接矩陣:鄰接矩陣:利用一個二維數組實現的矩陣,行列的元素表明頂點,而矩陣中的元素表明邊

  • 十字鏈表:十字鏈表(Orthogonal List)是有向圖的另外一種鏈式存儲結構。該結構能夠當作是將有向圖的鄰接表和逆鄰接表結合起來獲得的
    html

  • 圖的遍歷:
    • 廣度優先遍歷:像石頭落在水裏激起的漣漪同樣,一層一層的遍歷
    • BFS算法之因此叫作廣度優先搜索,是由於它始終將已發現的頂點和未發現的之間的邊界,沿其廣度方向向外擴展。亦即,算法首先會發現和s距離爲k的全部頂點,而後纔會發現和s距離爲k+1的其餘頂點。同深度優先搜索相反,BFS寬度優先搜索每次選擇深度最淺的節點優先擴展。而且當問題有解時,寬度優先算法必定可以找到解,而且在單位耗散時間的狀況下,能夠保證找到最優解。java

    • 深度優先遍歷:深度優先遍歷,顧名思義即爲一條道走到黑的搜索策略,行不通退回來換另一條道再走到黑,依次直到搜索完成。其過程簡要來講是對每個可能的分支路徑深刻到不能再深刻爲止,並且每一個節點只能訪問一次。能夠經過圖示清晰的說明。假設初始狀態是圖中全部頂點均未被訪問,則從某個頂點v出發,首先訪問該頂點,而後依次從它的各個未被訪問的鄰接點出發深度優先搜索遍歷圖,直至圖中全部和v有路徑相通的頂點都被訪問到。 若此時尚有其餘頂點未被訪問到,則另選一個未被訪問的頂點做起始點,重複上述過程,直至圖中全部頂點都被訪問到爲止。
  • 圖的最小生成樹:個有n個結點的連通圖的生成樹是原圖的極小連通子圖,且包含原圖中的全部n個結點,而且有保持圖連通的權值和邊最小git

教材學習中的問題和解決過程

  • 問題1:如何簡要理解拓撲算法
  • 問題1解決方案::相似於二叉樹的層次遍歷,遍歷全部結點,將入度爲0的結點存在一個棧中,依次輸出棧內的各個結點時,將每一個節點的子節點的度減1,而後將其中度爲0的結點存入棧中,循環執行上述操做,直到全部結點遍歷完。
  • 問題2:如何理解和繪製十字鏈表
  • 問題2解決方案:首先明確一些概念
    • 入弧和出弧:入弧表示圖中發出箭頭的頂點,出弧表示箭頭指向的頂點
    • 弧頭和弧尾:弧尾表示圖中發出箭頭的頂點,弧頭表示箭頭指向的頂點
    • 同弧頭和同弧尾:同弧頭,弧頭相同弧尾不一樣;同弧尾,弧頭不一樣互爲相同
      具體繪製過程:
    • 第一步,列出圖的全部頂點,並進行編號。每行含三個方格的橫格,頂點那欄分別填寫各頂點,入弧和出弧的暫時無論
    • 第二步,畫出各行對應的頂點表示出弧的全部關係。畫的時候爲了方便以後的連線,建議能夠將弧尾相同的畫在同一行,將弧頭相同的畫同一列。填寫弧尾與弧頭,同弧頭和同弧尾先暫時無論。
    • 第三步,連線。將表示頂點的三格圖中入弧指向對應列全部的四格方格。四格方格中,同弧頭指向本列,同弧尾指向本行。若出弧或同弧尾右邊沒有方格,則爲空。
  • 問題3:DFS和BFS算法比較,各自有什麼特色和應用場景
  • 問題3解決方案:
    • DFS深度優先搜索,空間需求較低,不須要BFS須要一個隊列保存搜索過程當中搜索記錄;其次,深搜在搜索過程當中要考慮回溯,在搜索HTML連接,爬取數據方面適用頗多;多用於解決連通性問題。
    • BFS廣度優先搜索,空間需求較高,根據其搜索模式,由於是按層進行搜索,因此很容易求得最短路徑。能夠應用於Dijkstral和prim算法

代碼調試中的問題和解決過程

  • 問題1:如何用Java具體實現深度遍歷算法

  • 問題1解決方案:具體實現以下。只要創建鄰接矩陣和對應的標誌數組,就能夠相對容易的實現。DFS利用遞歸來實現比較易懂,DFS非遞歸就是將須要的遞歸的元素利用一個棧Stack來實現,以達到遞歸時候的順序。
import java.util.Stack;

public class Graph {
//節點個數
    private static int number = 8;
//創立訪問標誌數組的布爾型數組
    private boolean[] flag;

//創立要遍歷節點的數組
    private int[] num= {1,2,3,4,5,6,7,8};
//創立這幾個數字的鄰接矩陣
    private int[][] edges = {
            {0, 1, 1, 0, 0, 0, 0, 0},
            {1, 0, 0, 1, 1, 0, 0, 0},
            {1, 0, 0, 0, 0, 1, 1, 0},
            {0, 1, 0, 0, 0, 0, 0, 1},
            {0, 1, 0, 0, 0, 0, 0, 1},
            {0, 0, 1, 0, 0, 0, 1, 0},
            {0, 0, 1, 0, 0, 1, 0, 0},
            {0, 0, 0, 1, 1, 0, 0, 0},

    };

    void DFSTraverse() {
        //設置一個和數字個數同等大小的布爾數組
        flag = new boolean[number] ;
        //從頂點開始,實現深度遍歷
        for (int i = 0; i < number; i++) {
            if (flag[i] == false) {
                // 若是當前頂點沒有被訪問,進入DFS
                DFS(i);
            }
        }
    }
//完成一次遍歷,直到後面無鏈接節點
    void DFS(int i) {
        // 標記第num[i]個節點被訪問
        flag[i] = true;
        //將該節點打印
        System.out.print(num[i] + " ");
        //尋找與num[i]節點相連的下一個訪問節點
        for (int j = 0; j < number; j++) {
            //從標誌數組第0位開始順序查找,若是這一點未被訪問,且與第num[i]個節點相連
            if (flag[j] == false && edges[i][j] == 1) {
                //遞歸
                DFS(j);
            }
        }
    }

    void DFS_Map(){
        flag = new boolean[number];
        Stack<Integer> stack =new Stack<Integer>();
        for(int i=0;i<number;i++){
            if(flag[i]==false){
                flag[i]=true;
                System.out.print(num[i]+" ");
                stack.push(i);
            }
            while(!stack.isEmpty()){
                int k = stack.pop();
                for(int j=0;j<number;j++){
                    if(edges[k][j]==1&&flag[j]==false){
                        flag[j]=true;
                        System.out.print(num[j]+" ");
                        stack.push(j);
                        break;
                    }
                }

            }
        }
    }


//測試類
    public static void main(String[] args) {

        Graph graph = new Graph();
        System.out.println("DFS遞歸:");
        graph.DFSTraverse();
        System.out.println();

        System.out.println("DFS非遞歸:");
        graph.DFS_Map();

    }
}

  • 問題2: kruskal(克魯斯卡爾)算法的實現思路是什麼,應該如何具體實現
  • 問題2解決方案:
    • 現將全部邊進行權值的從小到大排序
    • 定義一個一維數組表明鏈接過的邊,數組的下標爲邊的起點,值爲邊的終點
    • 按照排好序的集合用邊對頂點進行依次鏈接,鏈接的邊則存放到一維數組中
    • 用一維數組判斷是否對已經鏈接的邊能構成迴路,有迴路則無效,沒回路則是一條有效邊
    • 重複3,4直至遍歷完全部的邊爲止,即找到最小生成樹
public class GraphKruskal {
//定義Edge型數組
    private Edge[] edges;
    private int edgeSize;

    public GraphKruskal(int edgeSize) {
        //參數個數
        this.edgeSize = edgeSize;
        edges = new Edge[edgeSize];
        createEdgeKruskal();

    }
    //建立邊的集合,從小到大
    private void createEdgeKruskal() {

        Edge edge0 = new Edge(1, 3, 1);
        Edge edge1 = new Edge(4, 6, 2);
        Edge edge2 = new Edge(2, 5, 3);
        Edge edge3 = new Edge(3, 6, 4);
        Edge edge4 = new Edge(2, 3, 5);
        Edge edge5 = new Edge(3, 4, 5);
        Edge edge6 = new Edge(1, 4, 5);
        Edge edge7 = new Edge(3, 5, 6);
        Edge edge8 = new Edge(1, 2, 6);
        Edge edge9 = new Edge(5, 6, 6);

        edges[0] = edge0;
        edges[1] = edge1;
        edges[2] = edge2;
        edges[3] = edge3;
        edges[4] = edge4;
        edges[5] = edge5;
        edges[6] = edge6;
        edges[7] = edge7;
        edges[8] = edge8;
        edges[9] = edge9;
    }

    //kruskal算法建立最小生成樹
    public void createMinSpanTreeKruskal() {

        // 定義一個一維數組,下標爲連線的起點,值爲連線的終點
        int[] parent = new int[edgeSize];
        for (int i = 0; i < edgeSize; i++) {
            parent[i] = 0;
        }
        int sum = 0;
        //將數組中每個元素都賦給左邊
        for (Edge edge : edges) {
            // 找到起點和終點在臨時連線數組中的最後鏈接點,核心
            int start = find(parent, edge.start);
            int end = find(parent, edge.end);
            // 經過起點和終點找到的最後鏈接點是否爲同一個點,是則產生迴環

            if (start != end) {
                // 沒有產生迴環則將臨時數組中,起點爲下標,終點爲值
                parent[start] = end;
                System.out.println("訪問到了節點:{" + start + "," + end + "},權值:" + edge.weight);
                sum += edge.weight;
            }

        }

        System.out.println("最小生成樹的權值總和:" + sum);

    }

    // 獲取集合的最後節點
    private int find(int parent[], int index) {
        while (parent[index] > 0) {
            index = parent[index];
        }
        return index;
    }

    //鏈接頂點的邊
    class Edge {
        private int start;
        private int end;
        private int weight;

        public Edge(int start, int end, int weight) {
            this.start = start;
            this.end = end;
            this.weight = weight;

        }

    }


    public static void main(String[] args) {
        GraphKruskal graphKruskal = new GraphKruskal(10);
        graphKruskal.createMinSpanTreeKruskal();
    }

}

  • 問題3:Dijkstra的做用是什麼,如何具體實現Dijkstra算法
  • 問題3解決方案:迪傑斯特拉(Dijkstra)算法是典型最短路徑算法,用於計算一個節點到其餘節點的最短路徑。它的主要特色是以起始點爲中心向外層層擴展(廣度優先搜索思想),直到擴展到終點爲止。
    • 具體實現思路詳見代碼
    public class Dijkstra {
      public static final int M = 10000; // 表明正無窮
    
      public static void main(String[] args) {
          // 二維數組每一行分別是 A、B、C、D、E 各點到其他點的距離,
          // A -> A 距離爲0, 常量M 爲正無窮
          int[][] weight1 = {
                     {0, 13, 8, M, 30, M, 32},
                     {M, 0,  M,  M, M, 9, 7 },
                     {M, M, 0,  5,  M, M , M},
                     {M, M, M, 0,  6,  M , M},
                     {M, M, M, M,  0,  2 , M},
                     {M, M, M, M,  M,  0 ,17},
                     {M, M, M, M,  M,  M , 0},
    
          };
    
          int start = 0;
    
          int[] shortPath = dijkstra(weight1, start);
    
          for (int i = 0; i < shortPath.length; i++)
              System.out.println("從" + start + "出發到" + i + "的最短距離爲:" + shortPath[i]);
      }
    
      public static int[] dijkstra(int[][] weight, int start) {
          // 接受一個有向圖的權重矩陣,和一個起點編號start(從0編號,頂點存在數組中)
          // 返回一個int[] 數組,表示從start到它的最短路徑長度
          int n = weight.length; // 頂點個數
          int[] shortPath = new int[n]; // 保存start到其餘各點的最短路徑
          String[] path = new String[n]; // 保存start到其餘各點最短路徑的字符串表示
          for (int i = 0; i < n; i++)
              path[i] = new String(start + "-->" + i);
          int[] visited = new int[n]; // 標記當前該頂點的最短路徑是否已經求出,1表示已求出
    
          // 初始化,第一個頂點已經求出
          shortPath[start] = 0;
          visited[start] = 1;
    
          for (int count = 1; count < n; count++) { // 要加入n-1個頂點
              int k = -1; // 選出一個距離初始頂點start最近的未標記頂點
              int dmin = Integer.MAX_VALUE;
              for (int i = 0; i < n; i++) {
                  if (visited[i] == 0 && weight[start][i] < dmin) {
                      dmin = weight[start][i];
                      k = i;
                  }
              }
    
              // 將新選出的頂點標記爲已求出最短路徑,且到start的最短路徑就是dmin
              shortPath[k] = dmin;
              visited[k] = 1;
    
              // 以k爲中間點,修正從start到未訪問各點的距離
              for (int i = 0; i < n; i++) {
                  //若是 '起始點到當前點距離' + '當前點到某點距離' < '起始點到某點距離', 則更新
                  if (visited[i] == 0 && weight[start][k] + weight[k][i] < weight[start][i]) {
                      weight[start][i] = weight[start][k] + weight[k][i];
                      path[i] = path[k] + "-->" + i;
                  }
              }
          }
          for (int i = 0; i < n; i++) {
    
              System.out.println("從" + start + "出發到" + i + "的最短路徑爲:" + path[i]);
          }
          System.out.println("=====================================");
          return shortPath;
      }
    }

代碼託管

數組

網絡

上週考試錯題總結

結對及互評

評分標準

  1. 正確使用Markdown語法(加1分):
    • 不使用Markdown不加分
    • 有語法錯誤的不加分(連接打不開,表格不對,列表不正確...)
    • 排版混亂的不加分
  2. 模板中的要素齊全(加1分)
    • 缺乏「教材學習中的問題和解決過程」的不加分
    • 缺乏「代碼調試中的問題和解決過程」的不加分
    • 代碼託管不能打開的不加分
    • 缺乏「結對及互評」的不能打開的不加分
    • 缺乏「上週考試錯題總結」的不能加分
    • 缺乏「進度條」的不能加分
    • 缺乏「參考資料」的不能加分
  3. 教材學習中的問題和解決過程, 一個問題加1分數據結構

  4. 代碼調試中的問題和解決過程, 一個問題加1分學習

  5. 本週有效代碼超過300分行的(加2分)
    • 一週提交次數少於20次的不加分
  6. 其餘加分:
    • 週五前發博客的加1分
    • 感想,體會不假大空的加1分
    • 排版精美的加一分
    • 進度條中記錄學習時間與改進狀況的加1分
    • 有動手寫新代碼的加1分
    • 課後選擇題有驗證的加1分
    • 代碼Commit Message規範的加1分
    • 錯題學習深刻的加1分
    • 點評認真,能指出博客和代碼中的問題的加1分
    • 結對學習狀況真實可信的加1分
  7. 扣分:
    • 有抄襲的扣至0分
    • 代碼做弊的扣至0分
    • 遲交做業的扣至0分

點評模板:

  • 博客中值得學習的或問題:
    • 講解內容較爲豐富
  • 代碼中值得學習的或問題:
    • 結合所學,給出了算法的具體實現
  • 基於評分標準,我給本博客打分:18分測試

  • 參考示例this

點評過的同窗博客和代碼

  • 本週結對學習狀況
    • 20182302
    • 結對學習內容
      • 深度優先遍歷的具體實現
      • 十字鏈表的繪製
      • 辨析圖和數的區別
  • 上週博客互評狀況

其餘(感悟、思考等,可選)

  • 數據結構學習也算到尾聲了,圖這一章算法比較豐富,須要咱們花費時間逐一認真理解,並落實到代碼實現。只有這樣,咱們對數據結構的理解才能真正融會貫通

    學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 200/200 2/2 20/20
第二週 300/500 2/4 18/38
第三週 500/1000 3/7 22/60
第四周 300/1300 2/9 30/90
第五週 1600/2900 2/11 20/110
第六週 981 /3881 2/12 25/135
第七週 1700/5518 3/15 45/180
第八週 700/6200 2/17 20/200
第九周 4300/10500 2/19 30/230
第十週 2064/12564 1/20 30/260

嘗試一下記錄「計劃學習時間」和「實際學習時間」,到期末看看能不能改進本身的計劃能力。這個工做學習中很重要,也頗有用。
耗時估計的公式:Y=X+X/N ,Y=X-X/N,訓練次數多了,X、Y就接近了。

參考:軟件工程軟件的估計爲何這麼難軟件工程 估計方法

  • 計劃學習時間:30小時

  • 實際學習時間:30小時

  • 改進狀況:

(有空多看看現代軟件工程 課件
軟件工程師能力自我評價表
)

參考資料

相關文章
相關標籤/搜索