重拾資料結構

fitzeng發表於2017-08-04

這裡主要是記錄各種資料結構的結構體, 對於具體實現和講解日後回會以連結形式提供, 這裡只提供一個思維樹, 建立一個資料結構的思維體系, 後續更新歡迎關注 GitHub.

1 線性表

1.1 動態分配空間
typedef struct {
    ElemType * elem;
    int length;
    int listsize;
} SqList;複製程式碼
1.2 線性連結串列
typedef struct LNode {
    ElemType data;
    struct LNode * next;
} LNode, * LinkList;複製程式碼
1.3 靜態連結串列
typedef struct {
    ElemType data;
    int cur;    // 遊標指向下一個元素的陣列下標
} component, SlinkList[MAXSIZE];複製程式碼
1.4 迴圈連結串列 & 雙向連結串列
typedef struct DuLNode{
    ElemType data;
    struct DuLNode * prior;
    struct DuLNode * next;
} DuLNode, * DuLinkList;複製程式碼

2 棧和佇列

2.1 棧
typedef struct {
    SELemType * base;
    SElemType * top;##### 
    int stacksize;
} SqStack;複製程式碼

應用: 主要是利用先進後出的特性

  • 數制轉換

  • 括號匹配檢測

  • 行編輯程式

  • 迷宮非遞迴求解

  • 表示式求值

  • Hanoi塔問題

2.2 佇列
  • 鏈佇列
typedef struct QNode {
    QElemType data;
    struct QNode * next;
} QNode, * QueuePtr;
typedef struct {
    QueuePtr front;
    QueuePtr rear;
} LinkQueue;複製程式碼
  • 迴圈佇列
typedef struct {
    QElemType * base;
    int front;
    int rear;
} SqQueue;複製程式碼

3 串

3.1 定長串
typedef unsigned char SString[MAXSTRLEN + 1];
SString s;複製程式碼
3.2 變長
  • 堆分配儲存
typedef struct {
    char * ch;
    int length;
}HString;複製程式碼
  • 塊鏈儲存
typedef struct Chunk{
    char ch[CHUNKSIZE];
    struct Chunk * next;
} Chunk;
typedef struct {
    Chunk * head;
    Chunk * tail;
    int curlen;
} LString;複製程式碼

應用

  • 子串定位 (KMP)

4 陣列和廣義表

4.1 陣列順序儲存
typedef struct {
    ElemType * base;
    int dim;    // 陣列維數
    int * bounds;    // 維界基址
    int * constants;    // 印象函式常量基址
} Array;複製程式碼
4.2 矩陣

討論稀疏矩陣的儲存

  • 三元順序表
typedef struct {
    int i;
    int j;
    ElemType e;
} Triple;
typedef struct {
    Triple data[MAXSIZE + 1];
    int mu;    // 行
    int nu;    // 列
    int tu;    // 非零個數
}TSMatrix;複製程式碼
  • 行邏輯連結
typedef struct {
    Triple data[MAXSIZE + 1];
    int rpos[MAXRC + 1];    // 各行第一個非零元素位置表
    int mu, nu, tu;
} RLSMatrix;複製程式碼
  • 十字連結串列
typedef struct OLNode {
    int i;
    int j;
    ElemType e;
    struct OLNode * right;    // 該非零元素所在行的右鏈域
    struct OLNode * down;    // 該非零元素所在列的下鏈域
} OLNode, * OLink;
typedef struct {
    OLink * rhead;    // 行連結串列頭指標地址
    OLink * chead;    // 列連結串列頭指標地址
    int mu, nu, tu;
} CrossLink;複製程式碼
4.3 廣義表

表中有表

  • 頭尾連結串列儲存
typedef enum { ATOM, LIST } ElemTag;    // 0 : 原子, 1 : 子表
typedef struct GLNode {
    ElemTag tag;
    union {
        AtomType atom;
        struct {
            struct GLNode * hp;    // 表頭
            struct GLNode * tp;    // 表尾
        } ptr;    // 表節點指標域
    };
} * GList;複製程式碼
  • 擴充套件線性連結串列儲存
typedef enum { ATOM, LIST } ElemTag;    // 0 : 原子, 1 : 子表
typedef struct GLNode {
    ElemTag tag;
    union {
        AtomType atom;
        struct {
            struct GLNode * hp;    // 表頭
        } ptr;    // 表節點指標域
    };
    struct GLNode * tp;    // 表尾, 相當於 next.
} * GList;複製程式碼

5 樹和二叉樹

5.1 二叉樹儲存結構
  • 順序儲存結構

陣列, 利用下標定址

typedef TElemType SqBiTree[MAX_TREE_SIZE];複製程式碼
  • 鏈式儲存結構
typedef struct BiTNode {
    TElemType data;
    struct BiTNode * lchild;
    struct BiTNode * rchild;
} BiTNode, * BiTree;複製程式碼
5.2 遍歷二叉樹
  • 先序遍歷

根節點 -> 左子樹 -> 右子樹

  • 中序遍歷

左子樹 -> 根節點 -> 右子樹

  • 後序遍歷

左子樹 -> 右子樹 -> 根節點

算數表示式 a + b * (c - d) - e / f

字首表示式-先序遍歷(逆波蘭 : - + a * b - cd / ef)

中綴表示式-中序遍歷(原表示式 : a + b * (c - d) - e / f)

字尾表示式-後續遍歷(逆波蘭式 : abcd - * + ef / -)

5.3 線索二叉樹

儲存比遍歷過程中的節點相關性結果

前驅後繼節點和左右孩子指示

lchild LTag data RTag rchild

LTag 0 : lchild 域指示左孩子 1 : lchild 域指示前驅節點

RTag 0 : rchild 域指示右孩子 1 : rchild 域指示後繼節點

typedef enum PointerTag {Link, Thread};    // 0 : 指標 1 : 線索
typedef struct BiThrNode {
    TElemType data;
    struct BiThrNode * lchild;
    struct BiThrNode * rchild;
    PointerTag LTag;
    PointerTag RTag;
} BiThrNode, * BiThrTree;複製程式碼
5.4 樹和森林
  • 雙親表示法
typedef struct PTNode {
    TElemType data;
    int parent;
} PTNode;
typedef struct {
    PTNode nodes[MAX_TREE_SIZE];
    int r;    // 根的位置
    int n;    // 節點數
} PTree;複製程式碼
  • 孩子表示法
typedef struct CTNode {    // 孩子節點
    struct CTNode * next;
    int child;
} * ChildPtr;
typedef struct {
    TElemType data;
    ChildPtr firstchild;    // 孩子連結串列頭指標
} CTBox;
typedef struct {
    CTBox nodes[MAX_TREE_SIZE];
    int r;    // 根的位置
    int n;    // 節點數
} CTree;複製程式碼
  • 孩子兄弟表示法
typedef struct CSNode {
    ElemType data;
    struct CSNode * firstchild;
    struct CSNode * nextsibling;
} CSNode, * CSTree;複製程式碼
5.5 二叉樹和森林互換
  • 森林轉換成二叉樹

左孩子右兄弟(左右是對二叉樹而言, 孩子兄弟是對森林而言, 下面同理)

  • 二叉樹轉換成森林

左孩子轉換成孩子, 右孩子轉換成兄弟

5.6 樹和森林遍歷
  • 先序遍歷森林
  1. 第一棵樹的根

  2. 先序遍歷第一棵樹中根節點的子樹森林

  3. 先序遍歷除第一棵樹剩餘的樹構成的森林

  • 中序遍歷森林
  1. 中序遍歷第一棵樹中根節點的子樹森林

  2. 第一棵樹的根

  3. 中序遍歷除第一棵樹剩餘的樹構成的森林

7 圖

7.1 圖的儲存結構
  • 陣列表示法
typedef enum {DG, DN, UDG, UDN} GraphKind;    // {有向圖, 有向網, 無向圖, 無向網}
typedef struct ArcCell {
    VRType adj;    // 頂點相關型別. 無權圖 : 1/0 表示相鄰與否 帶權圖 : 權值資訊
    InfoType * info;    // 該弧相關的指標
} ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
typedef struct {
    VertexType vexs[MAX_VERTEX_NUM];    // 頂點向量
    AdjMatrix arcs;    // 鄰接矩陣
    int vexnum;    // 頂點數
    int arcnum;    // 弧數
    GraphKind kind;    // 圖的種類標誌
} MGraph;複製程式碼
  • 鄰接表

表節點

adjvex nextarc info

頭結點

data firstarc
typedef struct ArcNode {
    int adjvex;    // 該弧所指向頂點位置
    struct ArcNode * nextarc;    // 指向下一條弧的指標
    InfoType * info;    // 該弧相關資訊的指標
} ArcNode;
typedef struct VNode {
    VertexType data;    // 頂點資訊
    ArcNode * firstarc;    // 指向第一條依附該頂點的弧的指標
} VNode, AdjList[MAX_VERTEX_NUM];
typedef struct {
    AdjList vertices;
    int vexnum;    // 頂點數
    int arcnum;    // 弧數
    int kind;    // 種類標記
} ALGraph;複製程式碼
  • 十字連結串列

弧節點

tailvex headvex hlink tlink info

頂點節點

data firstin firstout
typedef struct ArcBox {
    int tailvex;    // 該弧的尾頂點位置
    int headvex;    // 該弧的頭頂點位置
    struct ArcBox * hlink;    // 弧頭相同的弧的鏈域
    struct ArcBox * tlink;    // 弧尾相同的弧的鏈域
    InfoType * info;    // 該弧相關資訊指標
} ArcBox;
typedef struct VexNode {
    VertexType data;
    ArcBox * firstin;    // 指向該節點第一條入弧
    ArcBox * firstout;    // 指向該節點第一條出弧
} VexNode;
typedef struct {
    VexNode xlist[MAX_VERTEX_NUM];    // 表頭向量
    int vexnum;    // 有向圖的當前頂點數
    int arcnum;    // 有向圖的當前弧數
} OLGraph;複製程式碼
  • 鄰接多重表

每一條邊用一個節點表示

mark ivex ilink jvex jlink info

每個頂點用一個節點表示

data firstedge
typedef enum {unvisited, visited} VisitIf;
typedef struct EBox {
    VisitIf mark;    // 訪問標記
    int ivex;    // 依附頂點位置
    int jvex;    // 依附頂點位置
    struct EBox * ilink;    // 依附頂點的下一邊
    struct EBox * jlink;    // 依附頂點的下一邊
    InfoType * info;    // 該邊的資訊指標
} EBox;
typedef struct VexBox {
    VertexType data;
    EBox * firstedge;    // 指向第一條依附該頂點的邊
} VexBox;
typedef struct {
    VexBox adjmulist[MAX_VERTEX_NUM];
    int vexnum;    // 無向圖的頂點數
    int edgenum;    // 無向圖的邊數
} AMLGraph複製程式碼
7.2 圖的遍歷
  • 深度優先搜尋

以迷宮為例子(面試中被問到, 印象比較深刻). 深度優先就是一條路走到黑, 所以返回的第一條路徑不保證是最優解.

Boolean visited[MAX];    // 訪問標誌陣列
Status (* VisiteFunc) (int v);    // 韓阿叔變數

void DFSTraverse(Graph G, Status (* Visit)(int v)) {    // 深度優先遍歷
    VisitFunc = Visit;    // 使用全域性變數 VisitFunc, 使 DFS 不必設定函式指標引數
    for (v = 0; v < G.vexnum; ++v) {
        visited[v] = FALSE;    // 訪問陣列標誌初始化
    }
    for (v = 0; v < G.vexnum; ++v) {
        if (!visited[v]) {
            DFS(G, v);    // 對未訪問的頂點呼叫 DFS
        }
    }
}

void DFS(Graph G, int v) {    // 從第 v 個頂點出發遞迴的深度優先遍歷圖 G.
    visited[v] = TRUE;
    VisitFunc(v);    // 訪問第 v 個頂點
    for (w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)) {
        if (!visited[w]) {
            DFS(G, w);    // 對 v 的尚未訪問的鄰接頂點 w 遞迴呼叫 DFS.
        }
    }
}複製程式碼
  • 廣度優先搜尋

有點層序遍歷的意思, 在遍歷完所有情況下(可以進行演算法優化, 對有些情況進行捨棄)可以得出最優解.

void BFSTraverse(Graph G, Status (* Visit) (int v)) {
    for (v= 0; v < G.vexnum; ++v) {
        visited[v] = FALSE;
    }
    InitQueue(Q);
    for (v = 0; v < G.vexnum; ++v) {
        if (!visited[v]) {
            visited[v] = TRUE;
            Visit(v);
            EnQueue(Q, v);
            while (!QueueEmpty(Q)) {
                DeQueue(Q, u);
                for (w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G, u, w)) {
                    // w 為 u 尚未訪問的鄰接頂點
                    if (!visited[w]) {
                        visited[w] = TRUE;
                        Visit(w);
                        EnQueue(Q, w);
                    } // if
                } // for
            } // while
        } // if
    } // for
} // BFSTraverse複製程式碼

// 以下待填

7.3 圖的連通性問題
  • 無向圖的連通分量和生成樹
  • 有向圖的強連通分量
7.4 最小生成樹
  • Prim 演算法 O(n2)

    沒圖戳 wikipedia 或 fitzeng.org
    沒圖戳 wikipedia 或 fitzeng.org

    以點為主: 主要是分為兩個集合, 一個是已加入的節點, 另一個是未加入節點. 在未加入的節點集合中找到一個離已加入集合最近的節點加入. 直至所有節點被加入.

  • Kruskal 演算法 O(eloge)

    沒圖戳 wikipedia 或 fitzeng.org
    沒圖戳 wikipedia 或 fitzeng.org

    以邊為主: 初始條件是把圖的所有邊去除變成 V 個連通圖. 然後每次找一條代價最小的邊加入, 確保每加入一條邊連通圖個數都減少一個(也就是確保無環路)。直至成一個連通圖時就是最小生成樹.

7.5 最短路徑
  • Dijkstra 演算法 O(n3)

    沒圖戳 wikipedia 或 fitzeng.org
    沒圖戳 wikipedia 或 fitzeng.org

    主要是維護一個表和一個已加入路徑集合, 表記錄從原點到每一個點的當前最小權值. 如果已加入路徑集合中的點通過某條路徑對未加入集合中的點的最小權值有影響則更新該節點權值. 最後在每次更新完成後判斷目前未加入集合中的最小權值節點加入集合, 再對該節點的邊所達的節點做如上判斷. 最終可以求出起點到每一個點的所有最短路徑.

  • Floyd 演算法 O(n3)

    主要是判斷經過該點到達的臨時目點的權值和該點目前權值(可以不考慮是否已經經過, 但是迴圈只會掃一遍所以沒有什麼影響)的大小來判斷是否更新權值.

  • 關鍵路徑和拓撲排序 (KeyWord : 鬆弛)

相關文章