1. 程式人生 > 實用技巧 >C語言簡單實現線性表的鏈式儲存結構(單鏈結構)

C語言簡單實現線性表的鏈式儲存結構(單鏈結構)

為了方面使用,先定義標頭檔案ElemType

#include "stdio.h"
typedef double ElemType;

//操作成功
#define OK 1
//操作錯誤
#define ERROR 0
//操作異常
#define OVERFLOW -2
//定義元素型別,int可使用基本資料型別和使用者自定義資料型別替換,根據使用場景選擇
typedef int Status ;

double absForDouble(double a);

/**
 * 求絕對值
 * @param a
 * @return 返回a的絕對值
 */
double absForDouble(double a) {
    
if (a < 0)return -a; return a; } /** * 實現兩個ElemType型別變數的匹配 * @param e1 ElemType型別變數e1 * @param e2 ElemType型別變數e2 * @return 匹配結果,1表示匹配,0表示不匹配 */ int match(ElemType e1, ElemType e2){ if(absForDouble(e1-e2)<1e-6) return 1; return 0; } /** * 列印資料元素 */ printElem(ElemType e){ printf(
"Current element's data is %lf!\n",e); }

實現單鏈表的定義

#include "ElemType.h"
#include "stdlib.h"
#include<stdio.h>

/**
 * 線性表的鏈式儲存結構
 * 什麼是線性表的鏈式儲存結構?
 * 使用一組任意的儲存單元儲存線性表的資料元素,這些儲存單元可以是連續的,也可以是不連續的,而為了表現線性表中各元素的直接後繼關係,在資料元素的基礎上
 * 額外的使用指標來儲存當前資料元素的直接後繼的位置資訊,這樣的包含了資料元素的資料和資料元素的直接後繼位置資訊的資料結構稱之為結點,而由n個結點連結
 * 成一個連結串列,即為線性表的鏈式儲存結構。一般的,把線性表的鏈式儲存結構簡稱為單鏈表或者線性連結串列,而結點儲存資料元素本身資訊的部分稱之為資料域,而儲存
 * 資料元素的直接後繼的位置資訊的部分稱之為指標域。換而言之,在單鏈表中用以儲存線性表的邏輯關係的是指標域
 
*/ /** * 單鏈表的定義 * 關於LNode* 和LinkedList的區別:一般的,LinkedList表示的是指向單鏈表的頭結點(在首元結點前預設的額外的結點,其資料域不儲存任何資訊)的指標,而LNode*則是指指向單鏈表的結點的指標。 * 頭指標:指向連結串列中第一個結點指標(若不預設頭結點,則指向首元結點,否則指向頭結點) * 首元結點:儲存線性表第一個資料元素的結點 * 為何預設頭結點? * 1、便於首元結點的處理,首元結點作為頭結點的直接後繼 * 2、便於頭指標對空表和非空表作同一處理,無論線性表是否為空表,頭指標都是指向頭結點的非空指標 */ typedef struct LNode { ElemType data; struct LNode *next; } LNode, *LinkedList;

單鏈表基本操作的簡單實現

/**
 * 單鏈表基本操作的實現
 */

/**
 * 初始化
 * 演算法描述:1、新建頭結點,使頭指標L指向頭結點
 * 2、置頭結點的指標域為空(NULL)
 * @param L 指向單鏈表頭指標的指標(實現動態記憶體的傳遞)
 * @return 操作結果狀態碼
 */
Status initLinkedList(LinkedList *L) {
    //新建頭結點,使頭指標L指向頭結點
    *L = (LNode *) malloc(sizeof(LNode));
    //新建結點失敗
    if (!L) return OVERFLOW;
    //置頭結點的指標域為空(NULL)
    (*L)->next = NULL;
    (*L)->data = 0;
    return OK;
}

/**
 * 銷燬單鏈表
 * 演算法描述:
 * 1、判斷指向頭指標的指標L指向的指標是否指向NULL
 * 2、若L指向NULL,則單鏈表已被銷燬,或者單鏈表為初始化,無法銷燬,返回ERROR
 * 3、若L不指向NULL,定義指向當前結點的指標p並使其指向頭結點
 * 4、定義臨時指標t指向NULL
 * 5、使用迴圈銷燬結點,首先使指標t指向指向當前結點的指標p指向的結點的直接後繼,銷燬指標p指向的結點,使p指向指標t指向的結點,若p!=NULL,再次迴圈
 * 6、置指標L,p,t指向NULL
 * 7、返回OK
 * @param L 指向單鏈表頭結點的指標L
 * @return 操作結果狀態碼
 */
Status destroyLinkedList(LinkedList *L) {
    //判斷指向頭指標的指標L指向的指標是否指向NULL
    //L指向NULL,則單鏈表已被銷燬,或者單鏈表為初始化,無法銷燬,返回ERROR
    if (!(*L)) {
        return ERROR;
    }
    //若L不指向NULL,定義指向當前結點的指標p並使其指向頭結點
    LNode *p = *L;
    //定義臨時指標t指向NULL
    LNode *t = NULL;
    while (p) {
        //使指標t指向指向當前結點的指標p指向的結點的直接後繼
        t = p->next;
        //銷燬指標p指向的結點
        free(p);
        //使p指向指標t指向的結點
        p = t;
    }
    //置指標L,p,t指向NULL
    t = NULL;
    p = NULL;
    L = NULL;
    ////返回OK
    return OK;
}

/**
 * 清空單鏈表
 * 演算法描述:
 * 1、判斷指向頭指標的指標L指向的指標是否指向NULL
 * 2、若L指向NULL,則單鏈表已被銷燬,或者單鏈表為初始化,無法清空,返回ERROR
 * 3、若L不指向NULL,定義指向當前結點的指標p並使其指向首元結點
 * 4、定義臨時指標t指向NULL
 * 5、使用迴圈銷燬結點,首先使指標t指向指向當前結點的指標p指向的結點的直接後繼,銷燬指標p指向的結點,使p指向指標t指向的結點,若p!=NULL,再次迴圈
 * 6、置指標p,t指向NULL
 * 7、置頭結點的指標域為NULL
 * 8、返回OK
 * @param L 指向單鏈表的頭指標的指標
 * @return 操作結果狀態碼
 */
Status clearLinkedList(LinkedList *L) {
    //判斷指向頭指標的指標L指向的指標是否指向NULL
    //L指向NULL,則單鏈表已被銷燬,或者單鏈表為初始化,無法清空,返回ERROR
    if (!(*L)) {
        return ERROR;
    }
    //若L不指向NULL,定義指向當前結點的指標p並使其指向頭結點
    LNode *p = (*L)->next;
    //定義臨時指標t指向NULL
    LNode *t = NULL;
    while (p) {
        //使指標t指向指向當前結點的指標p指向的結點的直接後繼
        t = p->next;
        //銷燬指標p指向的結點
        free(p);
        //使p指向指標t指向的結點
        p = t;
    }
    //置頭結點的指標域為NULL
    (*L)->next = NULL;
    //置指標p,t指向NULL
    t = NULL;
    p = NULL;
    ////返回OK
    return OK;
}

/**
 * 判斷單鏈表是否為空表
 * 演算法描述:
 * 1、判斷指標L的合法性(是否等於NULL),若等於NULL,置res指向的儲存單元的值為0,返回ERROR
 * 1、只需更加頭指標L的指標域即可判斷,若L->next=NULL,則單鏈表是空表,若L->next!=NULL,則單鏈表不是空表,綜上返回L指標域值的取反值即可
 * @param L 指向單鏈表的頭指標L
 * @param res 指向儲存單鏈表是否為空表的資訊的儲存單元的指標res
 * @return 操作結果狀態碼
 */
Status isEmpty(LinkedList L, int *res) {
    if (!L) {
        *res = 0;
        return ERROR;
    }
    //只需更加頭指標L的指標域即可判斷,若L->next=NULL,則單鏈表是空表,若L->next!=NULL,則單鏈表不是空表,綜上返回L指標域值的取反值即可
    *res = !L->next;
    return OK;
}

/**
 * 獲取單鏈表的長度
 * 演算法描述:
 * 1、判斷頭指標L是是否等於NULL,若等於NULL,表示單鏈表未初始化或已經被銷燬,置指標len指向的儲存單元的值為-1,返回ERROR
 * 2、定義指向當前結點的指標p並使其指向單鏈表的頭結點
 * 3、定義計數器j記錄指標p由頭結點移動至尾結點需要移動的次數,即單鏈表的長度,置j初始值為零
 * 4、迴圈移動指標p的位置並使計數器自增,直至p=NULL(表示指標p指向尾結點的後繼)
 * 5、置指標len指向儲存單元的值為變數j的值
 * 6、使指標p指向NULL,返回OK
 * @param L 指向單鏈表的頭結點的頭指標L
 * @param len 指向用以儲存單鏈表長度的指標len
 * @return
 */
Status getLength(LinkedList L, int *len) {
    //判斷頭指標L是是否等於NULL,若等於NULL,表示單鏈表未初始化或已經被銷燬,置指標len指向的儲存單元的值為-1,返回ERROR
    if (!L) {
        *len = -1;
        return ERROR;
    }
    //定義指向當前結點的指標p並使其指向單鏈表的頭結點
    LNode *p = L->next;
    //定義計數器j記錄指標p由頭結點移動至尾結點需要移動的次數,即單鏈表的長度,置j初始值為零
    int j = 0;
    //迴圈移動指標p的位置並使計數器自增,直至p=NULL(表示指標p指向尾結點的後繼)
    while (p) {
        p = p->next;
        ++j;
    }
    //置指標len指向儲存單元的值為變數j的值
    *len = j;
    //返回OK
    p = NULL;
    return OK;

}

/**
 * 取值,取單鏈表的第i個元素(i>0且i<n+1)
 * 演算法描述:使指向當前結點的指標p指向首元結點,用計數器j計數,j初始值為1,從首元結點開始依次訪問單鏈表的結點,直至指向當前結點的指標p的指標域為NULL
 * 或者計數器為i(即訪問到了序號為p的結點),否則不斷進行如下迴圈:
 * 1、p指向下一結點
 * 2、J自增
 * 當上述迴圈結束時,若計數器小於i,返回ERROR
 * 否則,使用引數e儲存序號為i的結點的資料域並返回OK表示取值成功
 * @param L 指向頭指標的指標
 * @param i 欲訪問元素的結點序號
 * @param e 指向儲存序號為i的結點的資料域的資料元素的指標
 * @return 操作結果狀態碼
 */
Status getElem(LinkedList L, int i, ElemType *e) {
    //使指向當前結點的指標p指向首元結點
    LNode *p = L->next;
    //用計數器j計數,j初始值為1
    int j = 1;
    //從首元結點開始依次訪問單鏈表的結點,直至指向當前結點的指標p的指標域為NULL或者計數器為i(即訪問到了序號為p的結點)
    while (j < i && p) {
        p = p->next;
        ++j;
    }
    //若!p為真,說明p當前指向NULL,即指向最後一個結點指標域指向的結點即第n+1個結點,說明i>n,i非法,返回ERROR
    //若j > i,由於j初始值為1,j>0,故得i<=0,i非法,返回ERROR
    if (!p || j > i) return ERROR;
    //否則,使用引數e儲存序號為i的結點的資料域並返回OK表示取值成功
    *e = p->data;
    p = NULL;
    //返回OK
    return OK;
}

/**
 *  定位單鏈表中與e匹配的資料元素的位置
 * 演算法描述:
 * 1、定義指向當前結點的指標p,令其指向首元結點
 * 2、遍歷單鏈表直至存在找到資料欲和e匹配的結點
 * 3、若p==NULL,條件表示式p為假,退出迴圈,說明查詢到了最後一個結點後的結點,計數器等於n+1(n為單鏈表的長度),查詢失敗,將-1賦值給指標i指向的記憶體,返回ERROR
 * 4、若查詢成功,此時計數器恰好等於該節點在單鏈表的序號,將計數器的值賦值給指標i指向的記憶體,返回OK
 * @param L 指向單鏈表頭指標的指標
 * @param i 指標傳參,用以儲存查詢結果
 * @param e 欲查詢的資料元素
 * @return 操作結果狀態碼
 */
Status indexOf(LinkedList L, int *i, ElemType e) {
    //定義指向當前結點的指標p,令其指向首元結點
    LNode *p = L->next;
    //定義計數器j = 1
    int j = 1;
    //遍歷單鏈表直至存在找到資料欲和e匹配的結點
    //當p==NULL時條件表示式p為假,退出迴圈,說明查詢到了最後一個結點後的結點,計數器等於n+1(n為單鏈表的長度),查詢失敗
    //當absForDouble(p->data-e)<1e-6(ElemType是浮點數,不能使用等於符號),說明查詢成功,此時計數器恰好等於該節點在單鏈表的序號
    /*printf("%lf %lf\n",p->data,e);
    printf("%d\n",p);
    printf("%d\n",p&&(p->data-e)>1e-6);*/
    while (p && absForDouble(p->data - e) > 1e-6) {
        p = p->next;
        ++j;
    }
    //若j > n,說明沒有找到匹配元素,置i值為-1,返回ERROR,表示查詢失敗
    if (!p) {
        *i = -1;
        return ERROR;
    }
    //否則j <= n,查詢到匹配元素, 置i值為計數器j的值
    *i = j;
    //返回OK,表示查詢成功
    p = NULL;
    return OK;
}

/**
 * 查詢單鏈表L中資料域與給定資料e匹配的結點,並返回該結點的地址
 * 演算法描述:
 * 1、定義指向當前結點的指標,使其指向單鏈表L的首元結點
 * 2、從首元結點開始遍歷單鏈表L,依次用每個結點的資料域匹配給定資料e,直至指向當前結點的指標p指向NULL或匹配成功
 * 3、返回指向當前結點的指標p
 * @param L 單鏈表的頭指標
 * @param e 欲查詢的資料域
 * @return p 返回查詢與給定資料域匹配的結點的地址
 */
LNode *locateElem(LinkedList L, ElemType e) {
    //定義指向當前結點的指標,使其指向單鏈表L的首元結點
    LNode *p = L->next;
    //從首元結點開始遍歷單鏈表L,依次用每個結點的資料域匹配給定資料e,直至指向當前結點的指標p指向NULL或匹配成功
    while (p && absForDouble(p->data - e) > 1e-6) {
        p = p->next;
    }
    //若p指向NULL說明查詢失敗
    //否則查詢成功
    //返回指向當前結點的指標p
    return p;
}

/**
 * 獲取前趨結點
 * 獲取單鏈表上資料域等於curE的第一個結點的前趨結點,若該結點無前趨結點,置priorNode指向的記憶體單元的值為NULL
 * 演算法描述:
 * 1、判斷頭指標L是否合法,若不合法,置priorNode指向的記憶體單元的值為NULL,返回ERROR
 * 2、定義指標curNode指向資料域等於curE的第一個結點的指標,使用locateElem()函式
 * 3、判斷curNode是否等於L->next,若是,curNode為首元結點,無前趨,置priorNode指向的記憶體單元的值為NULL,返回ERROR
 * 4、定義指向當前結點的指標p並指向單鏈表的頭結點
 * 5、迴圈移動指標p直至p->next=curNode
 * 6、指標p指向的結點為curNode的前趨,把指標p的值賦值給priorNode指向的記憶體單元
 * 7、置指標curNode,p為NULL,返回OK
 * @param L 指向單鏈表頭結點的頭指標L
 * @param curE 當前結點資料域
 * @param priorNode 指向資料域等於curE的上一個結點的指標的指標priorNode
 * @return 操作結果狀態碼
 */
Status priorElem(LinkedList L, ElemType curE, LNode **priorNode) {
    //判斷頭指標L是否合法,若不合法,置priorNode指向的記憶體單元的值為NULL,返回ERROR
    if (!L) {
        *priorNode = NULL;
        return ERROR;
    }
    //定義指標curNode指向資料域等於curE的第一個結點的指標,使用locateElem()函式
    LNode *curNode = locateElem(L, curE);
    //判斷curNode是否等於L->next,若是,curNode為首元結點,無前趨,置priorNode指向的記憶體單元的值為NULL,返回ERROR
    if (curNode == L->next) {
        *priorNode = NULL;
        return ERROR;
    }
    //定義指向當前結點的指標p並指向單鏈表的頭結點
    LNode *p = L;
    //迴圈移動指標p直至p->next=curNode
    while (p->next != curNode) {
        p = p->next;
    }
    //指標p指向的結點為curNode的前趨,把指標p的值賦值給priorNode指向的記憶體單元
    *priorNode = p;
    //置指標curNode,p為NULL,返回ERROR
    curNode = NULL;
    p = NULL;
    return OK;
}

/**
 * 獲取後繼結點
 * 獲取資料域為curE的結點的後繼結點,若當前結點無後繼結點,置nextNode指向的記憶體單元值為NULL,返回ERROR
 * 演算法描述:
 * 1、判斷頭指標L是否合法,若不合法,置nextNode指向的記憶體單元的值為NULL,返回ERROR
 * 2、定義指向資料域為curE的結點的指標curNode(使用locateElem()函式)
 * 3、判斷curNode->next是否為NULL,若為NULL,則curNode指向的結點為尾結點,無後繼,置nextNode指向的記憶體單元為NULL,返回ERROR
 * 4、令指標nextNode指向的指標指向curNode指標域指向的結點(即把curNode->next的值賦值給指標nextNode指向的記憶體單元)
 * 5、置指標curNode為NULL,返回OK
 * @param L
 * @param curE
 * @param nexrNode
 * @return
 */
Status nextElem(LinkedList L, ElemType curE, LNode **nexrNode) {
    //判斷頭指標L是否合法,若不合法,置nextNode指向的記憶體單元的值為NULL,返回ERROR
    if (!L) {
        *nexrNode = NULL;
        return ERROR;
    }
    //定義指向資料域為curE的結點的指標curNode(使用locateElem()函式)
    LNode *curNode = locateElem(L, curE);
    //判斷curNode->next是否為NULL,若為NULL,則curNode指向的結點為尾結點,無後繼,置nextNode指向的記憶體單元為NULL,返回ERROR
    if (curNode->next == NULL) {
        *nexrNode = NULL;
        return ERROR;
    }
    //令指標nextNode指向的指標指向curNode指標域指向的結點(即把curNode->next的值賦值給指標nextNode指向的記憶體單元)
    *nexrNode = curNode->next;
    //置指標curNode為NULL,返回OK
    curNode = NULL;
    return OK;
}

/**
 * 插入結點到單鏈表
 * 演算法描述:將資料域為e的結點插入到單鏈表的位置i上,即插入到第i-1個結點和第i-1個結點之間(i>0且i<n+2,n是單鏈表的當前長度),具體操作如下:
 * 1、查詢第i-1個結點並使用指向當前結點的指標p指向該結點
 * 2、生成一個新的結點s
 * 3、置新結點s的資料域為e
 * 4、使s的指標域指向結點第i個結點
 * 5、使p指向的結點的指標域指標新結點s
 * 注意4、5兩步的次序,必須通過第i-1個結點來訪問第i個結點,因此必須先讓新結點指向第i個結點,再讓第i個結點指向新結點,否則無法訪問第i個結點
 * @param L 指向頭指標的指標
 * @param i 欲插入的位置
 * @param e 插入的結點的資料域
 * @return 操作結果狀態碼
 */
Status insertLNode(LinkedList *L, int i, ElemType e) {
    //使當前結點指向頭結點
    LNode *p = (*L);
    //定義計數器j
    int j = 0;
    //查詢第i-1個結點並使用指向當前結點的指標p指向該結點,若j >= i-1或者當前結點的指標為NULL迴圈終止
    while (j < i - 1 && p) {
        p = p->next;
        ++j;
    }
    //如果i > n+1 或者 i < 1,i非法,返回ERRPR
    if (!p || j > i - 1)return ERROR;
    //生成一個新的結點s
    LNode *s = (LNode *) malloc(sizeof(LNode));
    //置新結點s的資料域為e
    s->data = e;
    //使s的指標域指向結點第i個結點
    s->next = p->next;
    //使p指向的結點的指標域指標新結點s
    p->next = s;
    s = NULL;
    p = NULL;
    return OK;
}

/**
 * 刪除單鏈表L上序號為i的結點,即刪除單鏈表中序號為i-1和i+1之間的結點
 * 演算法描述:
 * 1、定義指向當前結點的指標p,使其指向單鏈表的頭結點
 * 2、定義計數器j = 0
 * 3、查詢並使當前指向當前結點的指標序號為i-1的結點,修改計數器j的值,若i>n或i<1,返回ERROR,表示刪除失敗
 * 4、定義指標變數t(temp,臨時指標變數)指向當前結點的下一結點(即序號為i的結點,亦即待刪除的結點)
 * 5、使指向當前結點(序號為i-1的結點)的指標p指向的結點的指標域指向指標t的指標域指向的結點(序號為i+1的結點)
 * 6、釋放指標變數t指向的結點所佔用的記憶體
 * 7、使指標p、t指向NULL;
 * 8、返回OK,表示刪除成功
 * @param L
 * @param i
 * @return
 */
Status deleteNode(LinkedList *L, int i) {
    //定義指向當前結點的指標p,使其指向單鏈表的頭結點
    LNode *p = *L;
    //定義計數器j = 0
    int j = 0;
    //查詢並使當前指向當前結點的指標序號為i-1的結點,修改計數器j的值,若i>n或i<1,返回ERROR,表示刪除失敗
    //
    while (p->next && j < i - 1) {
        p = p->next;
        ++j;
    }
    //p->next = NULL 表示序號為i-1的結點為最後一個結點,即序號i的結點是第n+1個結點,顯然該結點不存在 > n,i值不合法,返回ERROR
    //j > i -1表示 i < 1,i值不合法,返回ERROR
    if (!(p->next) || j > i - 1) {
        return ERROR;
    }
    //定義指標變數t(temp,臨時指標變數)指向當前結點的下一結點(即序號為i的結點,亦即待刪除的結點)
    LNode *t = p->next;
    //使指向當前結點(序號為i-1的結點)的指標p指向的結點的指標域指向指標t的指標域指向的結點(序號為i+1的結點)
    p->next = t->next;
    //釋放指標變數t指向的結點所佔用的記憶體
    free(t);
    //使指標p、t指向NULL;
    t = NULL;
    p = NULL;
    //返回OK,表示刪除成功
    return OK;

}

/**
 * 構造單鏈表
 * 使用前插法建立帶有n個結點的單鏈表
 * 前插法:通過將新結點逐個插入連結串列的頭部(頭結點之後)來建立連結串列,每次申請一個新結點,讀入相應的元素值,然後將新結點插入到頭結點之後
 * 演算法描述:
 * 1、為頭結點申請動態記憶體,將動態記憶體的指標賦值給指標L指向頭的指標,若申請失敗,則返回OVERFLOW
 * 2、使用迴圈申請新結點的動態記憶體,若申請失敗,返回OVERFLOW,若申請成功,給新建的資料域賦值並將新結點插入到頭節點之後
 * 3、返回OK
 *
 *
 * @param L 指向單鏈表頭指標的指標L
 * @param n 插入新結點的個數n
 * @return  操作結果狀態碼
 */
Status createLinkedListByInsertNewNodeAfterHeadNode(LinkedList *L, int n) {
    //為頭結點申請動態記憶體,將動態記憶體的指標賦值給指標L指向頭的指標
    *L = (LNode *) malloc(sizeof(LNode));
    //若申請失敗,則返回OVERFLOW
    if (!*L)return OVERFLOW;
    (*L)->next = NULL;
    (*L)->data = 0;
    int i = 0;
    for (i = 0; i < n; ++i) {
        //申請新結點的動態記憶體
        LNode *p = (LNode *) malloc(sizeof(LNode));
        //若申請失敗,返回OVERFLOW
        if (!p) return OVERFLOW;
        //若申請成功,給新建的資料域賦值並將新結點插入到頭結點之後
        p->data = i + 1;
        p->next = (*L)->next;
        (*L)->next = p;
        p = NULL;
    }
    //返回OK
}

/**
 * 構造單鏈表
 * 使用後插法構造具有n個結點的單鏈表
 * 後插法:通過將新結點逐個插入到連結串列的尾部來建立連結串列。每次申請一個結點,讀取相應的資料元素的值,為了使新結點能夠插入到連結串列的尾部,需要增加一個尾指標r指向連結串列的尾結點
 * 演算法描述:
 * 1、為頭結點申請動態記憶體,將動態記憶體的指標賦值給指標L指向頭的指標,若申請失敗,則返回OVERFLOW
 * 2、定義尾指標r並使r指向頭結點(即將頭指標的值賦值給尾指標r)
 * 3、使用迴圈申請新結點的動態記憶體,若申請失敗,返回OVERFLOW,若申請成功,給新建的資料域賦值並將新結點插入到尾結點之後,將尾指標r指向新插入的結點
 * 4、返回OK
 * @param L 指向單鏈表頭指標的指標L
 * @param n 插入新結點的個數n
 * @return 操作結果狀態碼
 */
Status createLinkedListByInsertNewNodeAfterRearNode(LinkedList *L, int n) {
    //為頭結點申請動態記憶體,將動態記憶體的指標賦值給指標L指向頭的指標
    *L = (LNode *) malloc(sizeof(LNode));
    //若申請失敗,則返回OVERFLOW
    if (!(*L)) return OVERFLOW;
    (*L)->next = NULL;
    (*L)->data = 0;
    //定義尾指標r並使r指向頭結點(即將頭指標的值賦值給尾指標r)
    LNode *r = *L;
    int i = 0;
    for (i = 0; i < n; i++) {
        //申請新結點的動態記憶體
        LNode *p = (LNode *) malloc(sizeof(LNode));
        //若申請失敗,返回OVERFLOW
        if (!p)return OVERFLOW;
        //若申請成功,給新建的資料域賦值並將新結點插入到尾結點之後
        p->data = i + 1;
        p->next = NULL;
        r->next = p;
        //將尾指標r指向新插入的結點
        r = p;
        p = NULL;
    }
    r = NULL;
    return OK;
}

/**
 * 遍歷單鏈表
 * 遍歷單鏈表,依次輸出每個結點的資料域的值
 * 演算法描述:
 * 1、判斷指標L是否合法,若不合法,返回ERROR
 * 2、定義指向當前結點的指標p並使p指向首元結點
 * 3、迴圈移動指標p並輸出p指向的結點的資料域的值直至p=NULL(表示p指向尾結點的後繼,即遍歷結束)
 * 4、置指標p為NULL,返回OK
 * @param L 指向單鏈表的頭結點的頭指標
 * @return 操作結果狀態碼
 */
Status traverseList(LinkedList L) {
    //判斷指標L是否合法,若不合法,返回ERROR
    if(!L){
        return ERROR;
    }
    //定義指向當前結點的指標p並使p指向首元結點
    LNode *p = L->next;
    //迴圈移動指標p並輸出p指向的結點的資料域的值直至p = NULL(表示p指向尾結點的後繼,即遍歷結束)
    while (p){
        printElem(p->data);
        p = p->next;
    }
    //置指標p為NULL,返回OK
    //迴圈結束的條件就是p = NULL 故可以省略下一條語句
    p = NULL;
    return OK;
}

測試定義好的單鏈表和其基本操作的功能

int main() {
    LinkedList L = NULL;
    /*initLinkedList(&L);
    double d;
    if (insertLNode(&L, 1, 2.2)) {
        if (insertLNode(&L, 2, 3)) {
            if (getElem(&L, 1, &d)) {
                printf("%lf\n", d);
            }
        }
    }
    int i;
    d = 3.0;
    if (indexOf(L, &i, d)) {
        printf("%d\n", i);
    }
    LNode *p = locateElem(L, d);
    //查詢成功,使用結點地址訪問結點資料域
    //查詢失敗,輸出結點地址,驗證是否為NULL
    if (p) {
        printf("%lf\n", p->data);
    } else {
        printf("%d\n", p);
    }
    deleteNode(&L, 4);
    d = 3.0;
    if (indexOf(L, &i, d)) {
        printf("%d\n", i);
    } else {
        printf("%d\n", i);
    }*/

    //測試前插法
    /*if (createLinkedListByInsertNewNodeAfterHeadNode(&L, 10)) {
        double d;
        if (getElem(L, 7, &d)) {
            printf("%lf\n", d);
        }
    }*/

    //測試後插法
    if (createLinkedListByInsertNewNodeAfterRearNode(&L, 10)) {
        double d;
        if (getElem(L, 7, &d)) {
            printf("%lf\n", d);
        }
    }
    //測試銷燬單鏈表
    /*destroyLinkedList(&L);*/

    //測試獲取前趨和後繼
    //測試前趨
    double curE = 1;
    LNode *prior = NULL;
    priorElem(L, curE, &prior);
    if (prior) {
        printf("The data of prior node of node which data is curE is %lf\n", prior->data);
    } else {
        printf("The node whose data i curE has not prior node!\n");
    }
    //測試後繼
    LNode *next = NULL;
    curE = 9;
    nextElem(L, curE, &next);
    if (next) {
        printf("The data of next node of node which data is curE is %lf\n", next->data);
    } else {
        printf("The node whose data i curE has not next node!\n");
    }

    //測試遍歷單鏈表
    traverseList(L);
    //測試清空單鏈表
    //測試判斷單鏈表是否為空表
    int res = 0;
    isEmpty(L, &res);
    if (res) {
        printf("The LinkedList is a empty table.\n");
    } else {
        printf("The LinkedList isn't a empty table.\n");
    }
    //測試獲取單鏈表長度
    int length = 0;
    getLength(L, &length);
    if (length < 0) {
        printf("The LinkedList has not been initialized or the LinkedList has been distroyed!\n");
    } else {
        printf("The LinkedList's length is %d!\n", length);
    }
    clearLinkedList(&L);
    //測試判斷單鏈表是否為空表
    isEmpty(L, &res);
    if (res) {
        printf("The LinkedList is a empty table.\n");
    } else {
        printf("The LinkedList isn't a empty table.\n");
    }
    //測試獲取單鏈表長度
    getLength(L, &length);
    if (length < 0) {
        printf("The LinkedList has not been initialized or the LinkedList has been distroyed!\n");
    } else {
        printf("The LinkedList's length is %d!\n", length);
    }
    if (L) {
        printf("The LinkedList has not been destroyed!");
        free(L);
    }
    next = NULL;
    prior = NULL;
    L = NULL;
    return 0;
}

之後可能會實現雙鏈結構吧:)