一個通用連結串列的簡單實現
阿新 • • 發佈:2019-02-10
最近在CSDN上看到了absurd大神的幾篇關於系統程式設計師成長計劃的的博文
裡面提到了關於通用連結串列實現的思想,雖然資料結構學的還行,但是真的沒寫過通用的連結串列,對封裝的認識比較淺顯!
於是乎決定實現一下,真正開始寫才發現,對我這麼個眼高手低的菜鳥來說挺有難度的。寫篇博文記錄下。
整體實現的主題思想還是absurd大神博文中的兩個重要思想:
1, 連結串列資料段存指標void*
2 採用回撥函式來使的連結串列的基本函式更有通用性
貼程式碼:
links.h
links.c#ifndef LINKS_H_ #define LINKS_H_ #include <malloc.h> #include <stdbool.h> #include <stdio.h> #define LEN_LIST sizeof(DListNode) typedef bool (*DListDataPrintFunc)(void* data); //列印連結串列的回撥函式介面 typedef bool (*DListVistFunc)(void* data, void* ctx); //遍利連結串列的回掉函式介面 typedef bool (*DListDataAssign)(void* data, void* ctx); typedef struct _DListNode{ struct _DListNode* prev; struct _DListNode* next; void * data; //int data; }DListNode,*ptrDListNode; typedef struct { ptrDListNode head,tail; int len; }LinkList,*ptrLinkList; //開闢一個節點 返回指向該節點的指標 ptrDListNode CreateNode(); //釋放一個節點記憶體 返回空指標值 ptrDListNode DestroyDListNode(ptrDListNode e); //初始化一個連結串列頭節點 ptrLinkList InitList(); //銷燬一個連結串列頭節點 以及連結串列內所有元素 返回NULL ptrLinkList DestroyLinkList(ptrLinkList L); //為連結串列的節點賦值 bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx); //在第n個節點後插入s指向的節點 bool InsDListNode(ptrLinkList L, ptrDListNode s, int n); //刪除第n個節點 用q返回 bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n); //列印連結串列 void links_print(ptrLinkList L, DListDataPrintFunc); //遍歷連結串列 根據回撥函式 用ctx返回所求結果 bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx); #endif//
test.c#include "links.h" //給節點data賦值 bool DListNodeGetData(void* data, DListDataAssign assign, void* ctx) { assign(data,ctx); } //開闢一個結點 ptrDListNode CreateNode(int size_data) { ptrDListNode p = (ptrDListNode) malloc(LEN_LIST); p->prev = NULL; p->next = NULL; p->data = (void*) malloc(size_data); return p; } //初始化頭節點 ptrLinkList InitList() { ptrLinkList L = (ptrLinkList) malloc(sizeof(LinkList)); L->head = NULL; L->tail = NULL; L->len = 0; return L; } //銷燬一個節點 ptrDListNode DestroyDListNode(ptrDListNode e) { free(e->data); free(e); e = NULL; return NULL; } //銷燬整個連結串列 ptrLinkList DestroyLinkList(ptrLinkList L) { ptrDListNode p = L->head; int n = L->len; while(n--) { p = p->next; DestroyDListNode(L->head); L->head = p; } free(L); return NULL; } //在第一個節點前插入 static void InsFirst(ptrLinkList L, ptrDListNode s) { ptrDListNode p = L->head; L->head = s; s->prev = NULL; if(L->len == 0) { L->tail = s; s->next = NULL; } else{ s->next = p; p->prev = s; } L->len++; } //在第n個節點後插入 bool InsDListNode(ptrLinkList L, ptrDListNode s, int n) { if(n > L->len || n < 0 ) return false; if(!n){ InsFirst(L,s); return true; } ptrDListNode p = L->head; if(n == L->len) L->tail = s; while(--n) { p = p->next; } s->next = p->next; p->next = s; s->prev = p; if(L->tail != s) s->next->prev = s; else s->next = NULL; L->len ++; return true; } //刪除第一個節點q返回 static bool DelFirst(ptrLinkList L, ptrDListNode* q) { if(L->len == 0) return false; *q = L->head; if(L->len == 1){ L->head = NULL; L->tail = NULL; } else { L->head = L->head->next; L->head->prev = NULL; } (*q)->next = NULL; L->len--; return true; } //刪除第n個節點 q你返回 bool DelDListNode(ptrLinkList L, ptrDListNode* q, int n) { if(n > L->len || n < 0) return false; if(!n) { DelFirst(L,q); return true; } ptrDListNode p = L->head; while(--n) { p = p->next; } if(p == L->tail){ L->tail = p->prev; } else p->next->prev = p->prev; p->prev->next = p->next; *q = p; (*q)->next = NULL; (*q)->prev = NULL; L->len--; return true; } //遍歷訪問 bool Dlist_Foreach(ptrLinkList L, DListVistFunc visit, void* ctx) { ptrDListNode p = L->head; int n = L->len; long long* ctx_temp = ctx; *ctx_temp = 0; while(n--) { visit(p->data, ctx); p = p->next; } return true; } //遍歷列印 void links_print(ptrLinkList L, DListDataPrintFunc print) { ptrDListNode p = L->head; int n = L->len; while(n--) { print(p->data); putchar('\n'); p = p->next; } }
#include <stdio.h> #include "links.h" #define DATA_SIZE sizeof(Node) typedef struct Node{ int a; int b; }Node,*ptrNode; static bool Data_Assign(void* data, ptrNode ctx) { ptrNode temp = data; temp->a = ctx->a; temp->b = ctx->b; return true; } static bool print_int(void* data) { ptrNode temp = data; printf("%d %d",temp->a, temp->b); return true; } static bool get_sum(void* data, void* ctx) { ptrNode temp = data; long long* result = ctx; *result += temp->b; return true; } static bool find_max(void* data, void* ctx) { ptrNode temp = data; int* pimax = ctx; if(*pimax < temp->a) *pimax = temp->a; return true; } int main() { int i; ptrLinkList p = InitList(); ptrDListNode s = CreateNode(DATA_SIZE); ptrDListNode s1 = CreateNode(DATA_SIZE); ptrDListNode s2 = CreateNode(DATA_SIZE); ptrDListNode s3 = CreateNode(DATA_SIZE); ptrDListNode s4; long long sum; int imax; Node ss[5]; for(i = 1; i<5; i++) { ss[i].a = i; ss[i].b = i*10 + i; } DListNodeGetData(s->data, Data_Assign, &ss[1]); DListNodeGetData(s1->data, Data_Assign, &ss[2]); DListNodeGetData(s2->data, Data_Assign, &ss[3]); DListNodeGetData(s3->data, Data_Assign, &ss[4]); InsDListNode(p,s,0); InsDListNode(p,s1,0); InsDListNode(p,s2,0); InsDListNode(p,s3,3); links_print(p,print_int); Dlist_Foreach(p, get_sum, &sum); Dlist_Foreach(p, find_max, &imax); printf("%lld %d\n",sum,imax); DestroyLinkList(p); return 0; }
因為第一次寫,首先寫了一個固定int型資料的連結串列,實現了基本的插入刪除操作。為了通用性開始使用void*來存資料段,因為指標一塊兒仍舊是眼高手低基礎不牢,先假設是非結構體資料進行測試,編寫了幾個常規的回撥函式,實現遍歷列印,遍歷求和,遍歷求最大值。
畢竟絕大多數的連結串列資料段都是儲存的結構體,真正儲存結構體的時候又涉及到void型指標進行記憶體分配的問題,這裡依舊需要用回撥函式來完成,畢竟內部函式不知道到底是怎樣的一個結構體。
整體程式碼測試通過,由於在linux下編寫的,gdb除錯用的不熟練,效率太低,而且使用makefile之後我都不會用gdb除錯了,list都不出程式碼了,腦袋完全大了。這次也是人生第一次編寫makefile檔案。貼出來吧,以後回來改,估計是makefile寫的有問題才整的gdb除錯的時候只能run不能list....
makefile
test:test.o links.o
gcc test.o links.o -o test
test.o:test.c links.h
gcc -c -g test.c
links.o:links.c links.h
gcc -c -g links.c
#this is a makefile