當我們在實際做項目,或者是自主開發一點小東西的時候,往往會儲存一些數據,有時候我們需要添加這些數據,有時候需要刪除,而有時候,僅僅只需要查找到就行。鏈表中的每一個節點都是一個獨立開辟的空間,就像一個個的小箱子。那我們要如何將這些箱子串聯起來呢?這時候,鏈表就發揮了作用。一個最基本的節點,應當如下圖組成
typedef char x;
typedef struct SlistNote SlistNote;struct SlistNote
{x data;SlistNote* next;
};
因為我們可能需要不斷的改變數據的類型,所以在最初我們利用typedef來進行重定義。并且因為結構體指針每次創建名都太長,索性直接將名字進行替換
而上圖最重要的,其實是結構體中的內容:1.數據。在這個節點中存放著相應的數據? 2.指向下一個節點的地址。如上圖的next,便是指向下一個節點的地址。
因此,當我們想使用一個鏈表的時候,我們其實只要找到第一個節點的地址就可以了。
那么,我們要如何實現鏈表的初始化,前插后插,前刪后刪,查詢,定點操作呢?讓我們一個一個來。
初始化
SListNote* SltBuyNote(SLT_type x)
{SListNote* newnote = (SListNote*)malloc(sizeof(SListNote));assert(newnote);newnote->data = x;newnote->next = NULL;return newnote;
}
我們想要實現尾插和頭插,需要先把要插入的節點準備好。以上代碼,便是數據的初始化過程。先開辟出一片空間,并將這個空間的地址賦值給newnote這個指針。接著進行assert,避免前面的開辟空間失敗進而導致后續的一系列錯誤。接著將數據進行賦值,暫時先把next指針定義為空指針,防止變成野指針,接著將剛剛創建的這個指針給return了。
尾插
void BackInsert(SListNote** pphead, SLT_type x)
{SListNote* newnote = SltBuyNote(x);if (*pphead == NULL){*pphead = newnote;}else{SListNote* ppend = *pphead;while (ppend->next){ppend = ppend->next;}ppend->next = newnote;}
}
當我們知道如何初始化節點以后,我們便可以開始一些基本的操作,比如尾插。在最開始,我們先創建出一個節點newnote,這個節點內部的數據就是我們要插入的數據。接著,我們判斷一下被插入的節點的是否為空,若為空,則直接將newnote設置為初始節點,若不為空,那便先通過while循環找到ppend,也就是該結構體的末端。接著讓結構體末端的next指針指向newnote,那便完成了尾插。
頭插
void FrontInsert(SListNote** pphead, SLT_type x)
{SListNote* newnote = SltBuyNote(x);if (*pphead == NULL){*pphead = newnote;}else{newnote->next = *pphead;*pphead = newnote;}
}
頭插相比較于尾插要更簡單一些,前面基本相似,在else這邊,我們只需要把新節點的next指針指向*pphead,那其實就已經插上了。但是這樣有一些錯誤,因為此時鏈表的頭部已經發生改變,變成了newnote,因此,我們需要手動把節點地址改為newnote地址。
頭刪
void FrontDel(SListNote** pphead)
{assert(pphead && *pphead);SListNote* pcur = *pphead;if (pcur->next == NULL){free(*pphead);*pphead = NULL;}SListNote* pcur = *pphead;*pphead = pcur->next;free(pcur);
}
頭刪也是比較簡單的,在頭刪函數中,我們首先需要先判斷到底有沒有數據,利用assert函數完成,如果有,再判斷有幾個數據,如果只有一個,那便是將頭地址給刪除,如果多于一個,那就將原先的頭地址賦給一個值做備份,接著將頭地址改變為下一個節點地址,再釋放掉之前的頭地址,即可完成頭刪。
尾刪
void BackDel(SListNote** pphead)
{assert(pphead && *pphead);SListNote* pcur = *pphead;if (pcur->next == NULL){free(*pphead);*pphead = NULL;}else{while (pcur->next->next){pcur = pcur->next;}free(pcur->next);pcur->next->next = NULL;pcur->next = NULL;}
尾刪前面幾部類似于頭刪,如果已經確定有大于1個的數據,那就利用while精確定位到最后一個數據處,接著釋放掉最后一個數據的地址并賦NULL值。
指定位置刪除
void PosDel(SListNote** pphead, int pos)
{int count = 0;SListNote* pcur = *pphead;assert(pphead && *pphead);if (pos == 0){FrontDel(pphead);}else{while (pcur){pcur = pcur->next;count++;}pcur = *pphead;count = 0;while (count < pos - 1){pcur = pcur->next;count++;}SListNote* ppos = pcur->next;pcur->next = pcur->next->next;free(ppos);ppos = NULL;}
}
我們先斷言待刪鏈表不是空鏈表,接著看待刪位置是不是首節點,如果是首節點,那么直接執行頭刪,如果不是,則接著進行下一步。通過while循環找到pos位置處的節點,接著將這個節點的上一個節點的next指針連接到pos位置的下一個節點,接著刪除pos位置處的節點,將其free掉。
指定位置插入
指定位置插入分為前插和后插,不同的插入方式需要不同的代碼來實現
前插:
void FrontPosInsert(SListNote** pphead, int pos, SLT_type x)
{SListNote* newnote = SltBuyNote(x);if (*pphead == NULL){*pphead = newnote;}else{if (pos == 0){FrontInsert(pphead, x);}else{SListNote* pcur = *pphead;int count = 0;while (count != pos-1){pcur = pcur->next;count++;if (pcur == NULL){printf("不在可執行范圍內!");return 0;}}SListNote* ppos = pcur->next;pcur->next = newnote;newnote->next = ppos;}}
}
先創建一個節點newnote,接著判斷鏈條是否為空鏈條,若是空鏈條,則將*pphead直接賦一個newnote,若不是,則開始判斷pos的數值,若pos為0的話,那么就執行前插函數,若pos不為0,則在進行下一步。先找到pos所代表的節點的前一個節點,接著繼續判斷,若該節點已經超過已有的節點,則進行提醒并直接返回,當然,你也可以選擇進行一次后插。若該pos符合以上規則,則開始進行插入。
后插
void BackPosInsert(SListNote** pphead, int pos, SLT_type x)
{SListNote* newnote = SltBuyNote(x);if (*pphead == NULL){*pphead = newnote;}else{SListNote* pcur = *pphead;int count = 0;while (count != pos){pcur = pcur->next;count++;if (pcur == NULL){printf("不在可執行范圍內!");return 0;}}SListNote* ppos = pcur->next;pcur->next = newnote;newnote->next = ppos;}
}
尾插與頭插大體相似,在一些細節處略微改動,例如在后續找pos位置時,我們找到的是pos的位置,而之前是找到pos前面一個節點,這方面略有不同。其他大體相同。
查找節點
void Finding(SListNote** pphead, SLT_type x)
{assert(pphead && *pphead);SListNote* pcur = *pphead;int count = 0;while (pcur){if (pcur->data == x){printf("找到了!在%d處\n", count);return;}count++;pcur = pcur->next;}printf("沒找到");
}
查找節點并不難,用到的方法和之前許多代碼相似,可大致看看上圖。
摧毀鏈表
void Sli_destory(SListNote** pphead)
{assert(pphead && *pphead);SListNote* pcur = *pphead;while (pcur){SListNote* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;
首先我們也還是先斷言一下節點地址的存在性,接著從頭到尾一個節點一個節點的去刪除,為了方便顯示,我在最后又讓首節點顯示了個NULL,已證明已經刪除完成。