浙大數據結構MOOC 陳越/何欽銘 筆記整理(梳理知識點 查漏補缺_)

第三至五講 樹
node

1.掌握查找的概念。c++

   Q1:靜態查找和動態查找的區別是?算法

   靜態查找中,集合的記錄是固定的;而動態查找中記錄是動態變化的,除了查找,還可能發生插入和刪除。數組

2.掌握技巧"哨兵"的設置。數據結構

   Q1:哨兵的做用?ide

   用於順序表查找,所謂「哨兵」即用一個特殊值來做爲數組的邊界,能夠減小一條判斷語句(i<n),提升程序的效率。函數

3.掌握二分查找。性能

   Q1:二分查找能夠應用的前提?測試

  ①用於查找的數據元素的關鍵字知足有序ui

  ②而且連續存放(必須是數組,鏈表不能夠)

   Q2:二分查找的算法和時間複雜度?

    二分查找的時間複雜度爲O(log2N)

 1 int BinarySearch(StaticTable*Tbl,ElementType K)//在靜態表Tbl中查找關鍵字爲K的數據元素 
 2 {
 3     int left,right,mid,NoFound=-1;
 4     left=1;//初始左邊界 
 5     right=Tbl->Length;//初始右邊界 
 6     while(left<=right)
 7     {
 8         mid=(left+right)/2;
 9         if(K<Tbl->Element[mid])
10         right=mid-1;//調整右邊界 
11         else if(K>Tbl->Element[mid])
12         left=mid+1;//調整左邊界 
13         else return mid;//查找成功 
14     }
15     return NotFound;//查找不成功,返回-1 
16 }
BinarySearch

   Q3:二分查找對數據的存儲有什麼啓示?

   若是數據按照如圖所示的方式存儲,能夠達到相同的查找效果。

 4.掌握樹的概念。

    Q1:二叉樹的性質及其證實?

    對於任何非空二叉樹T,若n0表示葉結點的個數、n2是度爲2的非葉結點的個數,那麼二者知足關係n0=n2+1,證實略。(這條性質很是不熟悉!)

    應用該性質的一道課後題:

    如有一二叉樹的總結點數爲98,只有一個兒子的結點數爲48,則該樹的葉結點數是多少?

    這樣的樹不存在。總結點數=n0+n1+n2=98,已知n1=48,n2=n0-1,,代入得n0=51/2,故這樣的樹不存在。

    其他性質略。

    Q2: 課後思考題:一棵度爲 m的樹有n個節點。若每一個節點直接用m個鏈指向相應的兒子,則表示這個樹所須要的總空間是n*(m+1) ,(假定每一個鏈以及表示節點的數據域都是一個單位空間).。當採用兒子/兄弟(First Child/Next Sibling)表示法時,所需的總空間是

由於每一個節點都得須要m個鏈來指向相應的兒子,再加上一共有n個節點,故一共有n*(m+1)個子節點。現考慮兒子/兄弟表示法,將n個節點串聯,由於每一個節點都由兩個部分組成,一個是指向下一個兄弟節  點,一個是指向子節點,因此在樹中,這兩個部分都是須要做爲一個單位(即鏈)來存儲的,加上n個節點自己,一共須要3n個空間。

5.掌握樹的存儲方式。

6.  掌握樹的遍歷方法(遞歸、非遞歸遍歷)

    二叉樹遍歷的核心問題是 二維結構的線性化,須要一個存儲結構保暫時不訪問的結點。

   Q1: 樹的非遞歸遍歷如何實現?

   非遞歸算法實現的基本思路:使用堆棧。

   以中序遍歷爲例:

   算法思想:①遇到一個結點,將其壓入棧中,並遍歷它的左子樹。

                      ②當左子樹遍歷結束後,從棧頂彈出這個節點並訪問它

                      ③按照右指針中序遍歷該結點的右子樹。

 

void InOrderTraversal(BinTree BT)
{
    BinTree T=BT;
    Stack S=CreatStack(MaxSize);//建立並初始化堆棧 
    while(T||!IsEmpty(S))
    {
        while(T)
        {//一直向左並將沿途結點壓入堆棧 
            Push(S,T);
            T=T->Left;
        }
        if(!IsEmpty(S))
        {
            T=Pop(S);//結點彈出堆棧 
            printf("%5d",T->Data);//訪問打印結點 
            T=T->Right;//轉向右子樹 
        }
    }
 } 中序
中序非遞歸遍歷

    Q2:如何實現樹的層序遍歷?

    利用隊列實現。

    Step 1:先將根結點入隊。

    Step 2:從隊列中取出一個元素。

    Step 3:訪問該元素所指的結點。

    Step 4:若該元素所指結點的左、右孩子結點非空,則將其左、右孩子的指針順序入隊。

 1 void LevelOrderTraversal(BinTree BT)
 2 {
 3     Queue Q;BinTree T;
 4     if(!BT)return;
 5     Q=CreateQueue(MaxSize);
 6     AddQ(Q,BT);
 7     while(!IsEmptyQ(Q))
 8     {
 9         T=DeleteQ(Q);
10         printf("%d\n",T->data);
11         if(T->Left) AddQ(Q,T->Left);
12         if(T->Right) AddQ(Q,T->Right);
13     }
14     
15 }
算法

    Q3:  如何求二叉樹的高度?

 1 int PostOrderGetHeight(BinTree BT)
 2 {
 3     int HL,HR,MaxH;
 4     if(BT)
 5     {
 6         HL=PostOrderGetHeight(BT->Left);
 7         HR=PostOrderGetHeight(BT->Right);
 8         maxH=(HL>HR)?HL:HR;
 9         return(MaxH+1);
10     }
11     else
12     return 0;
13  } 
算法

    Q4:如何根據二元表達式樹遍歷獲得算式?

    經過三種順序遍歷獲得三種表達式。

    注意:中綴表達式會受到運算符優先級的影響。

    Q5:若是由兩種遍歷序列肯定一棵二叉樹,那麼必需要有的遍歷?

    中序遍歷。

    Q6:課後思考題:假定只有四個結點A、B、C、D的二叉樹,其前序遍歷序列爲ABCD,則下面哪一個序列是不可能的中序遍歷序列?

  • A. ABCD

  • B. ACDB

  • C. DCBA

  • D. DABC

 

      注意解題方法,容易思路混亂。逐一分析:

     A

 

     B

     C

 

 7.掌握二叉搜索樹及其相關操做。

    Q1:請給出二叉搜索樹的定義?

     二叉搜索樹又稱二叉排序樹或者二叉查找樹,能夠爲空,若是不爲空,它知足如下條件:

     ①非空左子樹的全部鍵值小於其根結點的鍵值;

     ②非空右子樹的全部鍵值大於其根結點的鍵值;

     ③左右子樹都是二叉搜索樹。

     Q2:如何進行樹的查找操做?如何改進算法使效率更高?

   

 1 Position Find(ElementType X,BinTree BST)
 2 {
 3     if(!BST)return NULL;
 4     if(X>BST->Data)
 5     return Find(X,BST->Right);
 6     else if(X<BST->Data)
 7     return Find(X,BST->Left);
 8     else
 9     return BST;
10 }
算法

   上述算法採用尾遞歸的方法(尾遞歸:即指在函數返回時用到遞歸)由於非遞歸函數的執行效率更高,所以將尾遞歸改成迭代函數。

 1 Position IterFind(ElementType X,BinTree BST)
 2 {
 3     while(BST)
 4     {
 5     if(X>BST->Data)
 6     BST=BST->Right;
 7     else if(X<BST->Data)
 8     BST=BST->Left;
 9     else
10     return BST;
11    }
12    return NULL;
13 }
算法

     Q3:如何查找最大和最小元素?

     基本原則:最大元素必定在樹的最右分枝的端節點上;最小元素必定在樹的最左分枝的端節點上。

 1 Position FindMin(BinTree BST)
 2 {
 3     if(!BST)return NULL;
 4     else if(!BST->Left)
 5     return BST;
 6     else
 7     return FindMin(BST->Left);
 8 }
 9 Position FindMax(BinTree BST)
10 {
11     if(BST)
12     while(BST->Right) BST=BST->Right;
13     return BST;
14 }
算法

     Q4:如何進行二叉搜索樹的插入?

 1 BinTree Insert(ElementType X,BinTree BST)
 2 {
 3     if(!BST)//若原樹爲空,生成並返回一個結點的二叉搜索樹 
 4     {
 5         BST=malloc(sizeof(struct TreeNode));
 6         BST->Data=X;
 7         BST->Left=BST->Right=NULL;
 8     }
 9     else
10     if(X<BST->Data) 
11     BST->Left=Insert(X,BST->Left);
12     else if(X>BST->Data)
13     BST->Right=Insert(X,BST->Right);
14     return BST;    
15     
16 }
算法

     Q5:如何進行二叉搜索樹的刪除?

     思路:分三種狀況考慮:

               ①要刪除的是葉結點:直接刪除,而後將其父結點所指的指針置爲NULL

               ②要刪除的是隻有一個孩子的結點:將其父結點的指針指向要刪除結點的孩子結點。

               ③要刪除的是有左右兩子樹的結點:用另外一結點(取右子樹中的最小元素或者左子樹中的最大元素)替代被刪除的結點。

 1 BinTree Delete(ElementType X,BinTree BST)//本算法由兩部分組成,一是遞歸,二是上述討論的刪除操做 
 2 {
 3    Position Tmp;
 4    if(!BST) printf("要刪除的元素未找到");
 5    else if(X<BST->Data)
 6    BST->Left=Delete(X,BST->Left);//左子樹遞歸刪除 
 7    else if(X>BST->Data)
 8    BST->Right=Delete(X,BST->Right);//右子樹遞歸刪除 
 9    else//找到要刪除的結點 
10    if(BST->Left&&BST->Right)//被刪除結點有左右兩個子結點 
11    {
12        Tmp=FindMin(BST->Right);//在右子樹中找最小的元素填充刪除結點 
13        BST->Data=Tmp->Data;
14        BST->Right=Delete(BST->Data,BST->Right);//在刪除結點的右子樹中刪除最小元素 
15    }
16    else
17    {//被刪除結點有一個或無子結點 
18        Tmp=BST;
19        if(!BST->Left)//有右孩子或無子結點 
20        {
21            BST=BST->Right;
22        }
23        else if(!BST->Right)//有左孩子或無子結點 
24             BST=BST->Left;
25     free(Tmp);
26        }
27        return BST;
28 }
算法

 

8.掌握平衡二叉樹及其相關操做。

   Q1:請給出平衡因子以及平衡二叉樹的定義?

   平衡因子(Balance Factor,簡稱BF)BF(T)=hL-hR,hL和hR分別爲T左右兩棵子樹的高度。

   平衡二叉樹:空樹或者任一結點左右子樹高度差的絕對值不超過1,即|BF(T)|≤1

   Q2:有關平衡二叉樹結點及高度的結論?

   

          

9.掌握堆。

    Q1:爲何要定義堆這一數據結構?

   優先隊列是一種特殊的隊列,其取出元素的順序是按照元素優先權(關鍵字)的大小,而不是元素進入隊列的前後順序。比較多種數據結構後,決定採用二叉樹結構做爲優先隊列的存儲方式,而且考慮樹的結構和樹的結點存儲順序:將最大關鍵字存儲在樹的根結點,爲了保證在插入和刪除元素時樹能保持平衡,採用徹底二叉樹的結構。

    Q2:堆的兩個特性?

    結構性:用數組表示的徹底二叉樹

    有序性:任意結點的關鍵字是其子樹全部結點的最大值(最小值)。最大堆又稱大頂堆,最大值;最小堆又稱小頂堆,最小值。(注:從根結點到任意結點路徑上結點序列都存在有序性)

    Q3:堆的數據結構及相關操做?

 1 typedef struct HeapStruct *MaxHeap;
 2 struct HeapStruct
 3 {
 4     ElementType *Elements;//存儲堆元素的數組 
 5     int Size;//堆的當前元素個數 
 6     int Capacity;//堆的最大容量 
 7 };
 8 MaxHeap Create(int MaxSize)
 9 {//建立容量爲MaxSize的空的最大堆 
10     MaxHeap H=malloc(sizeof(struct HeapStruct));
11     H->Elements=malloc((MaxSize+1)*sizeof(ElementType));
12     H->Size=0;
13     H->Capacity=MaxSize;
14     H->Elements[0]=MaxData;//定義「哨兵」 爲大於堆中全部可能元素的值,便於之後更快操做 
15     return H;
16  } 
堆的建立
void Insert(MaxHeap H,ElementType item)
{ //將元素item插入最大堆H,其中H->Elements[0]已經定義爲哨兵 
  int i;
  if(IsFull(H))
  {
      printf("最大堆已滿");
      return; 
  }
  i=++H->Size;//i指向插入後堆中的最後一個元素的位置 
  for(;H->Element[i/2]<item;i/=2)
      H->Elements[i]=H->Elements[i/2];//向根部過濾結點 
      H->Elements[i]=item;//將item插入 
      //H->Element[0]是哨兵元素,它不小於堆中的最大元素,控制循環結束 
}
最大堆的插入
 1 ElementType DeleteMax (MaxHeap H)
 2 //從最大堆中取出鍵值爲最大的元素並刪除一個結點 
 3 {
 4     int Parent,Child;
 5     ElementType MaxItem,temp;
 6     if(IsEmpty(H))
 7     {
 8         printf("最大堆已爲空");
 9     }
10     MaxItem=H->Elements[1];//取出根結點最大值 
11     //用最大堆中最後一個元素從根結點開始向上過濾下層結點 
12     temp=H->Elements[H->Size--];//將最大堆中最後一個元素存放在temp中,由於刪除了根結點,因此H->Size自減1 
13     for(Parent=1;Parent*2<=H->Size;Parent=Child)
14     {//從根結點的位置開始循環
15     //Parent*2<=H->Size是爲了判斷是否存在左孩子(根節點編號爲1時,結點i的左右孩子分別爲:2i和2i+1,若是不存在左孩子天然也不存在右孩子,循環就能夠結束了)
16     //Parent=Child向下繼續循環直到找到合適的位置 
17         Child=Parent*2;//指針指向左孩子 
18         if((Child))!=H->Size)&&(H->Elements[Child]<H->Elements[Child+1])//Child!=H->Size是左右孩子都存在的狀況 
19         Child++;//本段語句用來將指針指向左右孩子中最大的那個
20         if(temp>=H->Elements[Child])break;//若是temp的值大於等於左右孩子中最大的那個值,說明這裏就是temp最合適的位置,循環結束 
21         else
22         H->Elements[Parent]=H->Elements[Child];//繼續向下尋找合適的位置 
23         }
24         H->Elements[Parent]=temp;//將temp的值放在合適的位置上 
25         return MaxItem;//返回最大值 
26 } 
最大堆的刪除

    Q4:如何創建最大堆:將已經存在的N個元素按照最大堆的要求存放在一個一維數組中?

    Step  1:將N個元素按輸入順序存入,先知足徹底二叉樹的結構特性。

    Step  2:調整各結點的位置,以知足最大堆的有序特性。

    Q5:課後錯題:

     建堆時,最壞狀況下須要挪動元素次數是等於樹中各結點的高度和。問:對於元素個數爲12的堆,其各結點的高度之和是多少?

     該題即求最壞狀況下須要下沉的結點的挪動次數。最壞狀況就是最小堆調整爲最大堆:倒數第二層向下移動一次,倒數第三層向下移動兩次,倒數第四層向下移動三次。即3*1+2*2+1*3=10

    

 

 

10.掌握哈夫曼樹與哈夫曼編碼。

     Q1:請給出哈夫曼樹的定義?

     帶權路徑長度(WPL):設二叉樹有n個葉子結點,每一個葉子結點帶有權值wk,從根結點到每一個葉子結點的長度爲Ik,則每一個葉子結點的帶權路徑長度之和就是:WPL=ΣwkIk

     而最優二叉樹(即哈夫曼樹)就是WPL最小的樹

     Q2:哈夫曼樹如何構造?

     每次將權值最小的兩棵樹合併。而如何選取最小,能夠用堆解決問題。

哈夫曼樹的創建

     Q3:請給出哈夫曼樹的特色?

     ①沒有度爲1的結點 

     ②n個葉子結點的哈夫曼樹共有2n-1個結點(推導過程:n0:葉結點數;n1:只有一個兒子的結點總數;n2:有兩個兒子的結點總數;n2=n0-1)。

     ③哈夫曼樹的任意非葉結點的左右子樹交換後仍然爲哈夫曼樹。

     Q4:課後錯題:

     1.爲五個使用頻率不一樣的字符設計哈夫曼編碼,下列方案中哪一個不多是哈夫曼編碼?A.00,100,101,110,111    B.000,001,01,10,11      C.0000,0001,001,01,1   D.000,001,010,011,1

        A畫出來如圖所示,不符合哈夫曼樹的性質:沒有度爲1的結點。

     2.一段文本中包含對象{a,b,c,d,e},其出現次數相應爲{3,2,4,2,1},則通過哈夫曼編碼後,該文本所佔總位數爲:

        文本所佔總位數=3X1+3X2+2X2+2X3+2X4=27(最後一步算錯了,忘記乘以頻率)

11.掌握集合及運算。(期中測試中該知識點出錯)

     Q1:什麼是並查集?

     並查集是數據結構之一,主要用於解決一些元素分組的問題。它管理一系列不相交的集合,並支持兩種操做:合併(Union):把兩個不相交的集合合併爲一個集合;查詢(Find):查詢兩個元素是否在同一個集合中。並查集的重要思想在於,用集合中的一個元素表明集合。

     Q2:並查集中集合存儲如何實現?

    

     Q3:請寫出集合運算的算法?

    

 

 

 

 

 

 

    

 

 

 

第六講 圖

1.重點介紹圖的鄰接矩陣存儲。

    Q1:對無向圖的鄰接矩陣如何存儲才能節省空間?

  

      Q2:鄰接矩陣表示的優勢是?

      ① 簡單直觀    ②便於找出任一頂點全部的「鄰接點」

      ③便於檢查任意頂點之間是否存在邊

      ④便於計算任一頂點的度(出度和入度都很明確)【注意:有向圖的出度是對應行的非零元素的個數,入度是對應列的非零元素的個數】

      Q3:鄰接矩陣表示的缺點是?

      ①浪費空間,存稀疏圖時有大量無效元素

      ②浪費時間,如想要統計圖中共有多少條邊

2.重點介紹圖的鄰接表存儲。

      Q1:用簡練的語言描述一下鄰接表存儲如何實現?

      G[N]爲指針數組,對應矩陣每行一個鏈表,用來存儲非零元素。

      Q2:鄰接表表示的優勢是?

      ①便於找到任意頂點的全部「鄰接點」。

      ②節約稀疏圖的空間。須要N個頭指針+2E個結點(每一個結點至少兩個域)。

      Q3:鄰接表表示的缺點是?

      ①對於無向圖來講易計算度,而對於有向圖來講只能計算出度,還須要逆鄰接表計算入度。

      ②不便於檢查任意兩個頂點間是否存在邊。

3.重點掌握兩種表示方法的C語言實現

 

 1 /* 圖的鄰接矩陣表示法(C語言實現) */
 2 #define  MaxVertexNum  100      /* 最大頂點數設爲100 */
 3 #define  INFINITY  65535     /* ∞設爲雙字節無符號整數的最大值65535*/
 4 typedef  char  VertexType;      /* 頂點類型設爲字符型 */
 5 typedef  int  EdgeType;         /* 邊的權值設爲整型 */
 6 enum GraphType { DG, UG, DN, UN };  7 /* 有向圖,無向圖,有向網圖,無向網圖*/
 8   
 9 typedef  struct { 10     VertexType  Vertices[ MaxVertexNum ];  /* 頂點表 */
11  EdgeType Edges[ MaxVertexNum ][ MaxVertexNum ]; 12 /* 鄰接矩陣,即邊表 */
13     int  n, e;   /* 頂點數n和邊數e */
14     enum GraphType GType;   /* 圖的類型分4種:UG、DG、UN、DN */
15 } MGraph;    /* MGragh是以鄰接矩陣存儲的圖類型 */
16   
17 void  CreateMGraph ( MGraph *G ) 18 { 19     int i, j, k, w; 20     G-> GType = UN;    /* Undirected Network 無向網圖 */
21     printf( "請輸入頂點數和邊數(輸入格式爲:頂點數, 邊數):\n" ); 22     scanf( "%d, %d",&(G->n), &(G->e) ); /* 輸入頂點數和邊數 */
23     printf("請輸入頂點信息(輸入格式爲:頂點號<CR>):\n"); 24     for ( i = 0; i < G->n; i++ ) 25        scanf( "%c",&(G-> Vertices[i]) ); /* 輸入頂點信息,創建頂點表 */
26     for ( i = 0; i < G->n; i++ ) 27        for ( j = 0; j < G->n; j++ ) 28            G->Edges[i][j] = INFINITY; /* 初始化鄰接矩陣 */
29     printf( "請輸入每條邊對應的兩個頂點的序號和權值,輸入格式爲:i, j, w:\n" ); 30     for ( k = 0; k < G->e; k++ ) { 31        scanf("%d,%d,%d ",&i, &j, &w); /* 輸入e條邊上的權,創建鄰接矩陣 */
32        G->Edges[i][j] = w; 33        G->Edges[j][i] = w; /* 由於無向網圖的鄰接矩陣是對稱的 */
34  } 35 }
圖的鄰接矩陣表示法

 

 1 /* 圖的鄰接表表示法(C語言實現) */
 2 #define  MaxVertexNum  100     /* 最大頂點數爲100 */
 3 enum GraphType { DG, UG, DN, UN }; 
 4 /* 有向圖,無向圖,有向網圖,無向網圖*/
 5 typedef  struct  node{   /* 邊表結點 */
 6     int AdjV;            /* 鄰接點域 */
 7     struct  node  *Next;  /* 指向下一個鄰接點的指針域 */
 8     /* 若要表示邊上的權值信息,則應增長一個數據域Weight */
 9 } EdgeNode;
10 typedef  char  VertexType;   /* 頂點用字符表示 */
11 typedef  struct  Vnode{      /* 頂點表結點 */
12     VertexType  Vertex;      /* 頂點域 */
13     EdgeNode  *FirstEdge; /* 邊表頭指針 */
14 } VertexNode; 
15 typedef VertexNode AdjList[ MaxVertexNum ]; /* AdjList是鄰接表類型 */
16 typedef  struct{  
17     AdjList  adjlist;    /* 鄰接表 */
18     int  n, e;               /* 頂點數和邊數 */
19     enum GraphType GType;    /* 圖的類型分4種:UG、DG、UN、DN */
20 } ALGraph;  /*ALGraph是以鄰接表方式存儲的圖類型 */
21   
22 void CreateALGraph( ALGraph *G )
23 {
24     int i, j, k;
25     EdgeNode *edge;
26     G-> GType = DG;  /* Directed Graph  有向圖  */
27     printf( "請輸入頂點數和邊數(輸入格式爲:頂點數,邊數):\n" );
28     scanf( "%d,%d", &(G->n), &(G->e) ); /* 讀入頂點數和邊數 */ 
29     printf( "請輸入頂點信息(輸入格式爲:頂點號<CR>):\n" );
30     for ( i=0; i < G->n; i++ ) {   /* 創建有n個頂點的頂點表 */
31         scanf( " %c", &(G->adjlist[i].Vertex) );  /* 讀入頂點信息 */
32        G->adjlist[i].FirstEdge = NULL; /* 頂點的邊表頭指針設爲空 */
33     }
34     printf( "請輸入邊的信息(輸入格式爲: i, j <CR>):\n" );
35     for ( k=0; k < G->e; k++ ){   /* 創建邊表 */
36        scanf( "\n%d,%d", &i, &j); /* 讀入邊<vi,vj>的頂點對應序號*/
37        edge = (EdgeNode*)malloc(sizeof(EdgeNode)); /* 生成新邊結點edge */
38        edge->AdjV = j; /* 鄰接點序號爲j */
39        edge->Next = G->adjlist[i].FirstEdge;
40        /* 將新邊表結點edge插入到頂點vi的邊表頭部 */
41        G->adjlist[i].FirstEdge = edge;
42        /* 如果無向圖,還要生成一個結點,用來表示邊< vj, vi>  */
43     }
44 }圖的
圖的鄰接表表示法

 

4.重點掌握DFS.BFS

    Q1:DFS的時間複雜度?

    ①採用鄰接表存儲:O(N+E)

    ②採用鄰接矩陣存儲:O(N2

    Q2:BFS的算法(c++函數實現)

   

 1 void BFS(Graph G,int v) 
 2 {
 3     cout<<v;
 4     visited[v]=true;//訪問第v個頂點 
 5     InitQueue(Q);//輔助隊列Q初始化,置空 
 6     EnQueue(Q,v);//v進隊 
 7     while(!QueueEmpty(Q))//隊列非空 
 8     {
 9         DeQueue(Q,u);//隊頭元素出隊並置爲u 
10         for(w=FirstAdjVex(G,u);w>=0;w=NextAdjVex(G,u,w))
11         if(!visited[w])//w爲u的還沒有訪問的鄰接頂點 
12         {
13             cout<<w;
14             visited[w]=true;
15             EnQueue(Q,w);//w進隊 
16         }
17     }
18 }
BFS

   Q3:DFS算法和BFS算法(C語言實現)

 

 1 /* 鄰接表存儲的圖 – DFS(C語言實現) */
 2 /* Visited[]爲全局變量,已經初始化爲FALSE */
 3 void  DFS( ALGraph *G,  int i )
 4 {   /* 以Vi爲出發點對鄰接表存儲的圖G進行DFS搜索 */
 5     EdgeNode *W;
 6     printf( "visit vertex: %c\n", G->adjlist[i].Vertex );
 7     /* 至關於訪問頂點Vi */
 8     Visited[i] = TRUE;   /* 標記Vi已訪問 */
 9     for( W = G->adjlist[i].FirstEdge;  W;  W = W->Next ) 
10        if ( !Visited[ W->AdjV ] )
11            DFS( G, W->AdjV );
12 }
13 
14 /* 鄰接矩陣存儲的圖 – BFS(C語言實現) */
15 void  BFS ( MGraph G )
16 {   /* 按廣度優先遍歷圖G。使用輔助隊列Q和訪問標誌數組Visited */
17     Queue  *Q;    
18     VertexType  U, V, W;
19     for ( U = 0; U < G.n; ++U )  
20        Visited[U] = FALSE;
21     Q = CreatQueue( MaxSize ); /* 建立空隊列Q */
22     for ( U = 0; U<G.n; ++U )
23        if ( !Visited[U] ) { /* 若U還沒有訪問 */
24            Visited[U] = TRUE; 
25            printf( "visit vertex: %c\n", G.Vertices[U] );
26            /* 至關於訪問頂點U */
27            AddQ (Q, U);    /* U入隊列 */
28            while ( ! IsEmptyQ(Q) ) {
29               V = DeleteQ( Q );  /*  隊頭元素出隊並置爲V */
30               for( W = FirstAdjV(G, V);  W;  W = NextAdjV(G, V, W) )
31                   if ( !Visited[W] ) {
32                      Visited[W] = TRUE;
33                      printf( "visit vertex: %c\n", G.Vertices[W] );
34                      /* 至關於訪問頂點W */
35                      AddQ (Q, W);
36                   }
37            } /* while結束*/
38 } /* 結束從U開始的BFS */
DFS
DFS/BFS

 

 5.掌握圖相關名詞的概念

    Q1:什麼是圖的連通份量?

    無向圖的極大連通子圖

    Q2:什麼是無向徹底圖?什麼是有向徹底圖?

    在無向圖中,若是任意兩個頂點之間都存在邊,則稱該圖爲無向徹底圖。

    在有向圖中,若是任意兩個頂點之間都存在方向互爲相反的兩條弧,則稱爲有向徹底圖。

 

6.掌握圖的應用示例 :拯救007

 

題目說明:

在老電影「007之生死關頭」(Live and Let Die)中有一個情節,007被毒販抓到一個鱷魚池中心的小島上,他用了一種極爲大膽的方法逃脫 —— 直接踩着池子裏一系列鱷魚的大腦殼跳上岸去!

設鱷魚池是長寬爲100米的方形,中心座標爲 (0, 0),且東北角座標爲 (50, 50)。池心島是以 (0, 0) 爲圓心、直徑15米的圓。給定池中分佈的鱷魚的座標、以及007一次能跳躍的最大距離,你須要告訴他是否有可能逃出生天。

輸入格式:

首先第一行給出兩個正整數:鱷魚數量 N(≤)和007一次能跳躍的最大距離 D。隨後 N 行,每行給出一條鱷魚的 ( 座標。注意:不會有兩條鱷魚待在同一個點上)。

輸出格式:

若是007有可能逃脫,就在一行中輸出"Yes",不然輸出"No"。

題目分析:

Step 1:從題目中抽象出圖的模型。注意一個誤區:可能認爲圖的頂點只是鱷魚頭,而忽略了岸邊也是「圖的頂點」。

Step 2:圖的邊如何創建?

①以孤島爲圓心,以007跳躍的最大距離爲半徑,觀察有哪些頂點落入圓中

 ②選定一個頂點進行DFS,當發現該頂點沒法靠岸,則返回,選取下一個頂點,進行遞歸調用剛纔的函數

  ③直到找到能夠靠岸的頂點爲止

 算法設計:

 

 1 void Save007(Graph G)
 2 {
 3     for(each V in G)
 4     {
 5         if(!visited[V]&&FirstJump(V))//FirstJump函數是用來判斷是否在跳躍範圍內 
 6         {
 7             answer=DFS(V);
 8             if(answer==YES) break;
 9         }
10         
11     }
12     if(answer==YES)output("YES");
13     else output("NO");
14 }
15 int DFS(Vertex V)//在DFS算法的基礎上進行改良 
16 {
17     visited[V]=true;
18     if(IsSafe(V))answer=YES;//IsSafe函數用來判斷該頂點是否能夠一步到岸 
19     else
20     {
21         for(each W in G)
22         {
23             if(!visited[W]&&Jump(V,W))
24             {
25                 answer=DFS(W);
26                 if(answer==YES) break;
27             }
28         }
29     }
30     return answer;
31 }

 

7.掌握圖的應用示例:六度空間

   算法思路:對每一個結點進行廣度優先搜索,搜素過程當中累計訪問的結點數,須要記錄層數:僅計算六層之內的結點數。

  

 

 

8.掌握最小生成樹。

   Q1:如何理解最小生成樹?

   是一棵樹代表其沒有迴路,若是存在|V|個頂點則必定有|V-1|條邊;是生成樹代表其包含所有頂點且|V-1|條邊均在樹裏;最小則是指邊的權重最小。

   Q2:如何理解Prim算法和Kruskal算法?

   Prim算法「讓一棵小樹長大」是指選準一個頂點添加邊;Krusal算法「將森林合併成樹」是指直接按權值選取邊。

 9.掌握拓撲排序。

   Q1:如何理解拓撲排序及相關算法?               

  

 Q2:如何理解及計算關鍵路徑?(易出錯)

 

  Q3:課後錯題

 

錯因:誤解題意。

從0到3的時間是12,是由0-2-3決定的,而不是0-1-3。而4也是由0-2影響的,因此加快之後,能提早完工。

期中測試錯題彙總

1.有向圖鄰接矩陣中第i行非零元素的個數爲第i個頂點的出度,第i列非零元素的個數爲第i個頂點的入度。

2.設h爲不帶頭結點的單向鏈表。在h的頭上插入一個新結點t的語句是:t->next=h; h=t;

3.採用多項式的非零項鍊式存儲表示法,若是兩個多項式的非零項分別爲N​1​​和N​2​​個,最高項指數分別爲M​1​​和M​2​​,則實現兩個多項式相加的時間複雜度是:O(N​1​​+N​2​​)

   M1和M2爲干擾選項,與本題無關。實現兩個多項式相加,必須把每一個多項式都遍歷一遍。

4.一棵共有 18 個結點的三叉樹中,度爲 2 的結點 3 個,度爲 3 的結點 2 個,問該樹度爲 1 的結點有幾個?

   樹中結點數 = 全部結點度數之和+1  故爲5個

5.若一搜索樹(查找樹)也是棵有 n 個結點的徹底二叉樹,則不正確的說法是:最大值必定在葉結點上,反例以下:

正確:中位值結點在根結點或根的左子樹上。

 

第九至十講 排序

1.掌握簡單排序:冒泡排序和插入排序。

   Q1:如何理解簡單排序?

  

   Q2:如何理解冒泡排序?

  

   Q3:如何理解插入排序?

 Q4:插入排序中時間複雜度的下界由什麼決定?

 2.掌握希爾排序。

     Q1:請問希爾排序的原理是?

   Q2:希爾排序的基本算法是?

   Q3:希爾排序中要注意什麼狀況?

   增量元素不互質的狀況,會致使部分增量達不到排序效果。

3.掌握堆排序。

   Q1:請給出堆排序的兩種算法並分析優劣?

 

   Q2:堆排序是不穩定的,請舉個例子解釋?

   1    2(1)    3     2(2)

       1
      / \
    2(1) 3
     /
   2(2)

      2(2)
      / \
    2(1) 3
     /
     1

        3
       / \
     2(1) 2(2)
      /
      1

 4.掌握歸併排序。

    Q1:歸併排序的合併的核心是?

    有序子列的合併。

    Q2:如何用算法實現有序子列的合併?

 

   Q3:如何用遞歸算法實現歸併排序?

 

  Q4:如何用非遞歸算法實現歸併排序?

 

5.掌握快速排序。

   Q1:如何描述快速排序?

    Q2:子集劃分時若是出現元素等於主元,如何解決?

   

    第二種處理方法的時間複雜度如右圖。

    Q3:請給出快速排序的算法實現並解釋部分設計的思路?

       爲何要設計Cutoff及Insertion_Sort()函數?

  

         爲何要設計void Quick_Sort()?

            統一函數接口。

 

6.掌握表排序。

   Q1:如何進行間接排序(不移動數據的位置,只改變數據的輸出順序)?

  

     排序時採用插入排序。

    Q2:1.如何進行物理排序(移動數據)?

                N個數字的排列由若干個獨立的環組成,只需在環的內部進行數據順序的調整。如圖:不一樣顏色表示不一樣的環。

   

           2. 如何判斷一個環的結束?

              每訪問一個空位i後,就令table[i]=i。當發現table[i]==i時,環結束。

          3.該算法的時間複雜度分析?

  

 7.掌握桶排序和基數排序。

    Q1:請用簡要描述桶排序?

    把數據放入到多個桶裏面,在對桶裏面的數據進行排序,而後遍歷各桶獲得元素序列。

    Q2:請簡要描述基數排序?

    將整數按位數切割成不一樣的數字,而後按每一個位數分別比較。

    Q3:請給出基數排序的例子?

  

    Q4:請給出兩種基數排序MSD和LSD的例子?

   

      

  • MSD:先從高位開始進行排序。
  • LSD:先從低位開始進行排序。

       就本例來講,LSD的方法只需放入桶後按照桶的順序取出,不用再對桶中的數據進行排序,效率更高。

8.掌握排序算法的比較。

 

    課後錯題:

    1.下列排序算法中,哪一種算法可能出現:在最後一趟開始以前,全部的元素都不在其最終的位置上?

      A.堆排序  B.插入排序  C.冒泡排序  D.快速排序

      解析:注意是全部的元素!插入排序可能使所有的元素髮生位移,而堆排序使左右兩個子樹,某個子樹上的元素髮生位移。

    2.數據序列(3,2,4,9,8,11,6,20)只能是下列哪一種排序算法的兩趟排序結果?

       A.快速排序   B.冒泡排序   C.選擇排序    D.插入排序   

      解析:對於後三種排序方法,兩趟排序後,序列的首部或尾部的兩個元素應是有序的兩個極值,而給定的序列不知足。

9.掌握散列查找。
   Q1:什麼是裝填因子?
   設散列表的空間大小爲m,填入表中元素的個數爲n,則稱a=n/m爲散列表的裝填因子。

   Q2:請簡述散列函數的構造?

   ①數字關鍵詞散列函數的構造:直接定址法(取關鍵詞的某個線性函數值爲散列地址)、除留餘數法、數字分析法、摺疊法、平方取中法。

  

   ②字符關鍵詞散列函數的構造:

    

     注:C 庫函數 int atoi(const char *str) 把參數 str 所指向的字符串轉換爲一個整數(類型爲 int 型)。

            散列表是一個包含關鍵字的具備固定大小的數組,表的大小記爲 TableSize.

            <<在C語言中表明左移運算符。

     Q3:處理衝突的方法有哪些?

     ①換個位置:開放定址法   ②同一個位置的衝突對象組織在一塊兒:鏈地址法

     Q4:開放地址法分爲哪些?

     ①線性探測   ②平方探測  ③雙散列

      ASLu的計算方法(以該散列表爲例):

      地址0,到第一個關鍵字爲空的地址2須要比較3次,所以查找不成功的次數爲3.

      地址1,到第一個關鍵字爲空的地址2須要比較2次,所以查找不成功的次數爲2.

      地址3,到第一個關鍵字爲空的地址2須要比較1次,所以查找不成功的次數爲1.   

      地址7,到第一個關鍵字爲空的地址2須要比較9次,所以查找不成功的次數爲9.(從地址6開始,再循環回去).

      Q5:請寫出平方探測法的算法?

  Q6:什麼是雙散列探測法?

 Q7:什麼是再散列探測法?

 Q8:請描述分裂連接法及其算法?

10.掌握散列表的性能分析。

     Q1:影響散列表性能的因素有哪些?

    Q2:請分析①線性探測   ②平方探測  ③雙散列④分離連接的性能?

 

   Q2:請給出散列方法、開放地址法和分離連接法的優劣勢分析?

  

  Q3:請給出散列表查找的應用實例?

相關文章
相關標籤/搜索