20172332 2017-2018-2 《程序設計與數據結構》第九周學習總結

20172332 2017-2018-2 《程序設計與數據結構》第九周學習總結

教材學習內容總結

第十五章 圖

  • 1.無向圖是一種邊爲無序結點對的圖:(A,B)與(B,A)的含義是徹底同樣的。
  • 2.若是圖中的兩個頂點之間有一條連通邊,則稱這兩個頂點是鄰接的。(鄰接必須有邊,不然不叫鄰接)
  • 3.連通一個頂點及其自身的邊稱爲自循環(A,A)
  • 4.若是無向圖擁有最大數目的連通頂點的邊,則認爲這個無向圖是徹底的。(任意兩個頂點之間都會有一條邊)
  • 5.n個頂點的無向圖,要使該圖爲徹底的,要求有n(n-1)/2條邊。(不算重複的邊)
  • 6.路徑:圖中的一系列邊,從一個頂點到另外一個頂點的邊所組成。
  • 7.無向圖中路徑是雙向的。
  • 8.路徑的長度=該路徑中邊的條數=頂點數-1。
  • 9.若是無向圖中的任意兩個頂點之間都存在一條路徑,則認爲這個無向圖是連通的。
  • 10.環路是一種首頂點和末頂點相同且沒有重邊的路徑。沒有環路的圖稱爲無環的。
  • 11.無向樹是一種連通的無環無向圖,其中一個元素被指定爲樹根。
  • 12.有向圖(雙向圖):一種邊爲有序頂點對的圖。(A,B)和(B,A)是不一樣的邊。
  • 13.有向圖的路徑不是雙向的。
  • 14.拓撲序:有向圖中沒有環路,且有一條從A到B的邊,這能夠把頂點A安排在頂點B以前,這種排列獲得的頂點次序。
  • 15.有向樹的附加屬性:
    • (1)不存在其餘頂點到樹根的鏈接。
    • (2)每一個非樹根元素剛好有一個鏈接。
    • (3)樹根到每一個其餘頂點都有一條路徑。
  • 16.網絡(加權圖):每條邊都帶有權重或代價的圖。路徑權重是該路徑中各條邊權重的和。
  • 17.兩種遍歷:①廣度優先遍歷[隊列]:相似於樹的層次遍歷。②深度優先遍歷[棧]:相似於樹的前序遍歷。(圖中不存在根結點,因此能夠從任一頂點開始)
  • 18.測試連通性:不論哪一個爲起始頂點,當且僅當廣度優先遍歷中的頂點數目等於圖中的頂點數目時,該圖纔是連通的。
  • 19.最小生成樹:含有圖中全部頂點和部分邊的樹,其邊的權重總和小於或等於同一個圖中其餘任何一棵生成樹的權重總和。
  • 20.判斷最短路徑:①在廣度優先遍歷期間在對每一個頂點存儲另兩個信息便可:從起始頂點到本頂點的路徑長度,以及路徑中做爲本頂點前驅的那個頂點。②尋找加權圖的最便宜路徑。
  • 21.鄰接列表:用一種相似於鏈表的動態結點來存儲每一個結點帶有的邊。

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

  • 問題1:無向圖中的連通和徹底之間的關係是什麼。
  • 問題1解決方案:
區別 聯繫
徹底 任意兩個頂點之間都會有一條
連通 任意兩個頂點之間都存在一條路徑 徹底的必定連通
  • 問題2:拓撲序的深刻了解。
  • 問題2解決方案:(1)每一個頂點出現且只出現一次;(2)若A在序列中排在B的前面,則在圖中不存在從B到A的路徑。也能夠定義爲:拓撲排序是對有向無環圖的頂點的一種排序,它使得若是存在一條從頂點A到頂點B的路徑,那麼在排序中B出如今A的後面。該排序算法將偏序關係轉換爲了全序關係,從而保證告終果的惟一性。
  • 補充知識:偏序:有向圖中兩個頂點之間不存在環路,至於連通與否,是無所謂的。全序:就是在偏序的基礎之上,有向無環圖中的任意一對頂點還須要有明確的關係(反映在圖中,就是單向連通的關係,注意不能雙向連通,那就成環了)。html

  • 問題3:廣度優先遍歷的代碼理解。java

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()]);
            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());
    }
  • 問題3解決方案:用隊列管理遍歷,用無序列表構造結果。放入無序列表的順序是標記元素已讀的順序,入隊的順序與放入無序列表的頂點的相鄰頂點是否已讀有關係,若已讀就不入隊,進入下一步入無序列表。
//先把全部的頂點都標記成未訪問的。
for (int i = 0; i < numVertices; i++)
            visited[i] = false;
//把其實頂點入隊,標記爲已訪問。
traversalQueue.enqueue(new Integer(startIndex));
        visited[startIndex] = true;
//當隊不爲空的時候,x等於隊首元素
while (!traversalQueue.isEmpty())
        {
            x = traversalQueue.dequeue();
            //intValue()以 int 類型返回該 Integer 的值。把頂點對應的值放入無序列表中。 
            resultList.addToRear(vertices[x.intValue()]);
            //小於頂點數作一個循環,鄰接矩陣的出的那個元素
            for (int i = 0; i < numVertices; i++)
            {
                //若是頂點到i存在邊而且i沒有被標記,則i入隊,i被標記。
                if (adjMatrix[x.intValue()][i] && !visited[i])
                {
                    traversalQueue.enqueue(new Integer(i));
                    visited[i] = true;
                }
            }
        }
        return new GraphIterator(resultList.iterator());
  • 問題4:mstNetwork()方法中resultGraph.adjMatrix[i][j]=Double.POSITIVE_INFINITY是什麼意思?
  • 問題4解決方案:保存double 類型的正無窮大值的常量。

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

  • 問題1:git


  • 問題1解決方案:改爲下圖就好,由於進行的是頂點的元素進行比較,而不是結點進行比較。算法

  • 問題2:關於無向加權圖的代碼實現。
  • 問題2解決方案:想法及理解在註釋中體現。
//增長頂點
public void addVertex(T vertex) {
        //越界擴容
        if ((numVertices + 1) == adjMatrix.length)
            expandCapacity();

        //把新加頂點到剩下全部頂點的路徑定義爲正無窮(鄰接矩陣)
        vertices[numVertices] = vertex;
        for (int i = 0; i <= numVertices; i++) {
            adjMatrix[numVertices][i] = Double.POSITIVE_INFINITY;
            adjMatrix[i][numVertices] = Double.POSITIVE_INFINITY;
        }
        numVertices++;
        modCount++;
    }
//刪除頂點
    public void removeVertex(T vertex) {
        removeVertex(getIndex(vertex));
    }

    //根據索引去找到該頂點
    public void removeVertex(int index) {
        //當索引值存在時
        if (indexIsValid(index)) {
            //從該頂點開始,頂點集中的每一個數的位置須要向前移一位
            for (int j = index; j < numVertices - 1; j++) {
                vertices[j] = vertices[j + 1];
            }
            //數組中的最後一個位置定義爲空
            vertices[numVertices - 1] = null;

            //從該頂點開始,邊的起點從該頂點變爲下一個頂點,由於頂點集的位置向前移了一位。
            for (int i = index; i < numVertices - 1; i++) {
                for (int x = 0; x < numVertices; x++)
                    adjMatrix[i][x] = adjMatrix[i + 1][x];

            }
            //由於無向圖,因此從該頂點開始,邊的終點從該頂點變爲下一個頂點依次類推
            for (int i = index; i < numVertices; i++) {
                for (int x = 0; x < numVertices; x++)
                    adjMatrix[x][i] = adjMatrix[x][i + 1];
            }
            //數組中最後一個位置爲空,到各個頂點的邊爲空
            for (int i = 0; i < numVertices; i++) {
                adjMatrix[numVertices][i] = Double.POSITIVE_INFINITY;
                adjMatrix[i][numVertices] = Double.POSITIVE_INFINITY;
            }
            //個數-1,操做次數+1
            numVertices--;
            modCount++;
        }
    }
//輸入頂點添加邊
public void addEdge(T vertex1, T vertex2, double weight) {
        //根據頂點的索引值添加邊
        addEdge(getIndex(vertex1), getIndex(vertex2), weight);
    }

    public void addEdge(int index1, int index2, double weight) {
        //當起點終點的索引值都存在的時候,由於是無向圖,因此權重都相同,鄰接矩陣相應的位置放入權重。操做次數+1
        if (indexIsValid(index1) && indexIsValid(index2)) {
            adjMatrix[index1][index2] = weight;
            adjMatrix[index2][index1] = weight;
            modCount++;
        }
    }
//刪除邊
    public void removeEdge(T vertex1, T vertex2) {
        //根據頂點的索引值刪除邊
        removeEdge(getIndex(vertex1), getIndex(vertex2));
    }

    public void removeEdge(int index1, int index2) {
        //當起點終點的索引值都存在的時候
        if (indexIsValid(index1) && indexIsValid(index2)) {
            //把邊上的權重都變爲正無窮大
            adjMatrix[index1][index2] = Double.POSITIVE_INFINITY;
            adjMatrix[index2][index1] = Double.POSITIVE_INFINITY;
            modCount++;
        }
    }
  • 最小路徑的代碼比較長,因此一個方法一個方法的分析。
  • 下面這個方法輸出的是最小路徑的長度。
//找到兩個頂點間的最短路徑
    public int shortestPathLength(T startVertex, T targetVertex) {
        //根據索引值找到最短路徑長度
        return shortestPathLength(getIndex(startVertex), getIndex(targetVertex));
    }

    private int shortestPathLength(int startIndex, int targetIndex) {
        int result = 0;
        //若是起始索引值或者終止索引值不存在時,返回0
        if (!indexIsValid(startIndex) || !indexIsValid(targetIndex))
            return 0;

        int index1;

        //找到起點到終點最小路徑的頂點的索引值
        Iterator<Integer> it = iteratorShortestPathIndices(startIndex, targetIndex);

        //若是沒有下一個則爲0,沒路徑
        if (it.hasNext())
            index1 = ((Integer) it.next()).intValue();
        else
            return 0;

        //有下一個則+1,做爲長度
        while (it.hasNext()) {
            result++;
            it.next();
        }

        return result;
    }
  • 下面這個方法輸出的是最小路徑中的頂點元素。
//輸出最小路徑中的頂點
    public Iterator iteratorShortestPath(T startVertex, T targetVertex) {
        return iteratorShortestPath(getIndex(startVertex), getIndex(targetVertex));
    }
    public Iterator iteratorShortestPath(int startIndex, int targetIndex) {

        List<T> resultList = new ArrayList<T>();
        if (!indexIsValid(startIndex) || !indexIsValid(targetIndex))
            return resultList.iterator();

        //獲得最小路徑中的頂點索引值
        Iterator<Integer> it = iteratorShortestPathIndices(startIndex, targetIndex);
        //把對應的頂點添加到列表裏
        while (it.hasNext())
            resultList.add(vertices[((Integer) it.next()).intValue()]);
        return new GraphIterator(resultList.iterator());
    }
  • 下面這個方法是兩個方法中的基礎,找到最小路徑的中頂點對應的元素值(包括順序)
protected Iterator<Integer> iteratorShortestPathIndices(int startIndex, int targetIndex) {
        int index = startIndex;
        int[] pathLength = new int[numVertices];/*存路徑的長度*/
        int[] predecessor = new int[numVertices];/*前驅頂點*/
        QueueADT<Integer> traversalQueue = new LinkedQueue<Integer>();/*索引值*/
        UnorderedListADT<Integer> resultList = new ArrayUnorderedList<Integer>();/*頂點元素*/

        /*若是索引值不存在或者起始終止索引值相等,返回無序列表中的頂點元素*/
        if (!indexIsValid(startIndex) || !indexIsValid(targetIndex) || (startIndex == targetIndex))
            return resultList.iterator();

        /*作標記,先把標記數組所有變爲false*/
        boolean[] visited = new boolean[numVertices];
        for (int i = 0; i < numVertices; i++)
            visited[i] = false;
        /*把起始索引值加入列表*/
        traversalQueue.enqueue(Integer.valueOf(startIndex));
        /*對該索引值進行標記*/
        visited[startIndex] = true;
        /*該長度數組中該索引值的位置定位爲0,前驅結點定義爲-1*/
        pathLength[startIndex] = 0;
        predecessor[startIndex] = -1;

        /*當索引值列表不爲空而且起始和終止頂點元素不一樣時*/
        while (!traversalQueue.isEmpty() && (index != targetIndex)) {
            //index = 出隊列的元素(也就是以前存入的索引值)
            index = (traversalQueue.dequeue()).intValue();

            //找出該索引值到每一個頂點的邊
            for (int i = 0; i < numVertices; i++) {
                //若是邊存在,而且終點未標記(也就是這個邊沒有找到過)
                if (adjMatrix[index][i] < Double.POSITIVE_INFINITY && !visited[i]) {
                    /*把邊的終點的索引值對應的長度數組+1*/
                    pathLength[i] = pathLength[index] + 1;
                    /*前驅結點終點的索引值對應的位置存入index*/
                    predecessor[i] = index;
                    /*把新的索引值加入隊列*/
                    traversalQueue.enqueue(Integer.valueOf(i));
                    /*標記該頂點*/
                    visited[i] = true;
                }
            }

        }
        if (index != targetIndex)  // no path must have been found
            return resultList.iterator();
        
        StackADT<Integer> stack = new LinkedStack<Integer>();
        index = targetIndex;
        //找到最小路徑中的頂點索引值,進行入棧
        stack.push(Integer.valueOf(index));
        do {
            index = predecessor[index];
            stack.push(Integer.valueOf(index));
        } while (index != startIndex);
        //依次彈棧獲得正確的順序
        while (!stack.isEmpty())
            resultList.addToRear((stack.pop()));

        return new NetworkIndexIterator(resultList.iterator());
    }
  • 做爲無向加權圖的重要方法(最特殊的),由於有權重,因此存在一個找最便宜的方法
//找到最便宜的路
    public double shortestPathWeight(T vertex1, T vertex2) {
        return shortestPathWeight(getIndex(vertex1), getIndex(vertex2));
    }

    public double shortestPathWeight(int start, int end) {
        Double[] dist = new Double[numVertices];//存放該頂點對全部有邊頂點的權重
        boolean[] flag = new boolean[numVertices];//標記結點

        for (int i = 0; i < numVertices; i++) {
            flag[i] = false;/*讓全部都變成未標記*/
            dist[i] = adjMatrix[start][i];/*初始等於該頂點到全部頂點的權重(包括不存在邊的爲正無窮)*/
        }
        flag[start] = true;/*把開始頂點標記*/
        int k = 0;
        for (int i = 0; i < numVertices; i++) {
            Double min = Double.POSITIVE_INFINITY;
            for (int j = 0; j < numVertices; j++) {
                //若是爲標記,而且該索引值下存在權重(也就是存在邊)
                if (flag[j] == false && dist[j] < min && dist[j] != -1 && dist[j] != 0) {
                    min = dist[j];/*最小值就爲該權重*/
                    k = j;/*讓k等於該索引值*/
                }
            }
            flag[k] = true;/*標記該索引值的頂點*/
            /*找k索引值的剩下權重是否存在,若是有找到最小的添加到min裏,若是沒有一直+0*/
            for (int j = 0; j < numVertices; j++) {
                if (adjMatrix[k][j] != -1&&dist[j]!= -1) {
                    double temp = (adjMatrix[k][j] == Double.POSITIVE_INFINITY ? Double.POSITIVE_INFINITY : (min + adjMatrix[k][j]));
                    //若是j未標記而且temp小於權重組中原有值,則讓權重組j爲索引的位置爲temp
                    if (flag[j] == false && (temp < dist[j])) {
                        dist[j] = temp;
                    }
                }
            }
        }
        return dist[end];
    }

代碼託管

24540數組

上週考試錯題總結

點評過的同窗博客和代碼

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

  • 圖可謂是學了兩個學期java遇見的最難的東西了/微笑,並非概念難而是繞着繞着就把本身老繞暈了,還有就是可能理解了原理,可是代碼並不知道怎麼實現。

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 2/2
第二週 1010/1010 1/2 10/12
第三週 651/1661 1/3 13/25
第四周 2205/3866 1/4 15/40
第五週 967/4833 2/6 22/62
第六週 1680/6513 1/7 34/96
第七週 2196/8709 1/8 35/131
第八週 1952/10661 2/9 49/180
第九周 2100/24540 1/10 40/131
  • 計劃學習時間:30小時網絡

  • 實際學習時間:40小時數據結構

  • 改進狀況:圖的概念很好理解,可是實現很難!學習

參考資料

相關文章
相關標籤/搜索