資料結構 單鏈表C實現

2021-08-10 18:57:26 字數 3911 閱讀 3430

什麼叫結構體?就是能夠將不同資料型別集合在一起構造乙個新的資料型別的東西,它有乙個注意點就是不能引用自身作為結構體成員,為什麼呢?因為在建立這種型別的結構體變數時計算機無法得知給結構體變數分配多大的記憶體導致編譯器報錯,提示非法操作。那麼為什麼計算機無法給結構體變數分配某個固定記憶體呢?是這樣的,如果你的結構體原先已經存在兩個int型變數了,如果計算機是四位元組對齊的話那麼結構體型別目前已經佔據了8個位元組了,然後又在int型變數後面將結構體本身作為成員使用,那麼你認為計算機是分配多少位元組?是16個(原先的兩個int加上後來的兩個int)?還是說結構體裡套結構體就這樣一直套下去?顯然兩種都是不正確的,因為計算機無法繼續進行下去了所以只能報錯提示操作不合法了。但是,結構體雖然不能引用自身作為成員變數,而它卻可以引用自身結構體指標變數作為新成員。為什麼這就可以了呢?因為不管哪種指標變數占用的記憶體大小都是4個位元組所以計算機能夠正確的分配記憶體,這樣就引出了今天的主題----鍊錶。

什麼叫鍊錶?它由許多節點(包括頭結點和資料節點)構成。每個節點的特點就是它本身是乙個結構體變數,但是他的成員包括資料域和指標域,資料域存放資料內容,指標域就存放指向其他節點。鍊錶有乙個好處就是事先無需知道資料量,它可以實時動態分配節點記憶體空間並高效的在任意節點處插入新節點或刪除節點。鍊錶分為帶頭結點的和不帶有節點的,單向的,雙向的,迴圈的。下面看看乙個簡單是鍊錶是如何用圖示表示出來的。

typedef void linklist;

//節點指標域定義

typedef struct _tag_linklistnode

linklistnode;

typedef struct _tag_linklisttlinklist;

typedef struct value

val;

這就是定義乙個節點的內容。

這裡值得說一下的是在節點中將指標域單獨拿出來定義,並作為節點的第乙個成員,這樣結構體變數的位址就和指標域的位址相同,指向結構體就相當於直接指向了該節點。這樣做的好處是加強了通用性,鍊錶就可以連線不同資料型別的節點,是不是對資料的封裝進一步感覺到驚嘆。

鍊錶就是由一些包含資料域和指標域的節點構成的,當前節點的指標域指向下乙個節點,最後乙個節點的指標域就指向null,就是這樣不斷連線構建了長度隨時可變的鍊錶,相比於線性表它有更大的靈活性,不會浪費任何空間,並且煉表頭節點的資料域還有個特殊的功能,就是記錄鍊錶的長度。

鍊錶的功能和操作也就是一些增、刪、改、查等等。下面開始正式介紹鍊錶的實現。

1、建立

建立鍊錶即建立乙個頭結點,將length置0,header的next成員指向null。一般盡量在堆區上建立,因為在乙個程式執行時分配的空間中堆區空間十分大,相比於棧的要大得多,並且棧在乙個函式結束後就會釋放函式執行時的活動記錄,而堆雖然有記憶體洩漏的風險,但是一般記住free還是不會發生比較嚴重的錯誤。

**如下:

linklist* linklist_create()

return ret;

}

函式流程也正如我上面所說,就不再解釋。

2、插入

在前面線性表中說到過線性表的插入是比較費勁的,那麼這裡的插入就將是無比輕鬆加愉快的,它不需要移動任何節點,簡單的兩個步驟就能搞定,不多說,上圖看一下:

這裡只需要兩步就能將乙個新節點插入到鍊錶當中。1、將新節點的指標域指向當前節點的指標域2、將當前節點指向新節點。此處注意兩步順序不能交換,否則就會中斷鍊錶。原理是不是很簡單,但是一些小細節還是需要注意的,程式哪怕出了一丁點錯就會提出**。下面看看執行**;

int linklist_insert(linklist* list, linklistnode* node, int pos)

//新節點和原先的節點進行連線

node->next = current->next;

current->next = node;

slist->length++;

}return ret;

}

程式的核心也就是那兩步,首先malloc乙個節點大小的空間,檢驗一些引數的合法性,但是在插入之前需要遍歷一下鍊錶,找到要插入的位置pos,最後將length自增,便於獲取鍊錶的實際長度。

獲取節點的操作總共分為兩步,首先定位到要獲取節點的上乙個位置,再就是直接返回當前節點的指標域即可,即獲取的是乙個指標,這裡需要注意下,除錯列印時需要進行一些強制型別轉換。

3、刪除

刪除的原理相比插入更加簡單,先上圖直觀的看一下再說。

就一步操作,將要刪除的節點的前乙個節點的指標域指向它的下乙個節點的指標域,但是有一點需要注意,刪除後必須將節點釋放掉,不然可能造成記憶體洩漏。

linklistnode* linklist_delete(linklist* list, int pos)

ret = current->next;//節點定位到pos

free(current->next);

current->next = ret->next;//刪除操作

slist->length--;

}return ret;

}

看起來是不是很簡單呢。首先判斷刪除的節點是否合法,不合法就沒有執行下去的意義了,只有直接退出了,然後是定位節點,刪除節點。

4、逆置

最後乙個大問題,鍊錶的逆置,我覺得這個問題還是有一定難度的,我試著解釋清楚。

逆置就是將鍊錶資料節點的順序顛倒一下,而且是乙個節點乙個節點的逆置,首先我們知道,空表不需要逆置,乙個節點不需要逆置,只有當節點數大於等於二才有意義。

具體圖如下;

所以我們就需要乙個逆置的起始狀態,若phead表示頭結點,ppre表示前乙個節點,pcur表示當前節點。那麼,

ppre = phead->next;

pcur = phead->next->next;

就是從這個初始狀態開始逆置,具體逆置原理就是不斷的讓pcur指向ppre,然後讓pcur和ppre往右移動乙個位置 ,直到pcur為null代表交換結束。

具體是實現**如下:

int linklist_reverse(linklist *list)

//起始狀態

ppre = phead->next;//第乙個節點

pcur = phead->next->next;//第二個節點

//開始逆置

while(pcur)

//頭節點指向尾部節點

(phead->next)->next = null;//第乙個節點的指標域指向null

phead->next = ppre;//當while結束過後ppre指向的是最後乙個節點,然後將頭節點指向ppre

return 0;

}

根據程式的注釋以及結合圖形看差不多應該能理解過程了,最重要的就是逆置的模型需要理解。

直到現在,鍊錶的基本操作也就結束了,實際上逆置的實現可以用雙向鍊錶更好的實現,我們需要學習這個過程來加深鍊錶的成型和使用。

資料結構 單鏈表(C 實現)

單鏈表是一種鏈式訪問的資料結構,用一組位址任意的儲存單元存放線性表中的資料元素。鍊錶中的資料是以結點來表示的,每個結點的構成 元素 資料元素的映象 指標 指示後繼元素儲存位置 元素就是儲存資料的儲存單元,指標就是連線每個結點的位址資料。單鏈表基本操縱的實現 包含頭結點 include define ...

C 實現資料結構 單鏈表

2020年8月7日 周五 天氣晴 不悲嘆過去,不荒廢現在,不懼怕未來 用c 實現了簡單的單鏈錶類,功能包括插入 刪除 查詢相關元素,分離鍊錶等操作。是用vs2019實現的,每個函式的功能都新增了一定注釋,完整工程放在了我的github上,有需要的也可以自取。license gnu general p...

資料結構 單鏈表實現

線性表的鏈式儲存結構的特點是用一組任意的儲存單元儲存線性表的資料元素 這組儲存單元可以是連續的,也可以是不連續的 因此,為了表示每個資料元素與其直接後繼資料元素之間的邏輯關係,對資料元素來說,除了儲存其本身的資訊之外,還需儲存乙個指示其直接後繼的資訊 即直接後繼的儲存位置 這兩部分資訊組成資料元素的...