《程序設計與數據結構》第九周學習總結

學號 20172326 《程序設計與數據結構》第九周學習總結

教材學習內容總結

  • 圖:圖(Graph)是一種複雜的非線性結構,在圖結構中,每一個元素均可以有零個或多個前驅,也能夠有零個或多個後繼,也就是說,元素之間的關係是任意的。與樹的區別在於樹中的一個結點只有一個前驅,也就是說只有一個父結點。但圖中的頂點(結點)是沒有這種限制關係的。
  • 無向圖:邊爲無需結點對的圖。也就是說,現有兩個頂點A、B.(A,B)與(B,A)效果相同。當兩個頂點之間有邊鏈接,也就是說這兩個頂點是鄰接的,也稱做鄰居。一個頂點用一個邊鏈接本身被稱爲,例如(A,A)。徹底圖是指無向圖有最大數目的聯通頂點,換句話說,每一個頂點之間都有一條邊鏈接。根據等差數列求和公式能夠求出一個徹底圖的邊數(通常來講,不考慮自循環的狀況)。路徑是指一個頂點到另一個頂點所須要的最少邊數,或是所途徑的頂點數-1。連通,每一個頂點之間均可以經過邊到達彼此,不必定兩個頂點之間必有直接連通的邊,間接的也能夠。環路是指從一個頂點開始,並最終以這個頂點爲終點,途徑某幾個或者所有頂點的路徑。
  • 有向圖:若是無向圖中的邊是一條雙向車道,那麼有向圖中的邊就是一條單行道,方向取決於定義。所以,看似每一個定點之間都有邊的有序圖不必定就是一個連通圖。
  • 拓撲序,書上沒有詳細講,出於好奇,我查了查它的原理。首先,肯定頂點中的無依賴頂點,也就是沒有頂點到該頂點的邊的頂點。將這些頂點存入一個隊列中去。並從圖中將其刪去。以此爲規則,逐步遞歸,最終將他們都存入隊列。
  • 廣度優先遍歷法:在必定程度上,與樹的層序遍歷法有必定的類似性。首先,肯定一個頂點開始遍歷,以後是該頂點路徑相同的的鄰頂點。每次優先處理頂點的鄰頂點。
  • (圖)html

  • 來看這張圖,從9開始。以路徑爲1的開始遍歷,那麼就是六、七、8這三個頂點,緊接着,根據優先頂點的鄰頂點原則,爲三、4,以後爲7的鄰頂點-5。8沒有鄰頂點,跳過,爾後又是一、2。至此,廣度遍歷完成。在書上的實現過程當中,前後使用了隊列保證先進先出的性質,以及將其加入列表中。
  • 深度優先遍歷法:選定一個頂點,遍歷其鄰頂點。可是首先將一個鄰頂點的所有鄰頂點遍歷完,再返回,進行下一個鄰頂點的遍歷。相似於樹中的前序遍歷法。
  • (無序圖)
  • 順序爲A -> C -> B -> D -> F -> G -> E。對於相同路徑的頂點來講,遍歷的前後由具體的代碼決定,其本質上是一致的。
  • (有向圖)
  • 與無序圖基本相似,但區別在於部分邊多是有向的,因此要對不能到達的頂點進行判斷。該圖的遍歷順序爲A -> B -> C -> E -> D -> F -> G
  • 連通性:只要全部的頂點之間是連通的,那麼全部的頂點元素在遍歷一遍後均可以獲得存儲。可是若是有未連通的頂點,頂點元素與存儲元素之間勢必不等。經過此來判斷是連通。
  • 最小生成樹:將一個加權圖變爲一棵樹,固然,樹也是圖的一種。最小生成樹的特色在於將其邊的權重總和小於等於同一個圖任何生成樹的權重總和。有兩種算法能夠用來實現最小生成樹
  • 普里姆算法(Prim算法):首先輸入一個頂點。根據權重大小來選擇小權的邊。不斷進行循環,直到將每一個頂點連通。注意,頂點之間邊的權重比較並非隨着鏈接新的頂點就只單獨地看該頂點的鄰頂點。始終以總體來看。哪一個頂點與其鄰頂點的邊的權小就選擇那條邊。
  • (圖)
    node

  • Kruskal(克魯斯卡爾)算法:與普利姆算法不一樣。首先尋找尋找一條權最小邊,而後是第二條、第三條。在此過程當中,可使邊與邊之間連起來,而且最終都要鏈接起來。可是,不能出現迴路。例如,目前有一條邊是當前未鏈接最小的邊。但若是鏈接這條邊。這會使得最小生成樹出現迴路。那麼將不被容許,此時就要放棄這條邊。
  • 鄰接列表:用鏈表來存儲每一個結點之間的邊。對於無向圖,由於邊存在於兩個頂點之間。因此會存在兩個結點均存儲了相同邊。
  • 鄰接矩陣:使用布爾數組來實現存儲邊。將存在邊的位置記錄爲true,無位置的記錄爲false。對於一個無向圖,其存儲結果是一個對稱矩陣。可是對於有向圖來講,由於邊的單向性,結果將不一樣。git

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

  • 問題1:廣度遍歷法與深度遍歷法經過什麼實現了其各自特色要求的遍歷?
  • 問題1理解:首先打眼一看,彷佛兩個方法沒什麼區別。BFS至關因而從第一個頂點開始遍歷完其全部的鄰頂點的鄰頂點再進行下一個的遍歷。DFS相似於前序遍歷。 經過對課本代碼的分析。找出了幾個關鍵部分代碼。第一點,使用了不一樣的數據結構對其進行存儲。BFS使用了隊列的方式。DFS使用了棧進行存儲。下面就詳細的進行分析。先看BFS的代碼。算法

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;
                }
            }
        }

首先,先聲明,書上的方法均是是基於鄰接矩陣實現的。具體來說也就是兩個布爾數組來肯定頂點與頂底之間是否有邊。將頂點進入隊列後,再取出放入列表之中。而後根據鄰接矩陣來判斷邊,也就是找出全部的鄰接頂點。依次存入隊列之中。當找到全部的鄰接頂點後。將第一個進入隊列的頂底取出,找出其全部的鄰接頂點。以後再進行下一個頂點的鄰接頂點的查詢。經過隊列先進先出的順序,將全部頂點按照相似於樹的先序遍歷的方法儲存起來。那麼,如何防止出現重複的結點呢?創建的boolean型的visited數組就能夠解決這個問題,初始化時全部結點都是false也就是未被訪問。當訪問到一個頂點後,將其標記爲true。當下一次再訪問到這個頂點後。就進行跳過。編程

  • DFS代碼,
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();
        }

衆所周知,棧是後進先出的。那麼,若是以頂點V開始,它的第一個鄰頂點爲U。那麼它入棧,緊接着尋找U的第一個鄰頂點,而後將其入棧。每次使用peek方法獲取當前棧頂元素,也就是上一個頂點的第一個鄰頂點。當找不到時,將其彈出。就比如一棵樹,當把這一棵子樹的全部結點找完後,就去下一棵子樹找。同理,循環會將已經都訪問過的頂點彈出棧,直到找到沒有訪問過鄰頂點的頂點。按照這個順序,將頂點按照深度優先的順序存入列表中。數組

  • 問題2:最短路徑的實現問題
  • 問題2理解:實現最短路徑,這裏主要指的是無向圖。首先,咱們分別使用兩個數組,一個用於存放不一樣的索引的深度,一個存放前驅結點。
int[] pathLength = new int[numVertices];
int[] predecessor = new int[numVertices];

以後根據放入的順序進行遍歷,若是與頂點有邊,那麼,將其存入,以後用隊列,將其存儲。若是隻是須要具體的數字,咱們直接將其對應的索引對應的數字返回便可。數據結構

while (!traversalQueue.isEmpty() && (index != targetIndex))
        {
            index = (traversalQueue.dequeue()).intValue();

            //Update the pathLength for each unvisited vertex adjacent 
            //     to the vertex at the current index. 
            for (int i = 0; i < numVertices; i++)
            {
                if (adjMatrix[index][i] && !visited[i])//
                {
                    pathLength[i] = pathLength[index] + 1;
                    predecessor[i] = index;
                    traversalQueue.enqueue(new Integer(i));
                    visited[i] = true;
                }
            }
        }

可是,咱們不妨再將其順序返回,使得其更加直觀。經過以前對部分方法的分析咱們不難發現,對於這種非線性數據結構,有時使用隊列來達到層序遍歷,而使用棧達到前序遍歷(後序遍歷)這時,咱們就用棧(後進先出)來實現。ide

StackADT<Integer> stack = new LinkedStack<Integer>();
        index = targetIndex;
        stack.push(new Integer(index));
        do
        {
            index = predecessor[index];
            stack.push(new Integer(index));
        } while (index != startIndex);
        
        while (!stack.isEmpty())
            resultList.addToRear(((Integer)stack.pop()));
  • 同時,還出現了一種新的問題,也就是,從代碼當中,咱們沒法找到體現比較的過程。可是依舊能夠實現返回最短路徑的深度。緣由何在?
  • 其實,剛剛已經提到,使用了隊列的方法,從而實現了層序遍歷的方法,這樣一來,避免了那些曲裏拐彎的路徑,因此,不須要比較,就能夠實現。

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

  • 問題1:實現pp15_7時如何解決權重的問題
  • 問題1解決方案:以前介紹過了prim算法。同時,還有一種迪傑斯特拉算法(Dijkstra)所以,在此介紹一下。選擇一個初始頂點,存入一個dist數組中,而後,挨個遍歷,和頂點有邊的存入其權重。本身的位置爲0,沒有邊的位置存的是無窮大。以後,對沒有直接鏈接的進行處理。從初頂點到其可能有多條路徑,對應多種選擇,對應多種不一樣的權重和,通過選擇最小的權重。放至那個位置。
  • 在熟悉了這個方法後,開始編程。首先,把全部的權重放入dist數組中,對於沒有直接鏈接的位置,定義爲無窮大,這裏怎樣定義無窮大呢?
public static final double POSITIVE_INFINITY = 1.0 / 0.0;

就是無窮大了,它被定義於Double中,能夠直接調用。而後,首先對目前已有的「非0(不是它本身)非-1(二者之間有邊)。在這個基礎上,選出最小的值,存入列表之中。以後,用最小值的索引值來繼續向下遍歷。換句話說,對於dist數組中存取數據爲無窮大的(也就是計算出未直接與初頂點相連的間接權重)學習

for (int i = 0; i < numVertices; i++) {
            flag[i] = false;
            dist[i] = adjMatrix[start][i];
        }
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就是最小權的索引值。每次都選擇最小的權重邊做爲路徑。而後重複這個過程。最終,整個disc數組的每一個索引對應的就是從初頂點到該對應索引的最短權重路徑。咱們根據須要,取出便可。編碼

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]));
                    if (flag[j] == false && (temp < dist[j])) {
                        dist[j] = temp;
                    }
                }
            }
        }
        return dist[end];
  • 問題二:T和Objiect的區別
  • 解決過程:,按理來講,這二者彷佛沒什麼區別。在上學期的學習中,有一個定義讓我印象深入,就是Object類是全部類的父類。因此Object範圍很是廣,而T從一開始就會限定這個類型(包括它能夠限定類型爲Object)。
    Object因爲它是全部類的父類,因此會強制類型轉換,而T從一開始在編碼時(在寫代碼時)就限定了某種具體類型,因此它不用強制類型轉換。因此使用起來就更加方便。

代碼託管

上週考試錯題總結

  • 錯題1及緣由,理解狀況
  • Though not a queue at all, a minheap provides an efficient implementation of a _____________.
  • 堆是一種樹,根據定義對數據進行存儲,可是雖然它不是一種線性數據結構,但能夠做爲與其相似的定義高效率的完成相似隊列 的工做。
  • 錯題2及緣由,理解狀況
  • In an array implementation of a binary tree, the root of the tree is in position 0, and for each node n, n’s left child is in position ________ and n’s right child is in position ________.
  • 數組實現二叉樹,其左孩子永遠在2n + 1處,右孩子在2(n + 1)處。
  • 錯題3及緣由,理解狀況
  • The removeMin operation for both the linked and array implementations is
  • 用數組或者隊列實現最小堆的效率logn。
  • 錯題4及緣由,理解狀況
  • Since a heap is a binary search tree, there is only one correct location for the insertion of a new node, and that is either the next open position from the left at level h or the first position on the left at level h+1 if level h is full.
  • 對於堆,插入元素時,爲了知足其徹底樹的定義,當一層未滿時,優先將元素放至左孩子位置,如左孩子填滿,則放至右孩子處。若是一層滿。則放在下一層的第一個位置。

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

  • 圖的狀況有不少種,有向和無向,有權重和無權重,遍歷方法也比較難懂,須要繼續學習

    結對及互評

  • 博客中值得學習的或問題:
    排版精美,對於問題研究得很細緻,解答也很周全。
  • 代碼中值得學習的或問題:
    代碼寫的很規範,思路很清晰,繼續加油!

點評過的同窗博客和代碼

  • 本週結對學習狀況
  • 20172313
  • 20172332

    結對學習內容

  • 第十五章 圖

學習進度條

代碼行數(新增/累積) 博客量(新增/累積) 學習時間(新增/累積) 重要成長
目標 5000行 30篇 400小時
第一週 0/0 1/1 3/3
第二週 409/409 1/2 5/8
第三週 1174/1583 1/3 10/18
第四周 1843/3426 2/5 10/28
第五週 539/3965 2/7 20/48
第六週 965/4936 1/8 20/68
第七週 766/5702 1/9 20/88
第八週 1562/7264 2/11 20/108
第九周 2016/9280 1/12 20/128

參考資料

相關文章
相關標籤/搜索