实现一个自己的双向链表

问题引入

  • C 在写很多东西的时候都会牵扯到一些基础的数据结构的使用,而双向链表可以封装成好几种常用的数据结构,所以想着自己实现一个自己的双向链表,也会在以后慢慢完善此代码。

接口设计

/*
初始化双向链表
return value :
	0 : 成功
	-1 : malloc() 分配内存时出现错误
*/
int llist_init(d_llist_t **llist);

/*
往 llist 链表中插入值 *key,compare 为回调比较大小的函数,用户自己实现
这里设定 key 为堆上的内存地址,在 llist_destroy() 中会 free() 保证内存不会泄露
return value:
	0 : 成功
	-1 : malloc() 分配内存时出错
*/
typedef int (*compare)(void *a, void *b);
/*
compare return value:
        1 : a > b
        -1 : a < b
        0 : a == b
*/
int llist_insert(d_llist_t *llist, void *key, compare cmp);

/*
拿出链表对应位置的节点的 key,会从链表中删除节点
返回值为 key 本身,原节点已从链表中删除并 free,使用完后记得 free(key)
return value:
	NULL : 链表为空
	非空 : 对应位置的 key
*/
d_llist_node_t *llist_pop_first(d_llist_t *llist);
d_llist_node_t *llist_pop_last(d_llist_t *llist);

/*
查看链表对应位置的节点的 key,但不从链表中删除节点
return value :
	NULL : 链表为空
	非空 : key
*/
d_llist_node_t *llist_head_look(d_llist_t *llist);
d_llist_node_t *llist_tail_look(d_llist_t *llist);

/*
销毁链表
return value:
	0 : 成功
	其他情况 : 未知错误
*/
int llist_destroy(d_llist_t *llist);

/*
链表的遍历,针对 key 的输出格式,给出了回调接口
return value:
	0 : 成功
	其他情况 : 未知错误
*/
typedef int (*traver)(void *node);
int llist_traver(d_llist_t *llist, traver tvr);

/*
检查链表是否为用户指定的有序链表
return value:
	0 : 有序
	-1 : 不是有序的
*/
// typedef int (*compare)(void *a, void *b);
int llist_check(d_llist_t *llist, compare cmp);

代码

double_llist.h

#ifndef DOUBLE_LLIST__
#define DOUBLE_LLIST__

typedef void d_llist_node_t;
typedef void d_llist_t;

int llist_init(d_llist_t **llist);

typedef int (*compare)(void *a, void *b);
int llist_insert(d_llist_t *llist, void *key, compare cmp);

d_llist_node_t *llist_pop_first(d_llist_t *llist);
d_llist_node_t *llist_pop_last(d_llist_t *llist);

d_llist_node_t *llist_head_look(d_llist_t *llist);
d_llist_node_t *llist_tail_look(d_llist_t *llist);

int llist_destroy(d_llist_t *llist);

typedef int (*traver)(void *node);
int llist_traver(d_llist_t *llist, traver tvr);

// typedef int (*compare)(void *a, void *b);
int llist_check(d_llist_t *llist, compare cmp);


#endif

double_llist.c

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

#include "./double_llist.h"


typedef struct llist_node_s{

    void *key;	// key 指向用户传进来的数据的堆内存地址
    struct llist_node_s *prev;
    struct llist_node_s *next;

}llist_node_t;

typedef struct llist_s{
	// 这块也可以根据实际情况加一些其他元素
	
    llist_node_t *head;	// 链表的头节点
    llist_node_t *tail;	// 链表的尾节点

}llist_t;


int llist_init(void **llist){

    llist_t *me = NULL;

    me = (llist_t*)malloc(sizeof(*me));
    if(me == NULL){
        return -1;
    }

    me->head = NULL;
    me->tail = NULL;

    *llist = me;

    return 0;
}

/*
    typedef int (*compare)(void *a, void *b);
    compare return value:
        1 : a > b
        -1 : a < b
        0 : a == b
*/
int llist_insert(d_llist_t *llist, void *key, compare cmp){

    llist_t *me = llist;
    llist_node_t *cur = me->head;
    llist_node_t *node_ = NULL;

    node_ = (llist_node_t*)malloc(sizeof(*node_));
    if(node_ == NULL){
        return -1;
    }
    node_->next = NULL;
    node_->prev = NULL;
    node_->key = key;

    int res = 0;

    while(cur != NULL){

        res = cmp(key, cur->key);
        if(res > 0){
            cur = cur->next;
        }else{  // res < 0 || res == 0
            break;
        }

    }


    if(cur == NULL){
        // insert on tail
        // or head = tail = NULL, llist is null
        node_->prev = me->tail;
        node_->next = NULL;
        if(me->tail != NULL){
            me->tail->next = node_;
            me->tail = node_;
        }else{
            me->tail = node_;
        }
        if(me->head == NULL){
            me->head = node_;
        }

    }else if(cur == me->head){
        // insert on head
        node_->next = me->head;
        node_->prev = NULL;
        me->head->prev = node_;
        me->head = node_;

    }else{

        node_->next = cur;
        node_->prev = cur->prev;
        cur->prev->next = node_;
        cur->prev = node_;

    }

    return 0;
}

void *llist_pop_first(void *llist){

    llist_t *me = llist;
    llist_node_t *cur = me->head;

    if(cur == NULL){

        return NULL;

    }else if(cur->next == NULL){

        me->head = NULL;
        me->tail = NULL;

    }else{

        me->head = cur->next;
        me->head->prev = NULL;

    }

    void *ret = cur->key;

    free(cur);

    return ret;
}

void *llist_pop_last(void *llist){

    llist_t *me = llist;
    llist_node_t *cur = me->tail;

    if(cur == NULL){
        return NULL;
    }else if(cur->prev == NULL){
        me->head = NULL;
        me->tail = NULL;
    }else{
        me->tail = cur->prev;
        me->tail->next = NULL;
    }


    void *ret = cur->key;

    free(cur);

    return ret;
}

int llist_destroy(void *llist){

    llist_t *me = llist;
    llist_node_t *cur = me->head, *temp = NULL;

    while(cur != NULL){

        temp = cur->next;

        free(cur->key);
        free(cur);

        cur = temp;

    }

    free(me);

    llist = NULL;

    return 0;
}

/*
typedef int (*traver)(void *node);
*/
int llist_traver(void *llist, traver tvr){

    llist_t *me = llist;
    llist_node_t *cur = me->head;

    while(cur != NULL){

        tvr(cur->key);

        cur = cur->next;
        if(cur){
            printf(" -> ");
        }
    }
    printf("\n");

    return 0;
}


int llist_check(void *llist, compare cmp){

    llist_t *me = llist;
    llist_node_t *cur = me->head;

    if(cur != NULL && cur->next != NULL){
        cur = cur->next;
    }else{
        return 0;
    }

    while(cur != NULL){

        if(cmp(cur->key, cur->prev->key) < 0){
            return -1;
        }
        cur = cur->next;

    }

    return 0;
}




d_llist_node_t *llist_head_look(d_llist_t *llist){

    llist_t *me = llist;

    if(me->head == NULL){
        return NULL;
    }

    return me->head->key;
}
d_llist_node_t *llist_tail_look(d_llist_t *llist){

    llist_t *me = llist;

    if(me->tail == NULL){
        return NULL;
    }

    return me->tail->key;
}

测试代码 test.c

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

#include "./double_llist.h"


// 用户根据自己的数据类型实现的比较函数
int mycompare(void *a, void *b){
    int *x = a;
    int *y = b;

    return (*x) - (*y);
}

// 自定义输出函数
int mytraver(void *key){

    int *x = key;

    printf("[%d]", *x);
    
    return 0;
}

int main(){

    d_llist_t *llist = NULL;
    int ret = 0;
	
	// init 链表
    ret = llist_init(&llist);
    if(ret){
        puts("init()");
        exit(1);
    }

    srand((unsigned)time(NULL));
    int i = 0;
    for(i = 0; i < 1024 * 16; i++){
		
		// 链表中添加的元素需要开辟在堆上
        int *key = (int*)malloc(sizeof(int));

        (*key) = (rand() % 1024 + 1);
        // 添加元素
        // 若不需要指定排序,还可以指定头插还是尾插,这个以后再实现
        ret = llist_insert(llist, key, mycompare);

		// 测试 取出数据
        if(i % 2){
            llist_pop_last(llist);
            // llist_pop_first(llist);
        }

    }
		
	// 遍历链表
    // llist_traver(llist, mytraver);

	// 检查链表是否有序
    ret = llist_check(llist, mycompare);
    if(ret){
        printf("error\n");
    }else{
        printf("ok\n");
    }

	// 查看链表第一个节点的 key
    int *data = llist_head_look(llist);
    if(data != NULL){
        mytraver(data);
    }else{
        printf("llist is empty\n");
    }

    data = NULL;
	// 查看链表最后一个节点的 key
    data = llist_tail_look(llist);
    if(data != NULL){
        mytraver(data);
    }else{
        printf("llist is empty\n");
    }

	// 用完销毁链表,因为其中有开辟在堆上的内存,不删除会导致内存泄漏
	// 添加元素时开辟在堆上的 key 也会在这里 free
    ret = llist_destroy(llist);
    if(ret){
        puts("destroy()");
        exit(1);
    }

    exit(0);
}

总结

功能列表

  1. 数据都是用户自己定义
  2. 链表的有序 / 无序插入,如何排序可以由用户自己控制
  3. 链表的首尾取出 / 查看
  4. 检测链表是否有序
  5. 链表的遍历, 打印格式也可以由用户自己设计,同时也可以打成日志

待添加功能

  1. 搜索查看 / 取出指定节点的 key;
  2. 可以在链表的结构体中添加一些额外的变量,用来记录链表中元素个数等等情况;\
  3. 更多的添加节点的方式,如头插法和尾插法;
  4. 等哪天用到的时候再总结.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值