通用單向連結串列的實現(附使用例子)
當我開啟csdn,發現我有第一個粉絲了,激動得差點大喊“Hello World”,整晚都很開心,哈哈,為了保住我唯一的粉絲,我決定,以後勤快一點。今天就先出一篇通用連結串列實現的部落格,只包含了一個頭檔案(.h)和一個原始檔(.c),可以很方便地新增到你們的專案中。
list.h:
#ifndef __LIST_H__ #define __LIST_H__ #define LIST_NO_MEM -2 #define LIST_ERROR -1 #define LIST_SUCCESS 0 typedef struct _node node_t; /*node*/ struct _node{ node_t *next; //next node void *element; //pointer of elt }; /*list*/ typedef struct _list{ int nb_elt; //number of element of list node_t *node; //head pointer }list_t; /*initialise the list,the list shouldn't be null*/ int list_init(list_t *list); /*get the size of a list of element*/ int list_size(const list_t *list); /*add a element to list*/ int list_add(list_t *list,void *element,int pos); /*get an element from a list*/ void *list_get(const list_t *list,int pos); /*remove an element from a list*/ int list_remove(list_t *list,int pos); #endif
list.c
#include "list.h" #include <stdlib.h> #include <stdio.h> #include <memory.h> int list_init(list_t *list) { if(list == NULL) return LIST_ERROR; memset(list,0,sizeof(list_t)); return LIST_SUCCESS; } int list_size(const list_t *list) { if(list == NULL) return LIST_ERROR; return list->nb_elt; } /*index start from 0*/ int list_add(list_t *list,void *element,int pos) { int i = 0; node_t *new_node = NULL; node_t *node = NULL; if(list == NULL) return LIST_ERROR; new_node = (node_t*)malloc(sizeof(node_t)); if(new_node == NULL) return LIST_NO_MEM; memset(new_node,0,sizeof(node_t)); new_node->element = element; if(list->nb_elt == 0) { new_node->next = NULL; list->node = new_node; list->nb_elt++; return list->nb_elt; } if(pos < 0 || pos >= list->nb_elt) pos = list->nb_elt; node = list->node; if(pos == 0) { new_node->next = list->node; list->node = new_node; list->nb_elt++; return list->nb_elt; } while(i + 1 < pos) { i++; node = node->next; } if(pos == list->nb_elt) { new_node->next = NULL; node->next = new_node; list->nb_elt++; return list->nb_elt; } new_node->next = node->next; node->next->next = new_node; list->nb_elt++; return list->nb_elt; } void *list_get(const list_t *list,int pos) { int i = 0; node_t *node = NULL; if(list == NULL) return NULL; if(pos < 0 || pos >= list->nb_elt) return NULL; node = list->node; while(i < pos) { i++; node = node->next; } return node->element; } int list_remove(list_t *list,int pos) { int i = 0; node_t *node = NULL; node_t *del_node = NULL; if(list == NULL) return LIST_ERROR; if(pos < 0 || pos >= list->nb_elt) return LIST_ERROR; node = list->node; /*special case*/ if(pos == 0) { list->node = node->next; list->nb_elt--; free(node); return LIST_SUCCESS; } while(pos > i + 1) { i++; node = node->next; } del_node = node->next; node->next = node->next->next; list->nb_elt--; free(del_node); return LIST_SUCCESS; }
#include <stdlib.h> #include <stdio.h> #include "list.h" int main() { int size = 0; int i = 0; list_t list; char *elt; list_init(&list); char arr1[5] = "hello"; char arr2[5] = "world"; char arr3[32] = "i'm pumpkin monkey"; list_add(&list,(void*)arr1,-1); list_add(&list,(void*)arr2,-1); list_add(&list,(void*)arr3,-1); printf("list size:%d\n",list_size(&list)); list_remove(&list,1); size = list_size(&list); printf("list size:%d\n",size); for(i = 0;i<size;i++) { elt = (char*)list_get(&list,i); if(elt == NULL) { printf("get null node\n"); } printf("elment :%s\n",elt); } return 0; }
以上就是最基本的通用單向連結串列的實現,包含了建立、新增、刪除、長度、獲取等最常用的基本功能。還可以包含克隆,下個元素、等其他功能,可根據需要自己新增
實際上鍊表並不複雜,但是很多人還是沒法寫好,主要還是特殊情況(邊界條件)考慮得不夠充分。不僅是在連結串列,在任何一個函式的實現裡,都必須要考慮特殊場景。對於連結串列來說,其實需要注意的也就那幾個點,目標節點(pos)是否存在、連結串列長度為0和pos剛好等於連結串列長度、輸入連結串列是否有效、迴圈條件。
大家可以考慮加個頭結點:
資料結構中,在單鏈表的開始結點之前附設一個型別相同的結點,稱之為頭結點。頭結點的資料域可以不儲存任何資訊,頭結點的指標域儲存指向開始結點的指標(即第一個元素結點的儲存位置)。
頭結點作用:
1、防止單鏈表是空的而設的.當連結串列為空的時候,帶頭結點的頭指標就指向頭結點.如果當連結串列為空的時候,單鏈表沒有帶頭結點,那麼它的頭指標就為NULL.
2、是為了方便單鏈表的特殊操作,插入在表頭或者刪除第一個結點.這樣就保持了單鏈表操作的統一性!
3、單鏈表加上頭結點之後,無論單鏈表是否為空,頭指標始終指向頭結點,因此空表和非空表的處理也統一了,方便了單鏈表的操作,也減少了程式的複雜性和出現bug的機會。
4、對單鏈表的多數操作應明確對哪個結點以及該結點的前驅。不帶頭結點的連結串列對首元結點、中間結點分別處理等;而帶頭結點的連結串列因為有頭結點,首元結點、中間結點的操作相同 ,從而減少分支,使演算法變得簡單 ,流程清晰。對單鏈表進行插入、刪除操作時,如果在首元結點之前插入或刪除的是首元結點,不帶頭結點的單鏈表需改變頭指標的值,在C 演算法的函式形參表中頭指標一般使用指標的指標(在C+ +中使用引用 &);而帶頭結點的單鏈表不需改變頭指標的值,函式引數表中頭結點使用指標變數即可。