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

假设不采用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 test struct 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.移动Cur
for (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)//回调函数,把链表中的每一个数据和用户传入的关键字做比较,符合条件返回1
return cur;//返回节点的地址给用户
}
return NULL;//没有找到返回空指针
}

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

5.构造和析构

void slist_init(struct slist_info *info)
{
//空链表, 第一个节点为NULL
info->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语言实现单链表-03版

    在C语言实现单链表-02版中我们只是简单的更新一下链表的组织方式: 它没有更多的更新功能,因此我们这个版本将要完成如下功能: Problem 1,搜索相关节点: 2,前插节点: 3,后追加节点: 4, ...

  5. C语言实现单链表-02版

    我们在C语言实现单链表-01版中实现的链表非常简单: 但是它对于理解单链表是非常有帮助的,至少我就是这样认为的: 简单的不能再简单的东西没那么实用,所以我们接下来要大规模的修改啦: Problem 1 ...

  6. C语言实现单链表,并完成链表常用API函数

    C语言实现单链表,并完成链表常用API函数: 1.链表增.删.改.查. 2.打印链表.反转打印.打印环形链表. 3.链表排序.链表冒泡排序.链表快速排序. 4.求链表节点个数(普通方法.递归方法). ...

  7. C 封装一个通用链表 和 一个简单字符串开发库

    引言 这里需要分享的是一个 简单字符串库和 链表的基库,代码也许用到特定技巧.有时候回想一下, 如果我读书的时候有人告诉我这些关于C开发的积淀, 那么会走的多直啊.刚参加工作的时候做桌面开发, 服务是 ...

  8. C语言实现单链表节点的删除(带头结点)

    我在之前一篇博客<C语言实现单链表节点的删除(不带头结点)>中具体实现了怎样在一个不带头结点的单链表的删除一个节点,在这一篇博客中我改成了带头结点的单链表.代码演示样例上传至 https: ...

  9. C/C++语言实现单链表(带头结点)

    彻底理解链表中为何使用二级指针或者一级指针的引用 数据结构之链表-链表实现及常用操作(C++篇) C语言实现单链表,主要功能为空链表创建,链表初始化(头插法),链表元素读取,按位置插入,(有序链表)按 ...

随机推荐

  1. div 与 table 的优点

    div+css布局的好处: 1.符合W3C标准,代码结构清晰明了,结构.样式和行为分离,带来足够好的可维护性. 2.布局精准,网站版面布局修改简单. 3.加快了页面的加载速度(最重要的)(在IE中要将 ...

  2. 提取pfx证书公钥和私钥

    从pfx提取密钥信息,并转换为key格式(pfx使用pkcs12模式补足) 1.提取密钥对(如果pfx证书已加密,会提示输入密码.) openssl pkcs12 -in 1.pfx -nocerts ...

  3. day03-Linux操作系统目录结构

    一. Linux系统目录表示        ‘/’表示根目录:                                                            ‘.’表示当前目录 ...

  4. MPI编程指南

    MPI编程指南 一.     MPI概述 1.1  MPI的发展史 MPI标准化涉及到大约60个国家的人们,他们主要来自于美国和欧洲的40个组织,这包括并行计算机的多数主要生产商,还有来自大学.政府实 ...

  5. kuangbin专题16D(next求最小循环节)

    题目链接: https://vjudge.net/contest/70325#problem/D 题意: 给出一个循环字符串, 可以在两端添加任意字符, 问最少添加多少字符可以使循环字符串变成周期循环 ...

  6. CF1101C Division and Union 线段相交问题

    #include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #i ...

  7. centos上安装docker

    一 docker安装: 1 首先需要检查linux内核的版本,docker要求linux内核是在3.10之上的, uname -r 2 更新yum源,注意这步应该是管理员权限,如果当前不是管理员,切换 ...

  8. vue学习三:生命周期钩子

    生命周期钩子介绍: 每个 Vue 实例在被创建时都要经过一系列的初始化过程——例如,需要设置数据监听.编译模板.将实例挂载到 DOM 并在数据变化时更新 DOM 等.同时在这个过程中也会运行一些叫做生 ...

  9. Unity 关节

    引言 关节组件一共分为5大类,它们分别是链条关节.固定关节.弹簧关节.角色关节和可配置关节. 链条关节(Hinge Joint):将两个物体以链条的形式绑在一起,当力量过大超过链条的固定力矩时,两个物 ...

  10. hdu-2036求任意多边形面积

    改革春风吹满地 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...