双向链表管理程序

在写一个小工具的时候,需要用到对ini文件的管理。为了让这个小工具在Linux也适用,所以在写程序的时候没有使用mfc提供的类库,也没有使用windows提供的ini操作函数,直接拿标准C写了一个。

在操作ini的过程中,我拿两种双向链表来描述一个ini文件,也就意味着,要针对每种链表都写一套插入、删除之类的操作函数,格外麻烦。索性,就写了一个通用的双向链表管理程序,省的以后麻烦。

这个“通用”的双向链表管理程序的思路是:

每种双向链表的节点结构体里,虽然有太多的不一样,但是next和prev指针肯定是要有的,所以,就可以制定一个只包含next和prev成员的基础双向链表节点结构体类型,如下:

   1: struct basic_nod_t {
   2:     struct basic_nod_t *prev;
   3:     struct basic_nod_t *next;
   4: };

这样,只要是具备prev和next指针的节点,都可以强制转换为这种通用型的结构。当然,前提是,一个具体的节点结构体在定义的时候,需要让prev和next做为结构体的第一个和第二个成员,这种强制转换才会有效果。比如,现在有一个用来保存用户信息的节点结构体:

   1: struct user_info_t {
   2:     struct user_info_t *prev;
   3:     struct user_info_t *next;
   4:     char *username;
   5:     char *passwd;
   6: };

因为user_info_t结构体的前两个成员也是prev和next指针,那么,当尝试把user_info_t结构体强制转换为basic_nod_t时,basic_nod_t内的prev和next成员即为user_info_t的对应成员。

这样一来,通用型的管理程序就有机会实现了。因为对链表的插入、删除等操作,在一般情况下,只会牵扯到对prev和next指针的修改,所以,不管具体的结构体长什么样子,只要你有prev和next,我就可以操作!

来个具体的代码吧,拿插入节点为例:

   1: void add_node_to(void **head, void **tail, void *node, void *to, int before_or_after)
   2: {
   3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
   4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
   5:     struct basic_nod_t *n = (struct basic_nod_t*)node;
   6:     struct basic_nod_t *tt = (struct basic_nod_t*)to;
   7:     if(*h == NULL)
   8:     {
   9:         *h = *t = n;
  10:         n->prev = NULL;
  11:         n->next = NULL;
  12:     }
  13:     else if(before_or_after == 0)
  14:     {
  15:         //move the 'moved' node to the place before 'to'
  16:         if(tt == NULL)
  17:             tt = *h;
  18:         if(*h == tt)
  19:             *h = n;
  20:         n->prev = tt->prev;
  21:         n->next = tt;
  22:         tt->prev = n;
  23:         if(n->prev != NULL)
  24:         {
  25:             n->prev->next = n;
  26:         }
  27:     }
  28:     else if(before_or_after == 1)
  29:     {
  30:         //move the 'moved' node to the place after 'to'
  31:         if(tt == NULL)
  32:             tt = *t;
  33:         if(*t == tt)
  34:             *t = n;
  35:         n->next = tt->next;
  36:         n->prev = tt;
  37:         tt->next = n;
  38:         if(n->next != NULL)
  39:             n->next->prev = n;
  40:     }
  41: }

函数在传参的时候,采用了void指针类型,因为在实际使用过程中,节点结构体的定义并不一致。

其中,函数的head和tail参数,用来表示链表的头节点和尾节点。因为在插入节点的过程中,可能会修改head和tail,所以,这里在传参的时候,采用的是void**这种指针的指针的形式,用来表示,head和tail这两个指针将来是要被修改的。

在add_node_to函数内部,会首先将传进来的参数转换为basic_nod_t类型,此时,节点内的prev和next指针就可以被操作了。

OK,再来个删除节点的:

   1: void del_node(void **head, void **tail, void *node)
   2: {
   3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
   4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
   5:     struct basic_nod_t *n = (struct basic_nod_t*)node;
   6:     if(n == *h)
   7:     {
   8:         *h = n->next;
   9:     }
  10:     if(n == *t)
  11:     {
  12:         *t = n->prev;
  13:     }
  14:     if(n->prev != NULL)
  15:         n->prev->next = n->next;
  16:     if(n->next != NULL)
  17:         n->next->prev = n->prev;
  18: }

还有移动节点:

   1: void move_node(void **head, void **tail, void *moved, void *to, int before_or_after)
   2: {
   3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
   4:     struct basic_nod_t **t = (struct basic_nod_t**)tail;
   5:     struct basic_nod_t *m = (struct basic_nod_t*)moved;
   6:     struct basic_nod_t *tt = (struct basic_nod_t*)to;
   7:     if(    (h == NULL) || (t == NULL)
   8:         || (*h == NULL) || (*t == NULL)
   9:         || (m == tt))
  10:         return;
  11:     del_node(head, tail, moved);
  12:     add_node_to(head, tail, moved, to, before_or_after);
  13: }

最后再来个链表排线的(这里使用的是最简单的冒泡排序):

   1: void sort_list(void **head, void **tail, CMP_FUNC nodcmp)
   2: {
   3:     struct basic_nod_t **h = (struct basic_nod_t**)head;
   4:     struct basic_nod_t *nod1 = *h;
   5:     struct basic_nod_t *nod2 = nod1->next;
   6:     int swaped = 1;
   7:     while(swaped)
   8:     {
   9:         swaped = 0;
  10:         while(1)
  11:         {
  12:             if((*nodcmp)(nod1, nod2) > 0)
  13:             {
  14:                 move_node(head, tail, nod1, nod2, 1);
  15:                 nod2 = nod1->next;
  16:                 swaped = 1;
  17:             }
  18:             else
  19:             {
  20:                 nod1 = nod2;
  21:                 nod2 = nod2->next;
  22:             }
  23:             if(nod2 == NULL)
  24:             {
  25:                 nod1 = *head;
  26:                 nod2 = nod1->next;
  27:                 break;
  28:             }
  29:         }
  30:     }
  31: }

在排序的时候,由于通用管理程序并不知道排序的依据和算法,所以,需要提供给他一个nodcmp函数来完成两个节点大小的计算工作。CMP_FUNC的定义如下:

   1: typedef int (*CMP_FUNC)(void *t1, void *t2);

基本搞定,呵呵~~

使用起来也非常简单,仍然拿那个用户信息链表user_info_t为例:

   1: // 定义链表的头和尾指针, 初始情况下,链表为空
   2: struct user_info_t *head = NULL;
   3: struct user_info_t *tail = NULL;
   4: // 首先创建一个节点
   5: user_info_t *node = (struct user_info_t *)malloc(sizeof(struct user_info_t));
   6: // 然后初始化节点的一些必要信息
   7: node->username = (char *)malloc(strlen("test") + 1);
   8: strcpy(node->username, "test");
   9: node->passwd = (char *)malloc(strlen("admin") + 1);
  10: strcpy(node->passwd, "admin");
  11: // 然后将节点插入链表中
  12: add_node(&head, &tail, node);
  13: // 同样的方法还可以插入更多的节点;
  14: add_node(&head, &tail, node);
  15: // 删除节点也同样方便
  16: del_node(&head, &tail, node);
  17: // 注意,链表管理程序并不会帮助你释放空间,所以,如果节点是在堆中分配的,那么记得在从链表中剔除节点后,将节点占用的空间释放
  18: free(node->username);
  19: free(node->passwd);
  20: free(node);
  21: // 搞定

下面是已经写好的管理程序源代码下载链接,这是个VC的工程,包含了一个简单的测试程序,链表管理程序可以参考工程内的queue.c和queue.h,这两个文件可以放到其他平台下编译.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您介绍一下双向链表的实现方法。首先,您需要定义一个图书的结构体,包含图书的相关信息,例如书名、作者、出版社、价格等等。接着,您需要定义一个节点结构体,包含指向前驱节点和后继节点的指针,以及指向图书结构体的指针。代码如下所示: ```c struct book { char name[50]; char author[20]; char publisher[20]; float price; }; struct node { struct book *data; struct node *prev; struct node *next; }; ``` 接下来,您需要实现双向链表的各种操作函数,例如创建链表、插入节点、删除节点等。这里给出一个简单的示例代码: ```c // 创建双向链表 struct node *create_list() { struct node *head = (struct node *)malloc(sizeof(struct node)); head->data = NULL; head->prev = NULL; head->next = NULL; return head; } // 在链表末尾插入节点 void insert_at_end(struct node *head, struct book *data) { struct node *new_node = (struct node *)malloc(sizeof(struct node)); new_node->data = data; new_node->prev = NULL; new_node->next = NULL; struct node *ptr = head; while (ptr->next != NULL) { ptr = ptr->next; } ptr->next = new_node; new_node->prev = ptr; } // 在链表删除指定节点 void delete_node(struct node *head, struct node *del_node) { struct node *ptr = head; while (ptr != NULL && ptr != del_node) { ptr = ptr->next; } if (ptr == NULL) { return; } ptr->prev->next = ptr->next; ptr->next->prev = ptr->prev; free(ptr); } ``` 最后,您可以在 main 函数调用这些操作函数,实现图书管理系统的功能。例如,您可以创建一个双向链表,存储图书信息,然后可以通过遍历链表的方式查找、修改或删除某本图书的信息。完整的示例代码如下: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> struct book { char name[50]; char author[20]; char publisher[20]; float price; }; struct node { struct book *data; struct node *prev; struct node *next; }; // 创建双向链表 struct node *create_list() { struct node *head = (struct node *)malloc(sizeof(struct node)); head->data = NULL; head->prev = NULL; head->next = NULL; return head; } // 在链表末尾插入节点 void insert_at_end(struct node *head, struct book *data) { struct node *new_node = (struct node *)malloc(sizeof(struct node)); new_node->data = data; new_node->prev = NULL; new_node->next = NULL; struct node *ptr = head; while (ptr->next != NULL) { ptr = ptr->next; } ptr->next = new_node; new_node->prev = ptr; } // 在链表删除指定节点 void delete_node(struct node *head, struct node *del_node) { struct node *ptr = head; while (ptr != NULL && ptr != del_node) { ptr = ptr->next; } if (ptr == NULL) { return; } ptr->prev->next = ptr->next; ptr->next->prev = ptr->prev; free(ptr); } // 遍历链表,输出所有图书信息 void print_list(struct node *head) { struct node *ptr = head->next; while (ptr != NULL) { printf("书名:%s,作者:%s,出版社:%s,价格:%f\n", ptr->data->name, ptr->data->author, ptr->data->publisher, ptr->data->price); ptr = ptr->next; } } int main() { struct node *head = create_list(); // 添加图书信息 struct book book1 = {"C语言程序设计", "谭浩强", "清华大学出版社", 38.5}; struct book book2 = {"数据结构与算法分析", "Mark Allen Weiss", "机械工业出版社", 98.0}; struct book book3 = {"计算机组成原理", "唐朔飞", "清华大学出版社", 45.0}; insert_at_end(head, &book1); insert_at_end(head, &book2); insert_at_end(head, &book3); // 输出所有图书信息 printf("所有图书信息:\n"); print_list(head); // 修改图书信息 struct node *ptr = head->next; while (ptr != NULL && strcmp(ptr->data->name, "C语言程序设计") != 0) { ptr = ptr->next; } if (ptr != NULL) { strcpy(ptr->data->publisher, "人民邮电出版社"); printf("修改后的图书信息:\n"); print_list(head); } // 删除图书信息 ptr = head->next; while (ptr != NULL && strcmp(ptr->data->name, "数据结构与算法分析") != 0) { ptr = ptr->next; } if (ptr != NULL) { delete_node(head, ptr); printf("删除后的图书信息:\n"); print_list(head); } return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值