C语言通用双向链表

本文提供一种C语言双向链表的通用接口,且均使用宏描述,故仅需包含头文件。该接口在实际应用中使用更方便、灵活。

一、基本结构在这里插入图片描述

BASE结点:即链表的结构体,主要记录链表的 第一个(start)最后一个(end) 结点指针。
结点n:用户定义的结构体,除了数据外还需要包含 指针域(link),用于双向链表的连接。

二、结点定义方法

1. BASE结点

#define LIST_BASE_NODE_T(TYPE)\
struct\
{\
    unsigned int    count;\
    TYPE*           start;\
    TYPE*           end;\
}

count为该链表的结点总数,实际应用场景经常会用到。

2. 结点指针域

#define LIST_NODE_T(TYPE)\
struct\
{\
    TYPE*           prev;\
    TYPE*           next;\
}

示例

此刻有结构体 test_node_t

typedef struct test_node_struct test_node_t;
struct test_node_struct
{
    int         value;
};

仅需要为其添加指针域,以及链表的定义,以便使用后面封装的接口。

typedef struct test_node_struct test_node_t;
struct test_node_struct
{
    int         value;
    LIST_NODE_T(test_node_t) link;
};
typedef LIST_BASE_NODE_T(test_node_t) test_list_t;

注:可以添加多个指针域link1、link2…以便该结点可以同时存在于多个链表中。

三、基本操作接口

1. ADD FIRST

/* 添加结点N到链表的第一个 */
#define LIST_ADD_FIRST(LINK, BASE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = NULL;\
    (N)->LINK.next = (BASE).start;\
    /* 如果start存在, 设置N为它的prev */\
    if ((BASE).start)\
    {\
        (BASE).start->LINK.prev = (N);\
    }\
    /* 如果start不存在, 要设置N为end, 最后设置N为start */\
    else\
    {\
        (BASE).end = (N);\
    }\
    (BASE).start = (N);\
} while (0)

2. ADD LAST

/* 添加结点N到链表的最后一个 */
#define LIST_ADD_LAST(LINK, BASE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (BASE).end;\
    (N)->LINK.next = NULL;\
    /* 如果end存在, 设置N为它的next */\
    if ((BASE).end)\
    {\
        (BASE).end->LINK.next = (N);\
    }\
    /* 如果end不存在, 要设置N为start, 最后设置N为end */\
    else\
    {\
        (BASE).start = (N);\
    }\
    (BASE).end = (N);\
} while (0)

3. INSERT BEFORE

/* 插入结点N到结点NODE的前面 */
#define LIST_INSERT_BEFORE(LINK, BASE, NODE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (NODE)->LINK.prev;\
    (N)->LINK.next = (NODE);\
    /* 如果NODE的prev存在, 设置N为它的next */\
    if ((NODE)->LINK.prev)\
    {\
        (NODE)->LINK.prev->LINK.next = (N);\
    }\
    /* 如果NODE的prev不存在, 要设置N为start, 最后设置N为NODE的prev */\
    else\
    {\
        (BASE).start = (N);\
    }\
    (NODE)->LINK.prev = (N);\
} while (0)

4. INSERT AFTER

/* 插入N到结点NODE的后面 */
#define LIST_INSERT_AFTER(LINK, BASE, NODE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (NODE);\
    (N)->LINK.next = (NODE)->LINK.next;\
    /* 如果NODE的next存在, 设置N为它的prev */\
    if ((NODE)->LINK.next)\
    {\
        (NODE)->LINK.next->LINK.prev = (N);\
    }\
    /* 如果NODE的next不存在, 要设置N为end, 最后设置N为NODE的next */\
    else\
    {\
        (BASE).end = (N);\
    }\
    (NODE)->LINK.next = (N);\
} while (0)

5. REMOVE

/* 从链表中移除结点 */
#define LIST_REMOVE(LINK, BASE, N)\
do\
{\
    (BASE).count--;\
    /* 如果N的prev存在, 设置N的next为它的next */\
    if ((N)->LINK.prev)\
    {\
        (N)->LINK.prev->LINK.next = (N)->LINK.next;\
    }\
    /* 如果N的prev不存在, 设置N的next为start */\
    else\
    {\
        (BASE).start = (N)->LINK.next;\
    }\
    /* 如果N的next存在, 设置N的prev为它的prev */\
    if (((N)->LINK).next)\
    {\
        (N)->LINK.next->LINK.prev = (N)->LINK.prev;\
    }\
    /* 如果N的next不存在, 设置N的prev为end */\
    else\
    {\
        (BASE).end = (N)->LINK.prev;\
    }\
    /* 最后清空指针域 */\
    (N)->LINK.prev = NULL;\
    (N)->LINK.next = NULL;\
} while (0)

四、其他接口

/* 获取链表长度 */
#define LIST_GET_LEN(BASE)      ((BASE).count)

/* 获取链表第一个结点 */
#define LIST_GET_FIRST(BASE)    ((BASE).start)

/* 获取链表最后一个结点 */
#define LIST_GET_LAST(BASE)     ((BASE).end)

/* 获取N的上一个结点 */
#define LIST_GET_PREV(LINK, N)  ((N)->LINK.prev)

/* 获取N的下一个结点 */
#define LIST_GET_NEXT(LINK, N)  ((N)->LINK.next)

/* 从头开始向后查找结点 */
#define LIST_SEARCH_FROM_FIRST(LINK, BASE, N, TEST)\
do\
{\
    (N) = LIST_GET_FIRST(BASE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_NEXT(LINK, N);\
    }\
} while (0)

/* 从末尾开始向前查找结点 */
#define LIST_SEARCH_FROM_LAST(LINK, BASE, N, TEST)\
do\
{\
    (N) = LIST_GET_LAST(BASE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_PREV(LINK, N);\
    }\
} while (0)

/* 从结点NODE开始向前查找结点 */
#define LIST_SEARCH_BEFORE_NODE(LINK, BASE, NODE, N, TEST)\
do\
{\
    (N) = (NODE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_PREV(LINK, N);\
    }\
} while (0)

/* 从结点NODE开始向后查找结点 */
#define LIST_SEARCH_AFTER_NODE(LINK, BASE, NODE, N, TEST)\
do\
{\
    (N) = (NODE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_NEXT(LINK, N);\
    }\
} while (0)

测例

#include "list.h"
#include <assert.h>

typedef struct node_struct node_t;
struct node_struct
{
    int         value;
    LIST_NODE_T(node_t) link1;
    LIST_NODE_T(node_t) link2;
};
typedef LIST_BASE_NODE_T(node_t) list_t;

void test_list()
{
    const int       num = 1000;
    int             i;
    node_t*         n;
    node_t*         n1;
    node_t*         n2;
    node_t*         n_prev;
    node_t*         n_next;
    node_t*         n249;
    node_t*         n499;
    node_t*         n500;
    node_t*         n750;
    list_t          L1 = { 0 }, L2 = { 0 };

    /* 申请, 按顺序添加到L1 */
    for (i = 0; i < num; i++)
    {
        n = malloc(sizeof(node_t));
        assertx(n);
        n->value = i;
        LIST_ADD_LAST(link1, L1, n);
    }

    /* 向后遍历L1, 找到结点249 */
    LIST_SEARCH_FROM_FIRST(link1, L1, n249, n249->value == 249);
    /* 向前遍历L1, 找到结点750 */
    LIST_SEARCH_FROM_LAST(link1, L1, n750, n750->value == 750);
    /* 从结点249向后遍历L1, 找到结点499 */
    LIST_SEARCH_AFTER_NODE(link1, L1, n249, n499, n499->value == 499);
    /* 从结点750向前遍历L1, 找到结点500 */
    LIST_SEARCH_BEFORE_NODE(link1, L1, n750, n500, n500->value == 500);
    assert(n499 && n500 && LIST_GET_NEXT(link1, n499) == n500 && LIST_GET_PREV(link1, n500) == n499);

    /* 从结点249往前, 逐个添加到L2开始(0-249) */
    n = n249;
    while (n)
    {
        LIST_ADD_FIRST(link2, L2, n);
        n = LIST_GET_PREV(link1, n);
    }
    /* 从结点750往后, 逐个添加到L2最后(750-999) */
    n = n750;
    while (n)
    {
        LIST_ADD_LAST(link2, L2, n);
        n = LIST_GET_NEXT(link1, n);
    }
    /* 从结点250开始, 直到结点499, 逐个插入到L2(250-499) */
    n = n249;
    do
    {
        n_prev = n;
        n = LIST_GET_NEXT(link1, n);
        LIST_INSERT_AFTER(link2, L2, n_prev, n);
    } while (n != n499);
    /* 从结点749开始, 直到结点500, 逐个插入到L2(500-749) */
    n = n750;
    do
    {
        n_next = n;
        n = LIST_GET_PREV(link1, n);
        LIST_INSERT_BEFORE(link2, L2, n_next, n);
    } while (n != n500);

    assert(LIST_GET_LEN(L1) == num && LIST_GET_LEN(L2) == num);
    /* 验证L1和L2中结点顺序是否一致 */
    n1 = LIST_GET_FIRST(L1);
    n2 = LIST_GET_FIRST(L2);
    while (n1 && n2)
    {
        assertx(n1->value == n2->value);
        n1 = LIST_GET_NEXT(link1, n1);
        n2 = LIST_GET_NEXT(link2, n2);
    }

    /* 从L1中移除所有结点 */
    n = LIST_GET_FIRST(L1);
    while (n)
    {
        LIST_REMOVE(link1, L1, n);
        n = LIST_GET_FIRST(L1);
    }
    /* 释放 */
    n = LIST_GET_LAST(L2);
    while (n)
    {
        LIST_REMOVE(link2, L2, n);
        free(n);
        n = LIST_GET_LAST(L2);
    }
    return;
}

源码(list.h)

/**************************************************************
文件:
    list.h
目的:
    提供双向链表的方法和接口
接口:
    LIST_BASE_NODE_T            定义链表类型
    LIST_NODE_T                 定义链表结点的指针域

    LIST_INIT                   链表初始化
    LIST_NODE_INIT              结点初始化

    LIST_ADD_FIRST              添加到链表的第一个
    LIST_ADD_LAST               添加到链表的最后一个
    LIST_INSERT_BEFORE          插入到结点之前
    LIST_INSERT_AFTER           插入到结点之后
    LIST_REMOVE                 从链表中移除结点

    LIST_GET_LEN                获取链表长度
    LIST_GET_FIRST              获取链表第一个结点
    LIST_GET_LAST               获取链表最后一个结点
    LIST_GET_PREV               获取上一个结点
    LIST_GET_NEXT               获取下一个结点

    LIST_SEARCH_FROM_FIRST      从头开始向后查找结点
    LIST_SEARCH_FROM_LAST       从末尾开始向前查找结点
    LIST_SEARCH_BEFORE_NODE     从结点NODE开始向前查找结点
    LIST_SEARCH_AFTER_NODE      从结点NODE开始向后查找结点
**************************************************************/
#ifndef LIST_H
#define LIST_H

/* 说明: 用于定义链表类型 */
#define LIST_BASE_NODE_T(TYPE)\
struct\
{\
    unsigned int    count;\
    TYPE*           start;\
    TYPE*           end;\
}

/* 说明: 用于定义指针域 */
#define LIST_NODE_T(TYPE)\
struct\
{\
    TYPE*           prev;\
    TYPE*           next;\
}

/* 初始化链表 */
#define LIST_INIT(BASE)\
do\
{\
    (BASE).count = 0;\
    (BASE).start = NULL;\
    (BASE).end   = NULL;\
} while (0)

/* 初始化结点的指针域 */
#define LIST_NODE_INIT(LINK, N)\
do\
{\
    (N)->LINK.prev = NULL;\
    (N)->LINK.next = NULL;\
} while (0)

/* 添加结点N到链表的第一个 */
#define LIST_ADD_FIRST(LINK, BASE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = NULL;\
    (N)->LINK.next = (BASE).start;\
    /* 如果start存在, 设置N为它的prev */\
    if ((BASE).start)\
    {\
        (BASE).start->LINK.prev = (N);\
    }\
    /* 如果start不存在, 要设置N为end, 最后设置N为start */\
    else\
    {\
        (BASE).end = (N);\
    }\
    (BASE).start = (N);\
} while (0)

/* 添加结点N到链表的最后一个 */
#define LIST_ADD_LAST(LINK, BASE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (BASE).end;\
    (N)->LINK.next = NULL;\
    /* 如果end存在, 设置N为它的next */\
    if ((BASE).end)\
    {\
        (BASE).end->LINK.next = (N);\
    }\
    /* 如果end不存在, 要设置N为start, 最后设置N为end */\
    else\
    {\
        (BASE).start = (N);\
    }\
    (BASE).end = (N);\
} while (0)

/* 插入结点N到结点NODE的前面 */
#define LIST_INSERT_BEFORE(LINK, BASE, NODE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (NODE)->LINK.prev;\
    (N)->LINK.next = (NODE);\
    /* 如果NODE的prev存在, 设置N为它的next */\
    if ((NODE)->LINK.prev)\
    {\
        (NODE)->LINK.prev->LINK.next = (N);\
    }\
    /* 如果NODE的prev不存在, 要设置N为start, 最后设置N为NODE的prev */\
    else\
    {\
        (BASE).start = (N);\
    }\
    (NODE)->LINK.prev = (N);\
} while (0)

/* 插入N到结点NODE的后面 */
#define LIST_INSERT_AFTER(LINK, BASE, NODE, N)\
do\
{\
    (BASE).count++;\
    /* 初始化指针域 */\
    (N)->LINK.prev = (NODE);\
    (N)->LINK.next = (NODE)->LINK.next;\
    /* 如果NODE的next存在, 设置N为它的prev */\
    if ((NODE)->LINK.next)\
    {\
        (NODE)->LINK.next->LINK.prev = (N);\
    }\
    /* 如果NODE的next不存在, 要设置N为end, 最后设置N为NODE的next */\
    else\
    {\
        (BASE).end = (N);\
    }\
    (NODE)->LINK.next = (N);\
} while (0)

/* 从链表中移除结点 */
#define LIST_REMOVE(LINK, BASE, N)\
do\
{\
    (BASE).count--;\
    /* 如果N的prev存在, 设置N的next为它的next */\
    if ((N)->LINK.prev)\
    {\
        (N)->LINK.prev->LINK.next = (N)->LINK.next;\
    }\
    /* 如果N的prev不存在, 设置N的next为start */\
    else\
    {\
        (BASE).start = (N)->LINK.next;\
    }\
    /* 如果N的next存在, 设置N的prev为它的prev */\
    if (((N)->LINK).next)\
    {\
        (N)->LINK.next->LINK.prev = (N)->LINK.prev;\
    }\
    /* 如果N的next不存在, 设置N的prev为end */\
    else\
    {\
        (BASE).end = (N)->LINK.prev;\
    }\
    /* 最后清空指针域 */\
    (N)->LINK.prev = NULL;\
    (N)->LINK.next = NULL;\
} while (0)

/* 获取链表长度 */
#define LIST_GET_LEN(BASE)      ((BASE).count)

/* 获取链表第一个结点 */
#define LIST_GET_FIRST(BASE)    ((BASE).start)

/* 获取链表最后一个结点 */
#define LIST_GET_LAST(BASE)     ((BASE).end)

/* 获取N的上一个结点 */
#define LIST_GET_PREV(LINK, N)  ((N)->LINK.prev)

/* 获取N的下一个结点 */
#define LIST_GET_NEXT(LINK, N)  ((N)->LINK.next)

/* 从头开始向后查找结点 */
#define LIST_SEARCH_FROM_FIRST(LINK, BASE, N, TEST)\
do\
{\
    (N) = LIST_GET_FIRST(BASE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_NEXT(LINK, N);\
    }\
} while (0)

/* 从末尾开始向前查找结点 */
#define LIST_SEARCH_FROM_LAST(LINK, BASE, N, TEST)\
do\
{\
    (N) = LIST_GET_LAST(BASE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_PREV(LINK, N);\
    }\
} while (0)

/* 从结点NODE开始向前查找结点 */
#define LIST_SEARCH_BEFORE_NODE(LINK, BASE, NODE, N, TEST)\
do\
{\
    (N) = (NODE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_PREV(LINK, N);\
    }\
} while (0)

/* 从结点NODE开始向后查找结点 */
#define LIST_SEARCH_AFTER_NODE(LINK, BASE, NODE, N, TEST)\
do\
{\
    (N) = (NODE);\
    while (N)\
    {\
        if (TEST)\
            break;\
        (N) = LIST_GET_NEXT(LINK, N);\
    }\
} while (0)

#endif
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值