前言
适用性较广的双向环链。实现比较困难,主要看看怎么使用。
代码的使用方法:
- 移植llist.h和llist.c文件
- 定义适合自己的结构体类型,比如
struct score_st //用户的数据
{
int id;
char name[NAMESIZE];
int math;
int chinese;
};
- 自定义一个打印函数,如:
printf()括号里面根据实际情况修改。
static void print_s(const void *record)
{
const struct score_st *r = record;//类型转换
printf("%d %s %d %d\n",r->id,r->name,r->math,r->chinese); //改这里
}
- 自定义按某个关键字查找的函数,如按id查找
static int id_cmp(const void *key,const void *record)
{
const int *k = key; //类型转换
const struct score_st *r = record; //改成你的结构体(score_st)
return (*k - r->id); //改这里
}
示例代码1
main.c文件
#include <stdio.h>
#include "llist.h"
#define NAMESIZE 32
//用户的数据
struct score_st
{
int id;
char name[NAMESIZE];
int math;
int chinese;
};
/*函数声明*/
static void print_s(const void *record);
static int id_cmp(const void *key,const void *record);
static int name_cmp(const void *key,const void *record);
int main()
{
int i,ret;
struct score_st tmp;//定义用户数据的结构体
LLIST *handler; //定义头指针
//创建链表时指定结构体的大小
handler = llist_create(sizeof(struct score_st));
for(i=0;i<7;i++) //循环插入数据
{
tmp.id = i;
snprintf(tmp.name,NAMESIZE,"std%d",i);
tmp.math = rand()%100;
tmp.chinese = rand()%100;
//ret = llist_insert(handler,&tmp,LLIST_FORWARD); //首部插入
ret = llist_insert(handler,&tmp,LLIST_BACKWARD); //尾部插入
if(ret) exit(1);
}
llist_travel(handler,print_s); //遍历链表
printf("\n\n");
int id = 3;
struct sroce *data;
data = llist_find(handler,&id,id_cmp);
if(data==NULL)
{
printf("找不到\n");
}
else
{
print_s(data);
}
printf("\n\n");
char *del_name = "std6";
//ret = llist_delete(handler,&id,id_cmp);
ret = llist_delete(handler,del_name,name_cmp);
if(ret)
{
printf("delete失败\n");
}
llist_travel(handler,print_s);
llist_destroy(handler);
return 0;
}
/*函数实现*/
//自定义的打印函数:
static void print_s(const void *record)
{
const struct score_st *r = record;//类型转换
printf("%d %s %d %d\n",r->id,r->name,r->math,r->chinese);
}
//用户定义的按id查找的函数
static int id_cmp(const void *key,const void *record)
{
const int *k = key; //类型转换
const struct score_st *r = record;
return (*k - r->id);
}
//用户定义的按名字查找的函数
static int name_cmp(const void *key,const void *record)
{
const char *k = key;
const struct score_st *r = record;
return strcmp(k,r->name);
}
llist.h文件
#ifndef LLIST_H__
#define LLIST_H__
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define LLIST_FORWARD 1
#define LLIST_BACKWARD 2
//重定义函数类型
typedef void llist_op(const void *);//
typedef int llist_cmp(const void *,const void *);
//有效节点的结构体,变长结构体
struct llist_node_st
{
//void *data; //数据域
struct llist_node_st *prev;
struct llist_node_st *next;
char data[0];//数组名是地址,c99支持[0]
};
//头节点的结构体
typedef struct
{
int size;
struct llist_node_st head;
}LLIST;
//函数声明部分
LLIST *llist_create(int initsize);
int llist_insert(LLIST *,const void *data,int mode);
void *llist_find(LLIST *,const void *key,llist_cmp *);
int llist_delete(LLIST *, const void *key,llist_cmp *);
int llist_fetch(LLIST *,const void *key,llist_cmp *,void *data);
void llist_travel(LLIST *,llist_op *);
void llist_destroy(LLIST *);
#endif
llist.c文件
#include "llist.h"
//创建只包含头节点的空双向环链
LLIST *llist_create(int initsize)
{
LLIST *new;
new = malloc(sizeof(*new));
if(new==NULL) return NULL;
new->size = initsize; //用户数据的大小
new->head.prev = &new->head; //形成环
new->head.next = &new->head;
return new; //返回头指针
}
/*
断2个,连4个
若已知插入的前驱节点p
q->prev = p; //确定插入节点q的前驱
q->next = p->next //确定插入节点q的后继
q->prev->next = q; //断开q的前驱和后继的连接
q->next->prev = q; //断开q的前驱和后继的连接
若已知插入节点的后继节点p
q->next = p;
q->prev = p->prev;
q->prev->next = q;
q->next->prev = q;
*/
//节点的插入
int llist_insert(LLIST *ptr,const void *data,int mode)
{
struct llist_node_st *newnode;
//申请足够大的内存空间
newnode = malloc(sizeof(*newnode)+ptr->size);
if(newnode==NULL) return -1;
//内存拷贝
memcpy(newnode->data,data,ptr->size);
//首部插入,已知插入的前驱
if(mode == LLIST_FORWARD)
{
newnode->prev = &ptr->head;
newnode->next = ptr->head.next;
}
//尾部插入,已知插入的后继
else if(mode == LLIST_BACKWARD)
{
newnode->prev = ptr->head.prev;
newnode->next = &ptr->head;
}
else
{
return -3;
}
newnode->prev->next = newnode;
newnode->next->prev = newnode;
return 0;
}
//在链表ptr中寻找*key,找的方式是cmp
static struct llist_node_st *find_(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *cur;
for(cur=ptr->head.next;cur!=&ptr->head;cur=cur->next)
{
//找到时,cmp()返回0
if(cmp(key,cur->data)== 0) break;
}
return cur; //返回当前节点
}
void *llist_find(LLIST *ptr,const void *key,llist_cmp *cmp)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if(node == &ptr->head) return NULL;
return node->data;
}
//删除节点:在ptr指向的链表里寻找key,寻找的方式是 cmp
int llist_delete(LLIST *ptr, const void *key,llist_cmp *cmp)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
// 不删头节点
if(node==&ptr->head) return -1;
//删除node 节点
node->prev->next = node->next;
node->next->prev = node->prev;
free(node);
return 0;
}
//拿出节点:在ptr指向的链表里寻找key,寻找的方式是 cmp,返回拿出的数据
int llist_fetch(LLIST *ptr,const void *key,llist_cmp *cmp,void *data)
{
struct llist_node_st *node;
node = find_(ptr,key,cmp);
if(node==&ptr->head) return -1;
node->prev->next = node->next;
node->next->prev = node->prev;
if(data!=NULL)
{
memcpy(data,node->data,ptr->size);
}
free(node);
return 0;
}
//遍历链表:在ptr指向的链表里以op的方式遍历
void llist_travel(LLIST *ptr,llist_op *op)
{
struct llist_node_st *cur,*next;
//cur指向第1个有效节点;cur不指向头节点;cur不停往后指向
for(cur=ptr->head.next;cur!=&ptr->head;cur=cur->next)
{
op(cur->data); //回调函数
}
}
//销毁链表
void llist_destroy(LLIST *ptr)
{
struct llist_node_st *cur,*next;
//让cur指向第1个有效节点;头节点在循环不删;cur不停地向后移动
for(cur=ptr->head.next;cur!=&ptr->head;cur=next)
{
next = cur->next; //保存当前节点的下一节点
free(cur); //销毁当前节点
}
free(ptr); //销毁头节点
}
运行结果
示例代码2
简单应用
main.c文件
#include <stdio.h>
#include "llist.h"
#define NAMESIZE 32
struct student_st
{
char name[NAMESIZE];
int height;
int weight;
};
static void print_s(const void *record);
int main()
{
int i,ret;
struct student_st tmp;
LLIST *handler;
handler = llist_create(sizeof(struct student_st));
printf("分别输入姓名 身高 体重:\n");
for(i=0;i<3;i++)
{
scanf("%s%d%d",tmp.name,&tmp.height,&tmp.weight);
llist_insert(handler,&tmp,LLIST_FORWARD); //首部插入
if(ret) exit(1);
}
printf("打印结果\n");
llist_travel(handler,print_s);
llist_destroy(handler);
return 0;
}
//用户根据实际情况自定义的打印函数
static void print_s(const void *record)
{
const struct student_st *r = record;//类型转换
printf("%s %d %d\n",r->name,r->height,r->weight);
}
运行结果