注意:本文讨论的是无头单向非循环链表。

假设不采用Linux内核链表的思路,怎样用C语言实现通用链表呢?

一种常用的做法是:

typedef int element_t;

struct node_info

{

element_t data;

struct node_info *next;

};

其实这样的链表算不上通用,因为无法同时使用不同的数据类型。参考网友的思路,以上可以改为下面的定义:

//无头非循环单向链表
struct node_info {void *data;struct node_info *next;
};

先看看头文件:

#pragma once//无头非循环单向链表
struct node_info {void *data;struct node_info *next;
};struct student
{char name[20];unsigned char age;};//for teststruct  slist_info {struct node_info *first; //指向第一个节点void (*insert_head)(void *one_data, struct slist_info *info);//头插int (*del)(struct node_info *node, struct slist_info *info);//删除节点struct node_info* (*find)(struct slist_info *info,int(*compare)(void *dest,void *key),void *key);//这里的compare是回调函数,由用户提供void (*for_each)(const struct slist_info *info,void (*todo)(void *one_data));//遍历,todo是回调函数void (*for_each_safe)(const struct slist_info *info,void (*todo)(void *one_data));//安全遍历(觉得在这里这个方法意义不大)void (*invert)(struct slist_info *info);//反转链表int (*is_dead_loop)(struct slist_info *info);//判断是否有死环,是返回1,不是返回0
};#define   slist_is_empty(info)  ((info)->first == NULL) //链表是否为空//构造和析构
void  slist_init(struct  slist_info *info);
void  slist_destroy(struct slist_info *info);

与这个链表相关的操作如下。

1.插入元素(头插法)

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "slist.h"/*无头非循环单链表*/
static void slist_insert_head(void *one_data, struct slist_info *info)
{//头插法assert(one_data != NULL && info != NULL);struct node_info *node = (struct node_info *)malloc(sizeof(struct node_info));//分配内存assert(node!=NULL);node->data = one_data;//注意,这里只是简单地把指针指向用户的数据,并没有把用户数据复制过来if (slist_is_empty(info)) {//1.空链表info->first = node;node->next = NULL;}else { //2. 非空node->next = info->first;info->first = node;}
}

2.遍历

static void slist_for_each(const struct slist_info *info,void (*todo)(void *one_data))
{assert(info != NULL && todo != NULL);struct node_info *cur = NULL;for (cur = info->first; cur != NULL; cur = cur->next) {todo(cur->data);}
}static void slist_for_each_safe(const struct slist_info *info,void (*todo)(void *one_data))
{assert(info != NULL && todo != NULL);struct node_info *cur = NULL;struct node_info *Next = NULL;for (cur = info->first; cur != NULL; cur = Next) {Next = cur->next;//Next保存了下一个节点,如果这个节点被删除了,那么下一个节点还可以找到todo(cur->data);}
}

3.反转(面试的时候,有可能会问到,直接背过好了!)

static void  slist_invert(struct slist_info *info)
{assert(info != NULL);struct node_info *Cur = NULL;struct node_info *Prev = NULL;struct node_info *Next = NULL;//1.移动Next 2.反向 3.移动Prev 4.移动Curfor (Cur = info->first; Cur  != NULL; Cur = Next) {Next = Cur->next;Cur->next = Prev;Prev = Cur;}//修改头指针,指向首节点info->first = Prev;
}

4.查找

struct node_info *slist_find(struct slist_info *info,int(*compare)(void *dest,void *key),void *key)
{assert(info != NULL && compare != NULL);struct node_info *cur = NULL;for (cur = info->first; cur != NULL; cur = cur->next) {if(compare(cur->data,key)==1)//回调函数,把链表中的每一个数据和用户传入的关键字做比较,符合条件返回1return cur;//返回节点的地址给用户}return NULL;//没有找到返回空指针
}

注意,这里的第三个参数(关键字)也是用户传进来的。

5.构造和析构

void slist_init(struct slist_info *info)
{//空链表, 第一个节点为NULLinfo->first = NULL;info->insert_head = slist_insert_head;info->del = slist_del;info->find = slist_find;info->invert = slist_invert;info->is_dead_loop = slist_is_dead_loop;info->for_each = slist_for_each;info->for_each_safe = slist_for_each_safe;
}void slist_destroy(struct slist_info *info)
{if(!slist_is_empty(info)){struct node_info *cur = NULL;struct node_info *Next = NULL;for (cur = info->first; cur != NULL; cur = Next) {Next = cur->next;//Next保存了下一个节点,如果这个节点被删除了,那么下一个节点还可以找到free(cur);//释放每个节点占用的内存}}}

细心的读者会发现,好像有的函数没有实现啊。下一篇博文我们再讨论!

C语言实现通用链表初步(一)相关推荐

  1. C语言实现通用链表初步(四)----双向链表

    在前面的文章中,我们讨论了如何实现通用类型的链表,方法是用void *类型的指针,指向数据.那么还有其他的方法吗(不考虑内核链表)? 答案是肯定的.用零长数组也可以实现. struct node_in ...

  2. C语言实现通用链表初步(三)----单元测试

    前两节,我们已经完成了链表的一些操作,快来测试一下吧. 这里使用的单元测试工具名字叫"check". START_TEST(my_slist_1) {struct student ...

  3. C语言实现通用链表初步(二)

    接着上次的内容,我们继续! 还是无头单向非循环链表.假如要删除某个节点,如何实现? //删除成功返回0,失败返回-1 int slist_del(struct node_info *node, str ...

  4. 数据结构——双链表(C语言详述通用双链表)

    说明:   本文章旨在总结备份.方便以后查询,由于是个人总结,如有不对,欢迎指正:另外,内容大部分来自网络.书籍.和各类手册,如若侵权请告知,马上删帖致歉.   QQ 群 号:513683159 [相 ...

  5. 用c语言实现单链表的初始化,建表,查找,求长度,插入,删除等操作,【YTU+2430+C语言习题+链表建立+插入+删除+输(5)...

    的打印.判断链表是否为空.计算链表长度.插入节点.删除节点.删除整个链表.(2) 线性表adt顺序存储实现中的创建.查找.插入和删除等基本操作及相关算法,线性表adt链式存储实现中单链表.循环链表和双 ...

  6. 基于 Ruby 谈谈——程序设计语言的通用框架

    基于 Ruby GScript 谈谈--程序设计语言的通用框架 目录 基于 Ruby GScript 谈谈--程序设计语言的通用框架 一.架构思维导图 1.Ruby GScript 介绍 2.实践: ...

  7. java语言实现单链表---不含头结点

    java语言实现单链表---不含头结点 一.相关概念 1.什么是线性表 2.什么是顺序表 3.什么是链表 4.单链表.双链表.循环单链表.循环双链表 5.头结点和首结点 6.常见的栈和队列与线性表的关 ...

  8. js 创建一条通用链表

    js 创建一条通用链表 什么是「链表 科普 」? 链表是一种物理存储单元上非连续.非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的. 什么是「顺序存储结构 科普 」? 在计算机中用 ...

  9. c语言将一个已知头结点的单链表逆序_C语言数据结构实现链表逆序并输出

    C语言数据结构实现链表逆序并输出 将一个链表逆序并输出.我用了两种方法来实现,第一种是借助了一个新的空链表:第二种是在原来链表的基础上直接实现逆序. 实例代码: 头文件: #include #incl ...

最新文章

  1. java print快捷键_Java的ArrayList集合使用---唐代诗人斗地主!!!
  2. hihocoder 1127 : 二分图三·二分图最小点覆盖和最大独立集
  3. html标签info,HtmlTrInfo 元素
  4. linux 文本操作
  5. vue 深度拷贝数组_前端深拷贝和浅拷贝
  6. C++ map()和pair()用法
  7. worddayi计算机考试题,期末试卷
  8. easyui三级联动 html,jQuery使用EasyUi实现三级联动下拉框效果实例分享
  9. java选择题库_java题库1-单项选择题.doc
  10. 自己对war包解压的误区
  11. i7 8700k和i5 9400f哪个好 i78700k和i59400f性能差距
  12. 现代治理12.0:Diligent发出“现代领导力”倡议,帮助组织创建更具多元化和包容性的董事会和领导团队
  13. Axure计算器原型
  14. 静态网页与动态网页的差异
  15. html给页面整体添加左右边距_css div 网页的页面边距怎么控制
  16. 微软反linux广告图片,一张地铁站“前程无忧广告”的图片引发网友热议 官方:虚假图片...
  17. 如何来隐藏自己本地的ip地址进行上网?
  18. linux下设置共享目录
  19. 用stm32F103核心板的GPIOA端一管脚接一个LED,GPIOB端口一引脚接一个开关(用杜邦线模拟代替)。采用中断模式编程,当开关接高电平时,LED亮灯;接低电平时,LED灭灯。
  20. 贪心之最大相容子集合问题

热门文章

  1. csv文件导入sqlite
  2. 力扣——有序链表转换二叉搜索树
  3. 【Git】Git使用记录: 撤回已经commit到本地的提交记录
  4. Bootstrap 栅格 样式 组件 插件
  5. C++实现顺序栈的基本功能
  6. Servlet课程0425(五) sendRedirect实现不同页面共享数据
  7. Server Tomcat v6.0 Server at localhost was unable to stat within 45 seconds
  8. 使用sprc097的DSP281x_usDelay.asm
  9. Python学习笔记:网络编程
  10. 学习Matlab强大的符号计算(解方程)