双向循环链表

前言
适用性较广的双向环链。实现比较困难,主要看看怎么使用。
代码的使用方法:

  1. 移植llist.h和llist.c文件
  2. 定义适合自己的结构体类型,比如
struct score_st  //用户的数据
{
    int id;
    char name[NAMESIZE];
    int math;
    int chinese;
};
  1. 自定义一个打印函数,如:
    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);  //改这里
}
  1. 自定义按某个关键字查找的函数,如按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);
}

运行结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

⁽⁽ଘ晴空万里ଓ⁾⁾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值