1. 程式人生 > >20172333 2018-2019-1 《程序設計與數據結構》第九周學習總結

20172333 2018-2019-1 《程序設計與數據結構》第九周學習總結

結點 iter fin rsa 列表 數據結構 接口 order 講解

20172333 2018-2019-1 《程序設計與數據結構》第九周學習總結

教材學習內容總結

《Java軟件結構與數據結構》第十五章-圖

一、無向圖

  • 無向圖的定義
    • 圖是由結點與結點相連接構成的,與樹類似。這些結點被常常稱作頂點(vertice),這些頂點的連接叫做邊(edge)。
    • 無向圖(undirected graph)是一種邊為無序結點對的圖。
    • 兩個頂點之間有一條連通邊的頂點,我們稱作它們為領接的(adjacent)。這兩個頂點也叫作鄰居(neighbor),自己是自己的鄰居叫做自循環(self-loop)。
    • 一個完整的無向圖就是擁有n(n-1)/2的邊的無向圖。(n是頂點數,且沒有自循環)
    • 長度是兩個頂點的路徑長度,即路徑中的邊的條數或者頂點數-1。
    • 環路(cycle)是首頂點與尾頂點相同且沒有重邊的路徑。
    • 無向樹是一種連通的無環無向圖,其中一個元素被指定為樹根。
      技術分享圖片

二、有向圖

  • 有向圖的定義
    • 有向圖是一種邊為有序頂點對的圖。
      技術分享圖片

三、網絡

  • 網絡的定義
    • 網絡又被叫做加權圖。
      技術分享圖片

四、常用的圖算法

  • 遍歷
    • 遍歷被分為兩種,一種叫廣度優先遍歷(Breadth-first travelsal),另外一種被叫做深度優先遍歷(depth-first traversal)
    • 這兩個遍歷的區別在於,深度優先遍歷是使用棧來管理遍歷,而廣度優先遍歷是使用隊列。(書上粗略而又沒有什麽用的解釋)
    • 這兩個遍歷的使用效果區別:深度優先遍歷是遇到分叉的頂點進行記錄,並繼續前進直到沒有分叉後返回上一個分叉頂點。而廣度優先遍歷則是以扇形的模式對於周邊的頂點遍歷後再進行深入。
    • 深度優先遍歷示意圖技術分享圖片

    • 廣度優先遍歷示意圖技術分享圖片

    • 深度遍歷與廣度遍歷代碼

public Iterator<T> iteratorDFS(int startIndex)
    {
        Integer x;
        boolean found;
        StackADT<Integer> traversalStack = new LinkedStack<Integer>();
        UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();
        boolean[] visited = new boolean[numVertices];

        if (!indexIsValid(startIndex))
            return resultList.iterator();

        for (int i = 0; i < numVertices; i++)
            visited[i] = false;
        
        traversalStack.push(new Integer(startIndex));
        resultList.addToRear(vertices[startIndex]);
        visited[startIndex] = true;
        
        while (!traversalStack.isEmpty())
        {
            x = traversalStack.peek();
            found = false;

            //Find a vertex adjacent to x that has not been visited
            //     and push it on the stack 
            for (int i = 0; (i < numVertices) && !found; i++)
            {
                if (adjMatrix[x.intValue()][i] && !visited[i])
                {
                    traversalStack.push(new Integer(i));
                    resultList.addToRear(vertices[i]);
                    visited[i] = true;
                    found = true;
                }
            }
            if (!found && !traversalStack.isEmpty())
                traversalStack.pop();
        }
        return new GraphIterator(resultList.iterator());
    }

    
    public Iterator<T> iteratorDFS(T startVertex)
    {        
        return iteratorDFS(getIndex(startVertex));
    }

    
    public Iterator<T> iteratorBFS(int startIndex)
    {
        Integer x;
        QueueADT<Integer> traversalQueue = new LinkedQueue<Integer>();
        UnorderedListADT<T> resultList = new ArrayUnorderedList<T>();

        if (!indexIsValid(startIndex))
            return resultList.iterator();

        boolean[] visited = new boolean[numVertices];
        for (int i = 0; i < numVertices; i++)
            visited[i] = false;
        
        traversalQueue.enqueue(new Integer(startIndex));
        visited[startIndex] = true;
        
        while (!traversalQueue.isEmpty())
        {
            x = traversalQueue.dequeue();
            resultList.addToRear(vertices[x.intValue()]);

            //Find all vertices adjacent to x that have not been visited
            //     and queue them up 
            
            for (int i = 0; i < numVertices; i++)
            {
                if (adjMatrix[x.intValue()][i] && !visited[i])
                {
                    traversalQueue.enqueue(new Integer(i));
                    visited[i] = true;
                }
            }
        }
        return new GraphIterator(resultList.iterator());
    }
    
   
    public Iterator<T> iteratorBFS(T startVertex)
    {        
        return iteratorBFS(getIndex(startVertex));
    }
  • 測試連通性
    • 只有在廣度優先遍歷中的頂點數與圖中的頂點數相同的時候才是連通的。
  • 最小生成樹
    • 最小生成樹就是該樹的權重小於等於該圖的其他生成樹的權重。
    • 最小生成樹示意圖(分兩種算法,一種是Prim算法,適合邊較稠密的那種,另外一種是Dijkstra算法,這種適用於頂點多的那種)圖1技術分享圖片

,圖2技術分享圖片

  • 最小生成樹推演算法
public Graph getMST()
    {
        int x, y;
        int[] edge = new int[2];
        StackADT<int[]> vertexStack = new LinkedStack<int[]>();
        Graph<T> resultGraph = new Graph<T>();

        if (isEmpty() || !isConnected())
            return resultGraph;
        
        resultGraph.adjMatrix = new boolean[numVertices][numVertices];
        
        for (int i = 0; i < numVertices; i++)
            for (int j = 0; j < numVertices; j++)
                resultGraph.adjMatrix[i][j] = false;
                
        resultGraph.vertices = (T[])(new Object[numVertices]);
        boolean[] visited = new boolean[numVertices];
        
        for (int i = 0; i < numVertices; i++)
            visited[i] = false;        
        
        edge[0] = 0;
        resultGraph.vertices[0] = this.vertices[0];
        resultGraph.numVertices++;
        visited[0] = true;

        // Add all edges that are adjacent to vertex 0 to the stack. 
        for (int i = 0; i < numVertices; i++)
        {
            if (!visited[i] && this.adjMatrix[0][i])
            {
                edge[1] = i;
                vertexStack.push(edge.clone());
                visited[i] = true;
            }
        }

        while ((resultGraph.size() < this.size()) && !vertexStack.isEmpty())
        {
            // Pop an edge off the stack and add it to the resultGraph. 
            edge = vertexStack.pop();
            x = edge[0];
            y = edge[1];
            resultGraph.vertices[y] = this.vertices[y];
            resultGraph.numVertices++;
            resultGraph.adjMatrix[x][y] = true;
            resultGraph.adjMatrix[y][x] = true;
            visited[y] = true;

        
            for (int i = 0; i < numVertices; i++)
            {
                if (!visited[i] && this.adjMatrix[i][y])
                {
                    edge[0] = y;
                    edge[1] = i;
                    vertexStack.push(edge.clone());
                    visited[i] = true;
                }
            }
        }

        return resultGraph;
    }

五、圖的實現策略

  • 鄰接列表與鄰接矩陣
    • 鄰接列表的形式與之前我們學習處理哈希沖突所采用的數組與鏈表結合的方法類似,都是以數組的頭結點來實現的列表。
    • 鄰接列表的示意圖技術分享圖片

    • 鄰接矩陣分為兩個部分,一個部分是一個一位數組用於存儲每個頂點數據,另一個部分是一個二維數組用於存放每個頂點間的關系(邊)的數據,這個二維數組就是鄰接矩陣。
    • 鄰接矩陣的無向圖有幾個特點:
      • 主對角線上必然為0
      • 矩陣必然對稱
      • 矩陣的非0元素必然是邊的2倍

        用鄰接矩陣實現無向圖

  • 鄰接矩陣擴展的GraphADT接口
public interface GraphADT<T>
{
    /** 
     * Adds a vertex to this graph, associating object with vertex. 
     * 
     * @param vertex the vertex to be added to this graph
     */
    public void addVertex(T vertex);

    /** 
     * Removes a single vertex with the given value from this graph. 
     * 
     * @param vertex the vertex to be removed from this graph
     */
    public void removeVertex(T vertex);

    /** 
     * Inserts an edge between two vertices of this graph. 
     *
     * @param vertex1 the first vertex
     * @param vertex2 the second vertex
     */
    public void addEdge(T vertex1, T vertex2);

    /** 
     * Removes an edge between two vertices of this graph. 
     *
     * @param vertex1 the first vertex
     * @param vertex2 the second vertex
     */
    public void removeEdge(T vertex1, T vertex2);

    /** 
     * Returns a breadth first iterator starting with the given vertex. 
     * 
     * @param startVertex the starting vertex
     * @return a breadth first iterator beginning at the given vertex
     */
    public Iterator iteratorBFS(T startVertex);

    /** 
     * Returns a depth first iterator starting with the given vertex. 
     *
     * @param startVertex the starting vertex
     * @return a depth first iterator starting at the given vertex
     */
    public Iterator iteratorDFS(T startVertex);

    /** 
     * Returns an iterator that contains the shortest path between
     * the two vertices. 
     *
     * @param startVertex the starting vertex
     * @param targetVertex the ending vertex
     * @return an iterator that contains the shortest path
     *         between the two vertices
     */
    public Iterator iteratorShortestPath(T startVertex, T targetVertex);

    /** 
     * Returns true if this graph is empty, false otherwise. 
     *
     * @return true if this graph is empty
     */
    public boolean isEmpty();

    /** 
     * Returns true if this graph is connected, false otherwise. 
     *
     * @return true if this graph is connected
     */
    public boolean isConnected();

    /** 
     * Returns the number of vertices in this graph. 
     *
     * @return the integer number of vertices in this graph
     */
    public int size();

    /** 
     * Returns a string representation of the adjacency matrix. 
     *
     * @return a string representation of the adjacency matrix
     */
    public String toString();
}
  • NetworkADT
public interface NetworkADT<T> extends GraphADT<T>
{
    /** 
     * Inserts an edge between two vertices of this graph. 
     *
     * @param vertex1 the first vertex
     * @param vertex2 the second vertex
     * @param weight the weight 
     */
    public void addEdge(T vertex1, T vertex2, double weight);
    
    /** 
     * Returns the weight of the shortest path in this network. 
     *
     * @param vertex1 the first vertex
     * @param vertex2 the second vertex
     * @return the weight of the shortest path in this network
     */
    public double shortestPathWeight(T vertex1, T vertex2);
}
  • addEdge方法實現
public void addEdge(T vertex1, T vertex2)
    {
        addEdge(getIndex(vertex1), getIndex(vertex2));
    }

     public void addEdge(int index1, int index2)
    {
        if (indexIsValid(index1) && indexIsValid(index2))
        {
            adjMatrix[index1][index2] = true;
            adjMatrix[index2][index1] = true;
            modCount++;
        }
    }
  • 在實現這個方法的時候調用了getIndex的方法來定位正確的索引。

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

  • 問題1:圖的概念與樹的概念有重復的也有不同的地方,它們有什麽區別?
  • 回答1:樹其實就是沒有連接在一起的圖,這裏的沒有連接在一起指的是連接成環。
  • 問題2:拓撲序是什麽東西?
  • 回答2:樹上的解釋是有向圖中沒有環路,且有一條從A到B的邊,則可以把頂點A安排在頂點B之前。這種排序得到的頂點次序稱為拓撲序。但是沒圖沒真相,光從字面意思有點難以體會,然後我就去網上找到一個比較實際的拓撲序的圖。後來我自己對於拓撲序的解釋是“優先找到沒有入邊的點,將它打印出來,再將它的邊和它本身刪除,重復進行上次運算直到沒有點。”技術分享圖片

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

  • 問題1:在做PP15.1的時候,進行測試連通方法的時候,我故意想要將所有頂點連接在一起,最後測試的時候,測試結果卻是沒有連通,圖。技術分享圖片

  • 解決方案:在檢查代碼的過程中圖,我發現了我的返回錯誤的要求不僅要建立A到B的邊,也要建立B到A的邊,否則就錯誤。技術分享圖片

代碼托管

-圖代碼技術分享圖片

上周考試錯題總結

結對及互評

基於評分標準,我給李楠的博客打分:7分。得分情況如下:

正確使用Markdown語法(加1分)

模板中的要素齊全(加1分)

教材學習中的問題和解決過程, (加3分)

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

感想,體會真切的(加1分)

點評認真,能指出博客和代碼中的問題的(加1分)

點評過的同學博客和代碼

  • 本周結對學習情況
    • 20172330李楠
    • 結對照片
    • 結對學習內容

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

實現網絡圖的時候對於各種遍歷運用極度不熟悉。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一周 0/0 1/1 10/10
第二周 0/0 1/2 10/20
第三周 1500/1500 1/3 10/30
第四周 2761/4261 2/5 25/55
第五周 814/5075 1/6 15/70
第六周 1091/6166 1/7 15/85
第七周 1118/7284 1/8 15/100
第八周 1235/8519 2/10 15/115
第九周 1757/10276 1/11 25/140
  • 《Java程序設計與數據結構教程(第二版)》

  • 《Java程序設計與數據結構教程(第二版)》學習指導
  • 廣度優先遍歷與深度優先遍歷詳細解釋
  • 圖的遍歷之 深度優先搜索和廣度優先搜索
  • 史上最清晰的紅黑樹講解(上)
  • 史上最清晰的紅黑樹講解(下)

20172333 2018-2019-1 《程序設計與數據結構》第九周學習總結