线性表的链式存储(业务结点和链表算法分离模式)

    科技2022-08-29  104

    原有的链表结构体里添加了数据域,导致业务结点和算法逻辑耦合,虽然可以用typedef定义不同的数据类型,但是还是不太方便。参考了传智扫地僧相关源码

    LinkListApi.h如下:

    //直接给返回一个句柄,内部有一个外部不感知的结构体 //原始的链表是业务结点和链表的算法互相耦合,不便于使用 //要想写出可以使用的通用的链表,那就需要将业务结点和算法逻辑相互分离。 //然后还有一个巧妙的地方即使结构体的首地址和第一个元素的首地址是重合的 //初始化时返回给业务一个指针,不能让业务感知,返回void* #include<iostream> using namespace std; typedef void LinkList; //将这个结点暴漏给业务,业务直接使用就好 typedef struct tag_LinkListNode { struct tag_LinkListNode *next; }LinkListNode; //初始化一个链表 LinkList* LinkList_Create(); //销毁一个链表 void LinkList_Destroy(LinkList* list); //清除链表中的元素 void LinkList_Clear(LinkList* list); //求链表的长度 int LinkList_Length(LinkList* list); //向链表中插入一个元素 int LinkList_Insert(LinkList* list, LinkListNode* node, int pos); //获取链表中某个位置中的元素 LinkListNode* LinkList_Get(LinkList* list, int pos); //删除链表中某个元素的值 LinkListNode* LinkList_Delete(LinkList* list, int pos);

    LinkListApi.cpp

    //写一个内部的结构体,内部使用 //因为当初始化的时候,要给应用层返回一个 头指针 //因为涉及到要封装一个链表的长度字段,所以要重新定义一个结构体 //也就是结构体嵌套一个结构体,然后添加一个长度 //尽量先完场测试框架的书写,后完善框架 #include "LinkListApi.h" //只是人为的封装了一层,增加了个长度 typedef struct tagMyLinkList { LinkListNode header;//这个是原来链表的本质 int length; }MyLinkList; //初始化一个链表 LinkList* LinkList_Create() { MyLinkList*tmp = (MyLinkList*)malloc(sizeof(MyLinkList)); if (tmp == NULL) { printf("func LinkList_Create() err\n"); return NULL; } memset(tmp, 0, sizeof(MyLinkList));//清空内存 tmp->header.next=NULL; tmp->length = 0; return tmp;//返回给应用头指针 } //销毁一个链表 void LinkList_Destroy(LinkList* list) { if (list == NULL) { return; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, free(tmp); return; } //清除链表中的元素 void LinkList_Clear(LinkList* list) { if (list == NULL) { return; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, tmp->header.next = NULL; tmp->length = 0; return; } //求链表的长度 int LinkList_Length(LinkList* list) { if (list == NULL) { return -1; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, return tmp->length; } //向链表中插入一个元素 int LinkList_Insert(LinkList* list, LinkListNode* node, int pos) { //返回给应用层的是头指针,指向头结点 //定义计数器j int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = &(tmp->header); while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return -1; } //开始交换数据 node->next = current->next; current->next = node; ++tmp->length; //干活 return 0; } //获取链表中某个位置中的元素 LinkListNode* LinkList_Get(LinkList* list, int pos) { int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = &(tmp->header); while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return NULL; } return current->next;//也是要了一个地址 } //删除链表中某个元素的值 LinkListNode* LinkList_Delete(LinkList* list, int pos) { //返回给应用层的是头指针,指向头结点 //定义计数器j int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = &(tmp->header); while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return NULL; } //开始删除数据 LinkListNode*p = current->next; current->next = current->next->next; tmp->length--; return p; }

    测试框架代码:

    //测试框架的搭建 #include"LinkListApi.h" //业务想搭建一个老师的链表 struct Teacher { LinkListNode Node; int age; }; int main() { LinkList*p = NULL; p=LinkList_Create(); Teacher t1; t1.age = 35; Teacher t2; t2.age = 45; //把句柄带回去 LinkList_Insert(p, (LinkListNode*)&t1,0); LinkList_Insert(p, (LinkListNode*)&t2, 0); //开始遍历链表中的数据 for (int i = 0; i < LinkList_Length(p); i++) { struct Teacher*tmp = (struct Teacher*)LinkList_Get(p, i); printf("%d\n", tmp->age); } //删除链表中的数据 struct Teacher*p1 = (struct Teacher*)LinkList_Delete(p, 0); printf("delete element [%d]\n",p1->age); //继续遍历链表 for (int i = 0; i < LinkList_Length(p); i++) { struct Teacher*tmp = (struct Teacher*)LinkList_Get(p, i); if (tmp != NULL) { printf("%d\n", tmp->age); } } //清空链表中的数据 LinkList_Clear(p); //销毁链表 LinkList_Destroy(p); p = NULL; return 0; }

    输出结果如下:

    其中对一些结构的封装很关键,可以参考代码理解

    有些人可能会问,为啥

    typedef struct tagMyLinkList {     LinkListNode *header;//这个是原来链表的本质     int length; }MyLinkList;

    不定义指针这种形式,这个是随意的,也可以这样定义,这样就多了一次内存分配,以及在destroy的时候进行free

    这样定义后的代码如下所示:

    //写一个内部的结构体,内部使用 //因为当初始化的时候,要给应用层返回一个 头指针 //因为涉及到要封装一个链表的长度字段,所以要重新定义一个结构体 //也就是结构体嵌套一个结构体,然后添加一个长度 //尽量先完场测试框架的书写,后完善框架 #include "LinkListApi.h" //只是人为的封装了一层,增加了个长度 typedef struct tagMyLinkList { LinkListNode *header;//这个是原来链表的本质 int length; }MyLinkList; //初始化一个链表 LinkList* LinkList_Create() { MyLinkList*tmp = (MyLinkList*)malloc(sizeof(MyLinkList)); if (tmp == NULL) { printf("func LinkList_Create() err\n"); return NULL; } memset(tmp, 0, sizeof(MyLinkList));//清空内存 tmp->header = (LinkListNode *)malloc(sizeof(LinkListNode)); tmp->header->next=NULL; tmp->length = 0; return tmp;//返回给应用头指针 } //销毁一个链表 void LinkList_Destroy(LinkList* list) { if (list == NULL) { return; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, if (tmp->header != NULL) { free(tmp->header); tmp->header = NULL; } free(tmp); return; } //清除链表中的元素 void LinkList_Clear(LinkList* list) { if (list == NULL) { return; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, tmp->header->next = NULL; tmp->length = 0; return; } //求链表的长度 int LinkList_Length(LinkList* list) { if (list == NULL) { return -1; } MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, return tmp->length; } //向链表中插入一个元素 int LinkList_Insert(LinkList* list, LinkListNode* node, int pos) { //返回给应用层的是头指针,指向头结点 //定义计数器j int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = tmp->header; while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return -1; } //开始交换数据 node->next = current->next; current->next = node; ++tmp->length; //干活 return 0; } //获取链表中某个位置中的元素 LinkListNode* LinkList_Get(LinkList* list, int pos) { int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = tmp->header; while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return NULL; } return current->next;//也是要了一个地址 } //删除链表中某个元素的值 LinkListNode* LinkList_Delete(LinkList* list, int pos) { //返回给应用层的是头指针,指向头结点 //定义计数器j int j = 0; MyLinkList*tmp = (MyLinkList*)list;//这个是我自己封装的,自己知道,别人不知道, LinkListNode*current = NULL; //通过while循环使tmp指向要插入的前一个结点 current = tmp->header; while (current != NULL&&j < pos) { current = current->next; ++j; } if (current == NULL) { return NULL; } //开始删除数据 LinkListNode*p = current->next; current->next = current->next->next; tmp->length--; return p; }

    输出结果不变

    Processed: 0.008, SQL: 9