計(jì)算機(jī)編程內(nèi)功修練 --> 數(shù)據(jù)結(jié)構(gòu)之單鏈表

前言

上篇文章學(xué)習(xí)了線性表的順序存儲(chǔ)結(jié)構(gòu),不過(guò),在代碼的實(shí)現(xiàn)過(guò)程中,發(fā)現(xiàn)了順序表的一個(gè)很大的問(wèn)題:插入和刪除需要移動(dòng)大量的數(shù)據(jù)元素,那如何解決這個(gè)問(wèn)題?

鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)

前篇文章有談到過(guò)線性表的存儲(chǔ)方式,一種是順序存儲(chǔ)結(jié)構(gòu),另一種是鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)。我們?cè)賮?lái)回顧一下鏈?zhǔn)酱鎯?chǔ)的定義

為了表示每個(gè)數(shù)據(jù)元素與其直接后繼元素之間的邏輯關(guān)系,每個(gè)元素除了存儲(chǔ)本身的信息外,還需要存儲(chǔ)指示其直接后繼的信息

鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu).png

單鏈表

上面的定義有提到邏輯關(guān)系,有一種常見(jiàn)的鏈?zhǔn)酱鎯?chǔ)邏輯結(jié)構(gòu)可以體現(xiàn):單鏈表

n個(gè)結(jié)點(diǎn)鏈接成一個(gè)鏈?zhǔn)骄€性表的結(jié)構(gòu)叫做鏈表,當(dāng)每個(gè)結(jié)點(diǎn)中包含一個(gè)指針域時(shí),叫做單鏈表

鏈表.png

鏈表的基本概念

上面的定義有提到鏈表,一個(gè)鏈表一般可以分為三個(gè)部分:

  • 表頭結(jié)點(diǎn)

    鏈表中的第一個(gè)結(jié)點(diǎn),包含指向第一個(gè)數(shù)據(jù)元素的指針以及鏈表自身的一些信息

  • 數(shù)據(jù)結(jié)點(diǎn)

    鏈表中代表數(shù)據(jù)元素的結(jié)點(diǎn),包含指向下一個(gè)數(shù)據(jù)元素的指針和數(shù)據(jù)元素的信息

  • 尾結(jié)點(diǎn)

    鏈表中的最后一個(gè)數(shù)據(jù)結(jié)點(diǎn),其下一個(gè)元素指針為空,也就是指針域?yàn)榭?,表示無(wú)后繼

單鏈表結(jié)構(gòu)圖

單鏈表.png

實(shí)現(xiàn)單鏈表

在頭文件中聲明需要實(shí)現(xiàn)單鏈表的操作

typedef void LinkList;
typedef struct _tag_LinkListNode LinkListNode;
/**在C語(yǔ)言中可以用結(jié)構(gòu)體來(lái)定義鏈表中指針域*/
struct _tag_LinkListNode {

    /**指向下個(gè)元素的指針*/
    LinkListNode *next;
};

/**數(shù)據(jù)元素結(jié)點(diǎn)*/
typedef struct Value {

    LinkListNode next;
    int value;
} TValue;

LinkList *createLinkList();

void destroyLinkList(LinkList *list);

void clearLinkList(LinkList *list);

int getLinkListLen(LinkList *list);

int addLinkListEle(LinkList *list, LinkListNode *node, int position);

LinkListNode *getLinkListEle(LinkList *list, int position);

LinkListNode *deleteLinkListEle(LinkList *list,int position);

在實(shí)現(xiàn)的文件中定義頭節(jié)點(diǎn)

/**
 * 定義表頭結(jié)點(diǎn)
 */
typedef struct _tag_LinkList {

    /**指向第一個(gè)元素的頭指針*/
    LinkListNode header;
    /**整個(gè)單鏈表的長(zhǎng)度*/
    int length;

} TLinkList;

單鏈表的添加元素操作

/**
 * 添加元素到單鏈表中指定位置
 * @param list
 * @param node
 * @param position  在鏈表中的索引值
 * @return
 */
int addLinkListEle(LinkList *list, LinkListNode *node, int position) {

    TLinkList *linkList = (TLinkList *) list;
    /**判斷單鏈表、插入的元素以及插入的位置的合法性*/
    int ret = (linkList != NULL) && (node != NULL) && (position >= 0);
    int i;
    if (ret) {
        /** step 1 開(kāi)始位置指向表頭*/
        LinkListNode *current = (LinkListNode *) linkList;
        for (i = 0; i < position && current->next != NULL; i++) {

            /**
             * step 2 由表頭開(kāi)始通過(guò)next指針移動(dòng)position次
             * 第position處的下一個(gè)元素就是要插入的位置,也就
             * 是當(dāng)前元素的current->next。
             */
            current = current->next;
        }
        /**
         * step 3 交換當(dāng)前元素和插入元素的指針域
         * 注:這兩步順序不能搞反,否則會(huì)導(dǎo)致單鏈
         * 表斷鏈
         */
        node->next = current->next;
        current->next = node;

        /**更新單鏈表長(zhǎng)度*/
        linkList->length++;
    }
    return ret;
}

這里的核心算法就是step 2和step 3這兩步。我們可以通過(guò)圖來(lái)加深理解。請(qǐng)注意圖中的第2步與第1步是順序是不能搞反的,否則會(huì)導(dǎo)致斷鏈

插入.png

單鏈表的刪除元素操作

/**
 * 刪除鏈表中指定的元素
 * @param list
 * @param position  被刪除元素在鏈表中的索引值
 * @return
 */
LinkListNode *deleteLinkListEle(LinkList *list, int position) {

    TLinkList *linkList = (TLinkList *) list;
    LinkListNode *node = NULL;
    if (linkList != NULL && position >= 0 && position < linkList->length) {

        LinkListNode *current = (LinkListNode *) linkList;
        /**獲取第position處元素,該元素的next指針域就是需要?jiǎng)h除的元素*/
        for (int i = 0; i < position; i++) {
            current = current->next;
        }
        node = current->next;
        current->next = node->next;
        linkList->length--;

    }
    return node;
}
刪除.png

獲取元素

/**
 * 獲取指定位置的元素
 * @param list
 * @param position 被獲取元素在鏈表中的索引值
 * @return
 */
LinkListNode *getLinkListEle(LinkList *list, int position) {

    TLinkList *linkList = (TLinkList *) list;
    LinkListNode *node = NULL;
    int i;
    if (linkList != NULL && position >= 0 && position < linkList->length) {

        /** step 1 開(kāi)始位置指向表頭*/
        LinkListNode *current = (LinkListNode *) linkList;

        for (i = 0; i < position; i++) {

            /** step 2 由表頭開(kāi)始通過(guò)next指針移動(dòng)position次*/
            current = current->next;

        }
        /** step 3 當(dāng)前元素的next指針即指向要獲取的元素*/
        node = current->next;
    }
    return node;
}

總結(jié)

單鏈表的一些主要操作就都已經(jīng)實(shí)現(xiàn)完了,代碼中注釋很清楚,再加上圖一起來(lái)理解就更非常容易了(完整代碼)。那它與上篇文章學(xué)過(guò)的順序表有哪些優(yōu)缺點(diǎn)了?

  • 優(yōu)點(diǎn):

    • 無(wú)需一次性定制鏈表的容量
    • 插入和刪除無(wú)需移動(dòng)數(shù)據(jù)元素
  • 缺點(diǎn)

    • 數(shù)據(jù)元素必須保存后繼元素的位位置信息
    • 獲取指定數(shù)據(jù)的元素操作需要順序訪問(wèn)之前的元素
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容