【整理】資料結構——題目

Ignorance__發表於2020-11-06

文章目錄

前言

已經到衝刺階段了,整理儘量縮短時間。按100道非重複的題目來算,一天整理十道,最多需要30天整理完成,因為給專業課的時間現在還不是很多,廢話不多說,開始整理。

按照大綱順序整理

一、預備知識

a. 瞭解C++和Java基本語法結構,掌握遞迴思想。

二、程式效能

a. 瞭解複雜性表示和計算方法
  1. 【簡答題】給出一段極短的程式,大概三四行,包括for和while語句,計算複雜度。一般和求和和階乘相關。
    在這裡插入圖片描述
b. 掌握插入排序、選擇排序、氣泡排序、名次排序的基本思想
  1. 【演算法題】在演算法題中涉及到氣泡排序的演算法思想。
/**給定一個整數陣列,設計一個演算法,將陣列中的所有0元素全部移到陣列末尾,保持其他非零元素相對位置不變,要求除陣列本身外,不使用其他輔助儲存空間*/
void zeroBubble(int a[], int n)
{
    bool flag = false;
    for(int i = 0; i < n-1; i++)
        for(int j = 0; j < n-1-i; j++)
        {
            if(a[j] == 0 && a[j+1] != 0)
            {
                a[j] = a[j+1];
                a[j+1] = 0;
                flag = true;
            }
            if(flag == false)
                return;
        }
}
  1. 【簡答題】羅列出基本排序演算法的最好最壞和平均情況下的時間複雜度以及其它性質
    在這裡插入圖片描述
  2. 【簡答題】根據排序演算法寫出第一趟或前幾趟排序的結果
/*對關鍵字序列{23,16,72,60,25,9,68,71,52}進行堆排序,寫出輸出兩個關鍵字後的剩餘堆*/
/*有6個元素{20,15,12,7,9,18},進行直接插入遞增排序,寫出第三趟排序的結果*/
/*對關鍵字序列{10,7,18,31,15,9,22,26},使用氣泡排序/快速排序(選第一個記錄為支點),基礎排序,寫出每趟排序後的結果*/
  1. 【簡答題】簡述快速排序、最大堆排序、基數排序等各排序演算法的思想
/*快速排序:首先選取一個樞軸(一般選第一個元素),指標i和指標j分別在序列的開頭和結尾,取出樞軸位置上的樹。指標j從後往前移動,遇到比樞軸小的數停住,和指標i交換。指標i從前往後掃,遇到比樞大的數停住,和指標j交換,重複以上過程,直到下一次兩指標停住時,指標j在指標i的左邊,交換樞軸元素和指標j指向的元素。至此,樞軸左邊的數都小於等於它,右邊的數都大於或等於它。然後對樞軸兩邊的數分別進行快速排序。*/

/*最大堆排序:先將要排序的n個元素初始化為一個最大堆,然後每次輸出堆頂元素,再將堆底最後一個元素移到堆頂,對堆頂元素進行向下冒泡使其繼續保持最大堆性質,重複上述操作,在最終的序列中各元素將按非遞減次序排列。堆初始化的時間為O(n),n-1次刪除操作的時間複雜度為O(nlogn),因此總時間為O(nlogn)*/
  1. 【簡答題】有5000個無序元素,公式化描述(陣列),要求最快速度選取最大的10個元素。請問,在快速排序、堆排序、基數排序、歸併排序四種方法中,採用哪種方法最好,為什麼
/*採用堆排序的方法最好,在時間複雜度上,平均時間複雜度基數排序為O(n*k),其餘三種演算法為O(nlogn)。在空間複雜度上,快速排序為O(logn),堆排序為O(1),基數排序為O(n+k),歸併排序為O(n)。且堆排序不需要等到排序演算法結束就能選出前10個最大值,初始化堆完成後,刪除十次最大堆堆頂元素即可*/

三、資料描述

a. 掌握線性表的公式化描述、連結串列描述、間接定址等儲存方法
  1. 【簡答題】順序表和連結串列的性質和優劣
順序表:
	定義:線性表的順序儲存稱為順序表。
	性質:邏輯上相鄰的兩個元素在物理位置上也相鄰,具有隨機訪問特性。
	優點:可以順序存取,也可以隨機存取,存取時間複雜度為O(1)
	缺點:需要預先分配足夠大的空間,插入和刪除平均需要移動一半的元素,插入刪除時間複雜度為O(n), 不適用於需要頻繁插入和刪除的應用。
	適用物件:經常按序號訪問資料元素的物件,且長度和儲存規模可以估計。

連結串列:
	定義:線性表的鏈式儲存稱為連結串列
	性質:不適用於連續的儲存單元,邏輯上相鄰的兩個元素在物理位置上不一定相鄰。
	優點:適合進行插入刪除操作,不需要提前分配足夠的儲存空間,刪除和插入操作需要的時間複雜度為O(1)。
	缺點:不支援隨機存取,元素因指標需要額外佔用儲存空間,查詢元素的時間複雜度為O(n)。
	適用物件:線性表的長度和規模不容易估計的情況,頻繁進行插入刪除操作的線性表。

間接定址:
	優點:間接定址中,陣列不在儲存節點,而是儲存節點的地址,可以看做是公式化描述和鏈式描述的結合體。存取時間複雜度為O(1),插入刪除操作僅僅修改節點的地址在陣列中的位置,優於使用連結串列描述的插入刪除操作。
	缺陷:儲存節點的地址增加了額外的開銷
  1. 【順序表】設一組有序的記錄關鍵字序列為(13,18,24,35,47,50,62,83,90),查詢方法用折半查詢,要求計算出差找關鍵字62時的比較次數並計算出查詢成功時的平均查詢長度
比較次數:從n/2向下取整開始算就好。
平均查詢長度:對集合中的每一個元素計算查詢次數最後除以元素總數。
  1. 【間接定址】一個班級有15個學生,使用1,2,3,…,14,15作為學號。(i,j)表示學生i和學生j參加了同一個興趣小組。對給出的集合S={(1,2),(6,9),(15,7),(1,6),(10,8),(8,11)},請基於模擬指標(間接定址)設計資料結構表示集合S中的興趣小組,並羅列S中所有的興趣小組。
data:  1  2  6  7  8  9  10  11  15
link: -4  1  1  -2  -3  1  8  8  7

data為學號,link為元素的根節點的索引(根節點的索引為負數)
  1. 【公式描述】假定採用下述公式來描述一個線性表
    l o c a t i o n ( i ) = ( l o c a t i o n ( 1 ) + i − 1 ) location(i) = (location(1)+i-1) % MaxSize; location(i)=(location(1)+i1)
    與專門保留一個表長的做法不同的是,用變數first和last來指出表的第一個和最後一個元素的位置。試描述如何刪除元素,並分析操作的時間複雜度。
插入:假如線上性表第i個位置插入新元素,將i帶入公式,得到插入位置location(i)。若該位置不滿足first<=location(i)<=last, 插入失敗。否則,若該位置為空,直接插入該元素。若該位置不為空,將線性表第i個元素以及之後的所有的元素後移一個位置,空出一個位置插入新元素,時間複雜度為O(n)。
刪除:假如線上性表第i個位置刪除新元素,將i帶入公式,得到插入位置location(i)。若該位置不滿足first<=location(i)<=last, 刪除失敗。否則,將線性表第i+1個元素以及之後的所有的元素左移一個位置,該線性表的長度減一,時間複雜度為O(n)
  1. 【迴圈單連結串列】猴子圍圈投票問題,有n個猴子,按順時針編號,分別是1到n。現在開始選大王,從第一個開始順時針報數,報到m的猴子出圈,緊接著下一個開始從1順時針報數,如此下去,最後剩下來的就是大王,問使用什麼資料結構可以選出大王。
約瑟夫問題,使用迴圈單連結串列可解。
迴圈單連結串列:是單連結串列,表尾節點*r的next域指向連結串列頭結點LinkList,故表中沒有指標域為NULL的結點。
b. 瞭解遍歷器的作用和實現方法

在圖中可能會用到,其他地方還沒考過

c. 掌握線性表的插入、刪除、合併等運算方法
  1. 【演算法題】【順序表】判斷以@結尾的字串是否為迴文
演算法思想:從左往右和從右往左同時遍歷,如果有不一樣的,則不是迴文。
bool isHui(char a[])
{
    int i = 0, len = 0;
    bool res = true;
    while(a[i]!='@')
        len++;

    for(int j = 0; j < len/2; j++)
        if(a[j] != a[len-j-1])
            res = false;
    return res;
}
  1. 【演算法題】【連結串列】線性表用單連結串列儲存,請設計單連結串列類Chain的一成員函式simpleSelectSortList(),實現簡單選擇排序演算法,並分析演算法複雜度
template <class T>
struct ChainNode
{
    T data;
    ChainNode<T>* next;
};
template <class T>
void SimpleSelectSortList() // 假設連結串列不含有頭節點
{
    ChainNode<T>* p = root;
    ChainNode<T>* q;
    ChainNode<T>* minN;

    while(p != NULL)
    {
        minN = p;
        q = p->next;
        while(q != NULL)
        {
            if(q->data < minN->data)
                minN = q;
        }
        if(minN->data < p->data)
        {
            // swap
            T tem = p->data;
            p->data = minN->data;
            minN->data = tem;
        }
        p = p->next;
    }

}

  1. 【演算法題】【順序表】設計演算法實現將陣列r[s:t] 中所有奇數移到所有偶數之前,要求演算法複雜度為O(n),n為陣列元素的個數。
演算法思想:快速排序,一個從左往右,一個從右往左,如果左邊遇到偶數,右邊遇到奇數,停下,交換,直到兩指標交叉。

void quickSort(int r[], int s, int t)
{
    int p1 = s;
    int p2 = t;
    while(p1 < p2)
    {
        while(r[p1]%2 == 1 && p1<=t)
            p1++;
        while(r[p2]%2 == 0 && p2>=s)
            p2--;
        if(p1 < p2)
        {
            int tem = r[p1];
            r[p1] = r[p2];
            r[p2] = tem;
        }
    }
}
  1. 【演算法題】【連結串列】有一個單連結串列L,其結點的元素值以非遞減有序排列,請編寫函式purge,刪除單連結串列中多餘的元素值相同的結點。
演算法思想:因為是非遞減,如果遇見相鄰的一樣就刪掉
template <class T>
struct ChainNode
{
    T data;
    ChainNode* next;
};

void purge(ChainNode* root)
{
    if(root == NULL || root->next == NULL)
        return;
    ChainNode* pre = root;
    ChainNode* p = root->next;

    while(p!= NULL)
    {
        if(pre->data == data->data)
        {
            pre->next = pre->next->next;
            delete p;
        }
        p = pre->next;
    }
}

  1. 線性表使用公式化描述方式儲存。編寫一個函式,從給定的線性表A中刪除值在x到y(包括x和y)之間的所有元素,要求以較高的效率來實現
演算法思想:把在刪除範圍之內的元素標記上,遇到後面不在範圍之內的,之間往前移動,因為被標記的元素可視為空,最後長度-k,確保所有在範圍內的元素都被清空。
template <class T>
struct LinearList
{
    T* a;
    int length;
};

template <class T>
void deleteXY(T* a, T x, T y)
{
    int i;
    int k = 0;
    int n = sizeof(a)/sizeof(a[0]);
    for(i = 0; i<n; i++)
    {
        if(a[i] >= x && a[i] <= y)
            k++;
        else
            a[i-k] = a[k];
    }
    length -= k;
}

  1. 為帶表頭的單連結串列Chain編寫一個成員函式Reverse,該函式對連結串列進行逆序操作,要求逆序操作原地就地進行,不分配任何新的結點。要求首先給出類宣告,在類的宣告中,其它成員函式可省略。
演算法思想:指標反過來,最後把頭結點接到尾巴上。
// 第一題
template <class T>
struct ChainNode
{
    ChainNode<T>* next;
    T data;
};

template<class T>
class Chain
{
public:
    Chain()
    {
        first = NULL;
    };

    ~Chain();
private:
    ChainNode<T>* first;
};

template <class T>
void Reverse(ChainNode* head)
{
    if(head->next == NULL)
        return;

    ChainNode<T>* pre = head->next;
    ChainNode<T>* p = head->next->next;
    ChainNode<T>* tem;

    while(p != NULL)
    {
        tem = p->next;
        p->next = pre;
        pre = p;
        p = tem;
    }
    head->next = pre;
}
d. 掌握箱子排序,基數排序
  1. 【潛在考點】箱子排序
  2. 【基數排序】基數排序已經在上面提到過

四、陣列和矩陣

a. 掌握對角矩陣、三對角矩陣、對稱矩陣等特殊矩陣的特徵,掌握儲存方法和基本運算實現

20.【對稱矩陣】 對一個n階方陣,如果是一個對稱矩陣,即矩陣元素 a i j = a j i a_{ij} = a_{ji} aij=aji,為了節省時間,我們使用一維陣列元素D[]來儲存下三角元素,D[]元素個數為多少個?假設我們採用行主對映的方式,那麼任意矩陣元素 a i j a_{ij} aij在陣列中的位置是什麼?

元素個數:為等差數列,n*(n+1)/2
陣列中的位置除非特別提及,其位置從a[0]開始。
確定元素的點(i, j),除非特別提及,是從(1,1)開始。
以下三角為例,位置k = i*(i-1)/2 + j -1
  1. 【特殊矩陣】矩陣T滿足對於所有的i和j,有T(i, j) = T(i-1, j-1), 其中i>1, j<=n。該矩陣最多有多少個不同的元素?僅將這些不同元素對映儲存在一維陣列a中以減少冗餘,給出元素到陣列a[k]的對映方案
元素個數:共有2*n-1個元素
對映:k = i-j+n-1
  1. 【三對角矩陣】將三對角矩陣A中三條對角線的元素按行存放在一維陣列B中,B中共有多少元素,給出在對角線上的元素在B中的對映公式
元素個數:3*n-2
對映公式:2*i+j-3 至於如何推導的分為兩部分,在我另一篇草稿裡有寫
  1. 【五對角矩陣】將五對角矩陣A中五條對角線的元素按行存放在一維陣列B中,B中共有多少元素,給出在對角線上的元素在B中的對映公式
元素個數:5*n-6
對映公式:4*i+j-5

五、堆疊

a. 掌握堆疊的基本概念、基本操作和實現方法
  1. 堆疊的應用會隱藏在演算法裡,比如非遞迴的二叉樹前序、中序和後序遍歷和DFS演算法。

  2. 設有編號為123456的六輛車,順序進入一個棧式結構的站臺。能否得到435612和135426的出站序列,請說明理由

注意一定是從左往右讀,那麼第一輛車就是左邊的第一個數。
可以通過在草稿上,寫寫畫畫來判斷。
在這裡,435612不可行,因為2必定比1先出站。135426可行
  1. 設棧S的初始狀態為空,現有5個元素組成的序列{a,b,c,d,e},對該序列在S棧上依次進行如下操作(從a開始,出棧之後不再進棧):進、進、進、出、進、出、進請回答出棧序列
出棧序列:c、d、e、b、a
  1. 設有編號A,B,C,D的四輛車,順序進入一個棧式結構的站臺,試寫出這四輛車開出車站的所有不可能的順序
這個題比較考驗概念理解,使用列舉法比如A先出棧、B先出棧、C先出棧、D先出棧,共十種
b. 掌握括號匹配、離線等價類的實現思想
  1. 【潛在考點】括號匹配
  2. 【潛在考點】離線等價類

六、佇列

a. 掌握佇列的基本概念,基本操作和實現方法
  1. 堆疊的應用會隱藏在演算法裡,比如非遞迴的二叉樹層次遍歷和BFS演算法。

  2. 描述棧與佇列的相同點與不同點

  3. 簡述順序儲存佇列的滿和空的條件

相同點:都是操作受限的線性表,都只能線上性表的兩端或一端進行操作,都具有順序和鏈式兩種儲存結構。
不同點:棧是隻能在一端進行插入或刪除操作,其中允許進行插入和刪除的一端稱為棧頂,它是動態變化的,另一端稱為棧底,它是固定不變的,特點為先進後出。佇列是允許在表的一端進行插入,在另一端進行刪除,主要特點為先進先出。

方法:犧牲一個佇列單元來區分隊空和隊滿。
隊空條件:Q.front = Q.back;
隊滿條件:Q.front = (Q.back+1)%maxSize;

七、跳錶和雜湊

a. 瞭解跳錶的基本概念,基本操作和實現方法
  1. 【潛在考點】跳錶
b. 掌握雜湊的基本概念,基本操作和實現方法
  1. 雜湊表長度為11,雜湊函式為Hash(k) = k%11,元素序列為{1, 3, 19, 8, 14, 25, 6, 28}。請回答下列問題:
    (1)請畫出該序列的線性開型定址雜湊儲存結構
    (2)請畫出該序列的連結串列雜湊儲存結構
    (3)什麼是雜湊表的負載因子(表示桶的裝滿程度)
    (4)線上性開型定址雜湊表實現刪除時,如果只是把刪除元素所在的桶置空,會出現什麼問題
    (5)對於第4問,請寫出一種解決辦法
    (6)搜尋元素19和14所需要的比較次數是多少
    (7)給出刪除元素8後的雜湊表結構
    (8)在等概率情況下,查詢成功的平均次數(有元素的桶)
    (9)在等概率情況下,查詢失敗的平均次數(所有的桶)
    (10) 什麼是雜湊表?
    (11) 衝突可能與哪些因素有關?
雜湊表:根據關鍵字而直接進行訪問的資料結構
衝突產生的原因:雜湊函式可能會把兩個或者多個不同的關鍵字對映到同一地址;雜湊函式設計不合理;雜湊表長度不夠

八、二叉樹

a. 掌握二叉樹的基本概念、儲存方法、常用操作和特徵
  1. 前序、中序、後序和層次遍歷,畫出樹或者互相寫序列的題目
1 已知二叉樹的前序和後序序列為ABC和CBA,畫出四棵不同的二叉樹
2 根據二叉樹的陣列儲存,畫出樹的各種遍歷序列
3 前序序列A,B,C,D的二叉樹,中序序列可能是D,A,B,C嗎
4 二叉樹的層次序列為ABCDEFGHIJ,中序遍歷序列為DBGEHJACIF,寫出該二叉樹的前序遍歷序列
5 二叉樹的前序遍歷序列為ABDEGHJCFI,中序遍歷序列為DBGEHJACIF,寫出該二叉樹的層次遍歷序列
6 二叉樹的前序遍歷序列為ABDEGHJCFI,中序遍歷序列為DBGEHJACIF,寫出該二叉樹的後序遍歷序列
7 二叉樹的前序遍歷序列為ABDEGHJCFI,中序遍歷序列為DBGEHJACIF,寫出該二叉樹的葉節點
  1. 和二叉樹的度,結點個數相關的問題
核心公式:樹中結點數 = 總分叉數 + 1 = (1到n求和)度為k的結點的個數*k + 1 = 度為0的結點個數(葉子結點) + 度為1的結點個數 + ... 
1 設樹T的度為4,其中度為1234的節點個數分別為4211,則T中的葉子數為多少
2 假設高度為h的二叉樹只有度為0和度為2的結點,則此類二叉樹所包含的結點數至少為多少,請給出推導過程
b. 掌握二叉樹的前序、中序、後臺、按層次遍歷方式
  1. 【演算法題】最小深度是指根結點到最近的葉子結點的最短路徑上的結點的個數,給定一個鏈式儲存的二叉樹,設計演算法求其最小深度,敘述演算法思想並分析演算法的時間複雜度
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

// 遞迴
template <class T>
int minDepth(BTreeNode<T>* t)
{
	int res;
    if(t == NULL)
        return 0;
     if(t->leftChild == NULL && t->rightChild== NULL)
    {
        res = 1;
    }
    if(t->leftChild != NULL && t->rightChild != NULL)
    {
    	int ld = minDepth(t->leftChild);
        int rd  = minDepth(t->rightChild);
        res = (ld > rd ? rd : ld) + 1;
    }
    if(t->leftChild == NULL && t->rightChild != NULL)
    {
        int rd  = minDepth(t->rightChild);
        res = rd + 1;
    }
    if(t->leftChild != NULL && t->rightChild == NULL)
    {
    	int ld = minDepth(t->leftChild);
        res = ld + 1;
    }
    return res;
}
  1. 判斷一棵樹是否為最大樹
//第二題
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

template <class T>
bool isMaxTree(BTreeNode<T>* root)
{
    BTreeNode<T>* p = root;
    bool res1, res2;

    if(p == NULL)
        return true;

    if(root->leftChild != NULL && root->leftChild->data > p->data)
        return false;
    if(root->rightChild != NULL && root->rightChild->data > p->data)
        return false;
    res1 = isMaxTree(p->leftChild);
    res2 = isMaxTree(p->rightChild);
	
	return res1 && res2; 
}
  1. 試編寫演算法,求給定二叉樹上從根節點到葉子節點的一條路徑長度等於樹的深度減一的路徑,若這樣的路徑存在多條,則輸出路徑終點在“最左”的一條
// 第二題
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

template <class T>
int getDepth(BTreeNode<T>* root)
{
    int lh, rh;
    
    if(root == NULL)
        return 0;

    lh = getDepth(root->leftChild);
    rh = getDepth(root->rightChild);

    return (lh > rh ? lh : rh) + 1;
}

template <class T>
void findPath(BTreeNode<T>* root, int n)
{
    int h = getDepth(root);
    stack<BTreeNode<T>*> s;
    int length = 1;
    T* Path = new T[n];
    BTreeNode<T>* p = root;

    while(p != NULL || !s.empty())
    {
        if(p != NULL)
        {
            path[length++] = p;
            s.push(p);
            p = p->leftChild;
        }
        else
        {
        	p = s.top();
        	s.pop();
        	if(p->rightChild == NULL) //到葉節點了,判定是否為所求路徑
        	{
        		if(length == h)//因為有length++,該路徑的實際長度為length-1
        		{
        			for(int i = 1; i< length; i++)
        			{
        				cout << path[i]<<endl;
        			}
        		}
        	}
        	p = p->rightChild;
        }
    return;
}
  1. 編寫演算法,求解從根節點到給定點p所指節點之間路徑
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

template <class T>
T* findPath(BTreeNode* root, BTreeNode* p)
{
    if(p == NULL)
        throw "p can not be empty.";
    int length = 0;
    int n = numberOfVertices();
    T* path = new T[n];

    rFindPath(root, p, path, length);
    return path;
}

template <class T>
bool rFindPath(BTreeNode* root, BTreeNode* p, T* path, int length)
{
	bool lres, rres;
	if(root == NULL)
		return false;
    path[length++] = root;
    if(root == p)
        return true;
    // 這是一顆樹,所以只有一條路徑,左子樹和右子樹最多一個返回true。
    lres = rFindPath(root->leftChild, p, path, length);
    if(lres == false)
    {
    	rres = rFindPath(root->rightChild, p, path, length);
    }
   
    if(lres || rres)
        return true;
         
    length--;
    return false;
    }
}
  1. 計算二叉樹中度為1的結點個數
// 第二題

template <class T>
struct BTreeNode
{
    T data;
    BTreeNode* leftChild;
    BTreeNode* rightChild;
};

int getDegree(BTreeNode* root)
{
    int num = 0;
    
    if(root == NULL)
    	return 0;
    if(root->leftChild != NULL && root->rightChild == NULL)
    {
        num++;
    }
    if(root->rightChild != NULL && root->leftChild == NULL)
    {
        num++;
    }
    
    int lres = getDegree(root->leftChild);
    int rres = getDegree(root->rightChild);

    return (num+lres+rres);
}
  1. 編寫演算法,刪除二叉搜尋樹的最小元素。
// 第二題
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

template <class T>
void deleteMin(BTreeNode<T>* root)
{
    if(root == NULL)
        return;
    BTreeNode<T>* pre;
    BTreeNode<T>* p = root;

    while(p->leftChild != NULL)
    {
        pre = p;
        p = p->leftChild;
    }
    if(pre == NULL)
    {
    root = p->rightChild;
    delete p;
    }else
    {
    pre->leftChild = p->rightChild;
    delete p;
    }
}
  1. 判斷一棵樹是否為滿二叉樹
// 第二題

template <class T>
struct BTreeNode
{
    T data;
    BTreeNode* leftChild;
    BTreeNode* rightChild;
};

template <class T>
int getDepth(BTreeNode<T>* root)
{
    int lh, rh;

    if(root == NULL)
        return 0;

    lh = getDepth(root->leftChild);
    rh = getDepth(root->rightChild);

    return (lh > rh ? lh : rh) + 1;
}

template <class T>
int getNum(BTreeNode<T>* root)
{
    if(root == NULL)
    	return 0;

    int ln = getNum(root->leftChild);
    int rn = getNum(root->rightChild);
    
    return (ln + rn + 1);
}

template <class T>
bool isFull(BTreeNode<T>* root);
{
    int h = getDepth(root);
    int num = getNum(root);
    
    if((math.pow(2, h)-1) == num)
        return true;
        
    return false;
}
  1. 編寫演算法,求出二叉樹中結點的度數為1的個數,並以n返回(要求不能使用遞迴)
template <class T>
struct BTreeNode
{
    T data;
    BTreeNode<T>* leftChild;
    BTreeNode<T>* rightChild;
};

template<class T>
int getDegree(BTreeNode* root)
{
    BTreeNode<T>* p = root;
    stack<BTreeNode<T>*> s;
    int num = 0;
    
    if(p == NULL)
        return -1;
        
    while(p != NULL || !s.empty())
    {
        if(p != NULL)
        {
            if(p->leftChild == NULL && p->rightChild != NULL)
                num++;

            if(p->leftChild == NULL && p->rightChild != NULL)
                num++;

            s.push(p);
            p = p->leftChild;
        }
        else
        {
            p = s.top();
            s.pop();

            p = p->rightChild;
        }
    }
    
    return num;
}

c. 掌握基於樹儲存的線上等價類實現
  1. 【潛在考點】基於數儲存的線上等價類實現
d. 瞭解樹的儲存方法
  1. 根據樹的陣列描述或連結串列描述,畫樹或判斷其節點位置、儲存優缺點
二叉樹還可以用陣列進行邏輯描述,根據對映公式來確定節點之間的關係。

優點:可以隨機存取,不需要指標,空間利用率高。適用於完全二叉樹和滿二叉樹。
缺點:一個n個元素的二叉樹最多需要2^n個空間來儲存,當二叉樹缺少的元素很多時,會造成空間的極大浪費。

九、優先佇列

a. 掌握堆的基本概念和插入、刪除和初始化方法
b. 掌握堆排序的思想(經常與a一塊考)
  1. 對關鍵字序列{23,16,72,60,25,9,68,71,52}進行堆排序,輸出兩個關鍵字後的剩餘堆是什麼?
堆排序與其它排序有一點不同,就是分步驟的。首先是堆的初始化,使用的是篩選法建堆,其時間複雜度為O(n)。
之後輸出關鍵字便是最大(最小)堆的刪除
插入是向上冒泡
c. 掌握霍夫曼樹、霍夫曼編碼的實現方法
  1. 有一份電文中共使用5個字元:a,b,c,d,e,它們出現的頻率依次是 {1,3,19,8,14,25,6,28}
    1)試構造關於w的一顆霍夫曼樹
    2)求其加權路徑長度WPL
    3)求出每個字元的huffman編碼
d. 瞭解左高樹基本概念和插入、刪除、合併和初始化方法的實現
  1. 什麼是優先佇列?什麼是堆?為什麼使用堆描述優先佇列比使用線性邏輯描述優先佇列更好?什麼情況下使用左高樹描述優先佇列比使用堆描述優先佇列好?
堆:堆是一顆有最大堆和最小堆之分 / 在最大堆中每個節點的值都大於等於其子節點(如果有子節點的話)的值 / 最小堆定義類似 / 的完全二叉樹。
優先佇列:優先順序佇列是一種資料結構,每次從優先順序佇列中取出的是具有最高優先順序的元素。
為什麼使用堆:若使用線性邏輯描述優先順序佇列,插入刪除操作的平均時間複雜度為O(n),使用堆,則插入刪除的平均時間複雜度為O(logn),所以使用堆描述優先順序佇列。
左高樹:當兩個優先順序佇列或多個長度不同的佇列需要合併時,需要使用左高樹,左高樹可在對數時間內實現兩個優先順序佇列的合併。

十、搜尋樹

a. 掌握二叉搜尋樹(排序樹)基本概念和插入、搜尋、刪除的實現方法
  1. 一個二叉搜尋樹,設任一條從根到葉子的路徑包含的節點集合為S2,這條路徑中所有左邊的點的集合為S1,右邊所有店的集合為S3,設a,b,c分別為S1,S2,S3中的任意元素,是否有a<b<c,為什麼?
b. 掌握二叉平衡樹(AVL樹)基本概念和插入、搜尋、刪除的實現方法
  1. 給出按關鍵字序列{19,36,88,12,16,77,60}生成的二叉搜尋樹和AVL搜尋樹
  2. 推導12個元素構成的二叉平衡樹(AVL)的最大深度,並畫出示例
  3. 為什麼僅調整最小不平衡樹就不存在其他不平衡點?
  4. 什麼是AVL樹?高度為h的AVL樹最少有多少結點,最多有多少結點,n個節點的AVL樹的高度是多少?
AVL參照筆記
二叉平衡樹最大深度元素個數遞推公式:N_(h) = N_(h-1)+N_(h-2)+1,N_(0) = 0,N_(1) = 1,N_(2) = 2
因為AVL樹不平衡後,會使最小不平衡樹高度加1,正是因為最小不平衡樹高度加1導致的不平衡,經過調整後,最小不平衡樹的高度回覆了原來的值,所以樹的其它部分的平衡因子又與之前的相同,因此不再會有平衡點
  1. 【潛在考點】二叉搜尋樹相對於跳錶和雜湊的優勢
c. 掌握m叉搜尋樹和B樹基本概念以及插入、搜尋、刪除的實現方法
  1. 依次輸入{1,2,6,7,11,4,8,13,10,5,17,9,16,20,15}建立5階B樹,寫出建立的過程,然後寫出刪除結點8和結點16後的過程,計算磁碟的讀寫次數(等待看課本)
  2. 一個8key值的3階B樹最多有多少節點?最少有多少?並畫圖表示
  3. 【潛在考點】m叉搜尋樹
看筆記
m階B樹
B-樹首先是一棵m叉搜尋樹,並且是一顆擴充套件樹,並且滿足以下條件

a. 根節點至少有兩個孩子
b. 除根節點以外,所有內部節點至少存在孩子數目為(m/2)的上界
c. 第二條也就是說所有內部節點至少存在的元素數目為孩子數目 - 1。
d. 所有外部節點在同一層。
e. 只包含一個根節點的樹也是B樹。

十一、圖

a. 掌握圖的基本概念
  1. 【內含考點】該考點內容包含在題中之中,可參考筆記複習
  2. 【證明題】對有向圖來說,該“該圖為無環圖”等價於“該圖的頂點存在這樣的編號,使其鄰接矩陣為下三角形且對角線為全0”
充分性:由於不存在從某頂點出發再回到自身的邊,所以鄰接矩陣主對角元素均為0;由於不存在環,至少可以找到一入度為0的頂點,其編號最小,鄰接頂點按BFS順序從小到大編號,其鄰接矩陣為下三角形。
必要性:反證法:若圖含有環,那麼對於鄰接矩陣必存在至少一個(i,j),i<=j,使(j,i)不為零,與條件不符,得證。
  1. 【證明題】若無向圖G的頂點度數的最小值大於或等於2,證明G必然存在環路
反證法:若無向圖G的頂點度數的最小值大於等於2,那麼G必然不存在環路。若G不存在環路,至少有一點為端點,端點處的度數為1,與條件矛盾,所以G必然存在環路。
b. 掌握圖的鄰接矩陣和鄰接連結串列儲存方法
c. 掌握圖的深度優先和廣度優先遍歷演算法
  1. 由圖的鄰接表或鄰接矩陣,求
    (1)從頂點1開始的深度優先遍歷所得的序列
    (2)從頂點1開始的深度優先遍歷所得的生成樹
    (3)從頂點1開始的廣度優先遍歷所得的序列
    (4)從頂點1開始的廣度優先遍歷所得的生成樹
    (5)寫出該圖的一個拓撲序列
    (6)由鄰接表畫鄰接矩陣,由鄰接矩陣畫鄰接表
    (7)尋找某點到某點都所有簡單路徑
    (8)列出圖中所有的有向環路和它們的長度
d. 掌握圖的尋找路徑和尋找連通構件的方法
  1. 【演算法題】設計演算法,判斷圖的鄰接表中是否存在V_i到V_j的路徑
/**
用C++或java語言,判斷圖的鄰接表中是否存在Vi到Vj的路徑(i != j)
*/
// 邊表
struct ChainNode
{
    int data;
    ChainNode* next;
};

// 頂點表
struct GraphChain
{
    int data;
    ChainNode* firstNode;
};

struct linkedDigraph // 鄰接表
{
    ChainNode* aList;
    int n;  //頂點數
    int e;  //邊數
};

bool findPath(int source, int goal, linkedDigraph &G)
{
    int n = numberOfVertices();
    int* reach = new int[n+1];
    for(int i = 0; i<n+1; i++)
    {
        reach[i] = 0;
    }

    if(source == goal || rFindPath(source,goal,G,reach))
        return true;
    return false;
}

bool rFindPath(int source, int goal, linkedDigraph &G,reach)
{
    reach[source] = 1;

    ChainNode* u = G.aList[source]->firstNode;
    while(u->next != NULL)
    {
        if(reach[u->data] == 0)
        {
            if(u->data == goal || rFindPath(u->data,goal,G,reach))
                return true;
        }
        u = u->next;
    }
    return false;
}
  1. 【演算法題】已知有向圖以鄰近表表示,編寫演算法,求指定頂點k的入度
// 邊表
struct ChainNode
{
    int data;
    ChainNode* next;
};

// 頂點表
struct GraphChain
{
    int data;
    ChainNode* firstNode;
};

struct linkedDigraph // 鄰接表
{
    ChainNode* aList;
    int n;  //頂點數
    int e;  //邊數
};

int inDegree(int k, linkedDigraph &G)
{
    int n = numberOfVertices();
    int in = 0;
    for(int i = 1; i<=n; i++)
    {
        ChainNode* u = G.aList[i]->firstNode;
        while(u->next != NULL)
            if(u->data == k)
                in++;
            u = u->next;
    }
    return in;
}


  1. 【演算法題】已知無向圖以鄰接表儲存,試編寫演算法刪除邊(i,j)
// 第三題
// 邊表
struct ChainNode
{
    int data;
    ChainNode* next;
};

// 頂點表
struct GraphChain
{
    int data;
    ChainNode* firstNode;
};

struct linkedDigraph // 鄰接表
{
    ChainNode* aList;
    int n;  //頂點數
    int e;  //邊數
};

void deleteIJ(int i, int j, linkedDigraph &G)
{
    ChainNode* p = G.aList[i]->firstNode;
    ChainNode* pre = p;

     // 正好是頭結點
    if(p->data == j)
    {
        aList[i]->firstNode = p->next;
        delete p;
        return;
    }

    // 不是頭結點
    for(ChainNode* u = p->next; u != NULL ; u = u->next)
    {
        if(u->data == j)
        {
            pre->next = pre->next->next;
            delete u;
            return;
        }
        else
        {
            pre = u;
        }
    }
}

void deleteEdge(int i ,int j, linkedDigraph &G)
{
    deleteIJ(i,j,G);
    deleteIJ(j,i,G);
}
  1. 【演算法題】 假設有向圖以鄰接表儲存,試編寫演算法刪除弧(i,j)
// 第三題

struct ChainNode // 邊表
{
    int data;
    ChainNode* next;
};

struct GraphChain //頂點表
{
    int data;
    ChainNode* firstNode;
};

struct linkedDigraph // 鄰接表
{
    ChainNode* aList;
    int n;  //頂點數
    int e;  //邊數
};

void deleteIJ(int i, int j, linkedDigraph &G)
{
    ChainNode* p = G.aList[i]->firstNode;
    ChainNode* pre = p;

     // 正好是頭結點
    if(p->data == j)
    {
        aList[i]->firstNode = p->next;
        delete p;
        return;
    }

    // 不是頭結點
    for(ChainNode* u = p->next; u != NULL ; u = u->next)
    {
        if(u->data == j)
        {
            pre->next = pre->next->next;
            delete u;
            return;
        }
        else
        {
            pre = u;
        }
    }
}
  1. 【演算法題】已知有n個頂點的有向圖的連結表,設計演算法計算圖中出度為0的頂點數
// 第三題

template <class T>
struct ChainNode // 邊表
{
    T data;
    ChainNode<T>* next;
};

template <class T>
struct GraphChain //頂點表
{
    T data;
    ChainNode<T>* firstNode;
};

template <class T>
struct linkedDigraph // 鄰接表
{
    ChainNode<T>* aList;
    int n;  //頂點數
    int e;  //邊數
};

template <class T>
int outDegree(linkedDigraph &G)
{
	GraphChain<T>* aList = G.aList;
    int n = numberOfVertices();
    int num = 0;

    for(int i = 1; i<=n; i++)
    {
        if(aList[i]->firstNode == NULL)
            num++;
    }
    return num;
}
  1. 【演算法題】鄰接矩陣,僅儲存下三角矩陣,編寫演算法計算給定頂點的度
// 第三題
int mapping(int i ,int j)
{
    if(i >= j)
        return i*(i-1)+j-1;
    else
        return j*(j-1)+i-1;
}

int getDegree(int k, int n, int a[])
{
    int num = 0;
    int tem;
    for(int i = 0; i < k; i++)
    {
        tem = mapping(k, i);
        if(a[tem] != 0)
            num++;
    }
      for(int i = k+1; i < n; i++)
    {
        tem = mapping(k, i);
        if(a[tem] != 0)
            num++;
    }
    return num;
}
e. 掌握生成樹的尋找方法
  1. 如果不是最小生成樹,有BFS和DFS演算法;

  2. 最小生成樹有Prim和Kruskal演算法;在演算法設計中詳細說明

十二、貪婪演算法

a. 瞭解貪婪演算法的基本概念
  1. 【潛在考點】描述什麼叫做貪婪演算法
b. 掌握AOV網的拓撲排序演算法
  1. 與圖一起考,寫出拓撲排序序列
c. 掌握單源最短路徑的Dijkstra演算法
  1. 【圖表題】根據圖形,寫出Dijkstra演算法執行過程
d. 掌握最小耗費生成樹的概念、Prim和Kruskal演算法
  1. 在最小生成樹的Kruskal演算法中,如何判定是否存在迴路?
使用並查集,如果新邊的兩個頂點同屬於一個祖先,那麼便形成迴路。否則不形成迴路
  1. 給出圖、鄰接連結串列或者鄰接矩陣,求Prim或KrusKal的最小生成樹
e. 瞭解AOE網的關鍵路徑演算法
  1. 根據AOE網的圖,求關鍵路徑
AOE網:頂點叫做事件、邊叫做活動
這種比較簡單,使用觀察法便可以做出來
  1. 根據圖表,畫出AOE網,列出各事件最早、最晚發生時間;找出AOE中的關鍵路徑,並回答完成該工程所需要的最短時間
有可能需要使用虛線

十三、分而治之演算法

a. 瞭解分而治之思想
  1. 簡述分而治之演算法的思想
b. 掌握快速排序、歸併排序實現方法
  1. 演算法整理中有提到
c. 瞭解選擇問題基本思想

十四、動態規劃

a. 掌握所有頂點對時間的最短路徑演算法
  1. 給定圖的鄰接矩陣、鄰接表或者圖,寫出FLoyd演算法執行過程

相關文章