数据结构-有头双向循环链表

list.h

#ifndef LIST_H__
#define LIST_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的数组,之前的标准是不支持的,为了方便我们定义char data[1]
    char data[1] ;//变长:data 就是变长数据的首地址:类似占位符
                  //如果char data;->找不到data 的地址
                  //直接可以把头节点的size放到data中,申请llist_node_st + size 的大小
};

//定义头节点LLIST类型,链表的起始地址
typedef struct llist_head
{
    int size ;
    struct llist_node_st head ;
    /*****************
     *封装方法
     ****************/
    int (*insert)(struct llist_head * , const void * data , int mode );
    void *(*find)(struct llist_head * ,const void *key ,llist_cmp *);
    int (*delete)(struct llist_head *,const void *key , llist_cmp * );
    int (*fetch)(struct llist_head * , const void *key ,llist_cmp * , void *data);
    void (*travel)(struct llist_head *,llist_op * );
}LLIST;


LLIST * llist_create(int initsize);
void llist_destroy(LLIST *);
#endif
View Code

list.c

/*
 *面向对象的思想
 * 支持变长结构体:在网络传输的过程,包的大小不固定的情况适用
 *实现create , delete ,insert , find , fetch等功能
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"

/*********************函数声明******************************/
int llist_insert(LLIST *ptr , const void * data , int mode );
void * llist_find(LLIST *ptr ,const void *key ,llist_cmp *);
int llist_delete(LLIST *ptr,const void *key , llist_cmp * );
void llist_travel(LLIST *ptr ,llist_op *op );
int llist_fetch(LLIST *ptr , const void *key ,llist_cmp *cmp , void *data);


/* ****************************
 * 功能:创建带头节点链表
 * 参数:initsize(变参的长度)
 * 返回:无
 * ***************************/
LLIST * llist_create(int initsize)
{
    //1.创建
    LLIST * new ;
    new = malloc(sizeof(*new));
    if(new == NULL)
        return NULL;
    //2.初始化
    new->size = initsize ;
    new->head.prev = &new->head ;
    new->head.next = &new->head ;
    new->insert = llist_insert ;
    new->delete = llist_delete ;
    new->travel = llist_travel ;
    new->find = llist_find ;
    new->fetch = llist_fetch ;
}

/* *******************************************************
 * 功能:以mode的方式向链表ptr中插入数据data
 * 参数:ptr:链表
 *       data:要插入的数据
 *       mode:插入的方式:FORWARD(头插) BACKWARD(尾插)
 * 返回:-1:内存申请失败,-2:方式不对,0:返回正确
 * *******************************************************/
int llist_insert(LLIST *ptr , const void * data , int mode )
{
    //1.创建新节点并分配空间
    struct llist_node_st *newnode ;
    newnode = malloc(sizeof(*newnode) + ptr->size );//申请空间就是结构体大小+头节点里的size元素
    if(newnode == NULL)
        return -1 ;
    //2.将data内容复制到新节点的数据中
    memcpy(newnode->data,data,ptr->size);
    //3.判断插入方式并插入
    if(mode == LLIST_FORWARD)
    {
        newnode->prev = &ptr->head ;
        newnode->next = ptr->head.next ;
    }
    else if(mode == LLIST_BACKWARD)
    {
        newnode->next = &ptr->head ;
        newnode->prev = ptr->head.prev ;
    }
    else
        return -2 ;
    newnode->prev->next = newnode ;
    newnode->next->prev = newnode ;
    return 0 ;
}

/* *******************************************************
 * 功能:以op的方式遍历链表ptr
 * 参数:ptr:链表
 *       op:遍历方式(main.c中使用print_s打印的方式遍历)
 * 返回:无
 * *******************************************************/
void llist_travel(LLIST *ptr ,llist_op *op )
{
    //1.创建一个节点
    struct llist_node_st *cur ;
    //2.cur节点从头的后继开始;以不循环到头节点为止 ;向后移动
    for (cur = ptr->head.next ; cur != &ptr->head ;cur = cur->next)
    {
        //3.将每个节点的data返回给用户
        op(cur->data) ;
    }
}

/* ******************
 * 功能:销毁链表ptr
 * 参数:ptr:链表
 * 返回:无
 * *****************/
void llist_destroy(LLIST *ptr)
{
    //1.创建当前节点以及当前节点的下一个节点
    struct llist_node_st *cur ;
    struct llist_node_st *next ;
    //2.当前节点从头的后继开始;并且不等于头节点地址 ;指向下一个节点
    for(cur = ptr->head.next ; cur != &ptr->head ; cur = next)
    {
        next = cur->next;
        //3.释放空间
        free(cur);
    }
    //4.释放头节点
    free(ptr);
}


/* *******************************************************
 * 功能:从链表ptr,按照cmp的比较方式,查找key  只能内部调用,非接口函数
 * 参数:ptr:链表
 *       key:查找内容
 *       cmp:比较函数
 * 返回:返回find符合的节点指针,没有符合返回NULL
 * *******************************************************/
static struct llist_node_st  * find_(LLIST * ptr ,const void *key ,llist_cmp *cmp)
{
    //1.创建节点
    struct llist_node_st *cur ;
    //2.当前节点从头的后继开始;并且不等于头节点地址 ;指向下一个节点
    for(cur = ptr->head.next ;cur != &ptr->head ; cur = cur->next)
    {
        //3.如果匹配跳出循环,并返回当前节点的指针
        if(cmp(key , cur->data) == 0 )
            break;
    }
    return cur ;
}

/* *******************************************************
 * 功能:从链表ptr,按照cmp的比较方式,查找key
 * 参数:ptr:链表
 *       key:查找内容
 *       cmp:比较函数
 * 返回:返回相应节点的数据起始地址,否则返回NULL
 * *******************************************************/
void * llist_find(LLIST * ptr ,const void *key ,llist_cmp *cmp)
{
    //1.创建节点
    struct llist_node_st *node ;
    //2.调用find_函数得到匹配的节点
    node = find_(ptr , key ,cmp);
    //3.没有找到匹配节点,返回空NULL
    if(node == &ptr->head)//头节点--没有找到
        return NULL;
    //4.找到节点返回节点的数据的起始地址
    return node->data;
}

/* *******************************************************
 * 功能:从链表ptr,按照cmp的比较方式,删除key的节点
 * 参数:ptr:链表
 *       key:查找内容
 *       cmp:比较函数
 * 返回:成功返回0,否则返回-1
 * *******************************************************/
int llist_delete(LLIST *ptr, const void *key,llist_cmp *cmp)
{
    //1.创建节点
    struct llist_node_st *node ;
    //2.找到匹配的节点,没找到返回-1
    node = find_(ptr, key ,cmp );
    if(node == &ptr->head)
        return -1 ;
    //3.更改前驱节点的后继、后继节点的前驱
    node->prev->next = node->next ;
    node->next->prev = node->prev ;
    //4.释放节点,正确返回0
    free(node) ;
    return 0 ;
}

/* *******************************************************
 * 功能:从链表ptr,按照cmp的比较方式,删除key,并将删除节点回填data
 * 参数:ptr:链表
 *       key:查找内容
 *       cmp:比较函数
 * 返回:返回相应节点的数据起始地址,否则返回NULL
 * *******************************************************/
int llist_fetch(LLIST *ptr , const void *key ,llist_cmp *cmp , void *data)
{
    //1.创建节点
    struct llist_node_st *node ;
    //2.找到匹配的节点,没有返回-1
    node = find_(ptr , key ,cmp);
    if(node == &ptr->head)
        return -1 ;
    //3.修改前驱节点的后继、修改后继节点的前驱
    node->prev->next = node->next ;
    node->next->prev = node->prev ;
    //4.将要删除的节点回填
    if(data != NULL)
    {
        memcpy(data , node->data , ptr->size);
    }
    //5.释放节点
    free(node);
    return 0 ;
}
    
                                                                           
View Code

create()函数结尾忘记加了return new ;

main.c

#include <stdlib.h>
#include <stdio.h>

#include "list.h"

#define NAMESIZE    32

//定义数据结构体
struct score_st
{
    int id ;
    char name[NAMESIZE] ;
    int math ;
    int chinese ;
};
//打印函数
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);

}
//name比较函数
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));

}

int main()
{
    //1.定义节点
    LLIST *handler;
    //2.定义数据结构
    struct score_st tmp ;
    //3.定义接收的数据结构指针
    struct score_st *data ;
    //4.变量i以及返回值ret
    int i ,ret ;
    //5.查找id 以及name
    int id = 3 ;
    char *del_name = "std6";

    /************************************/
    //1.创建节点:成功返回节点指针,失败返回NULL;参数为初始数据长度initsize
    handler = llist_create (sizeof(struct score_st));
    if(handler == NULL)
        exit(1);
    //2.链表数据赋值:使用insert方式
    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 = handler->insert(handler,&tmp,LLIST_BACKWARD);
        if(ret)
            exit(1);
    }
    //3.链表遍历
    handler->travel(handler,print_s);
    printf("\n\n");
    //4.链表数据查找
    data = handler->find(handler , &id ,id_cmp);
    if(data == NULL)
        printf("can not find \n");
    else
        print_s(data);

    printf("\n\n");
    //5.链表数据删除
    ret = handler->delete(handler ,del_name  ,name_cmp);
    if(ret)
        printf("delete failed \n");
    //6.链表遍历
    handler->travel(handler,print_s);
    //7.链表销毁
    llist_destroy(handler);
    exit(0);
}
View Code

Makefile

all :main
main:list.o main.o
    $(CC) $^ -o $@
clean:
    rm *.o main -rf
View Code

 

posted @ 2016-03-03 13:49  muzihuan  阅读(294)  评论(0编辑  收藏  举报