<C语言数据结构三>C语言带你玩转“单向链表”

一、链表的基础知识

链表是C语言中比较常用的一种数据结构,相对与上几篇文章的队列结构来说,链表是在一种物理存储单元上非连续、非顺序的存储结构。链表中的每个节点都是通过链表中的指针链接次序实现的;一般来说每个节点都会包含以下两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表又会分成很多个类型:单线链表,单向循环链表,双向链表,双向循环链表等;
这篇文章我们主要聊聊单向链表,单向链表又分为以下两种类型:

  • 无头节点链表:无头节点链表表示的是头节点中是没有数据域的,只包含了指针域
  • 有头节点链表:有头节点链表表示的是头节点中既有指针域也有数据域

二、单向链表的常用接口

  • 链表的创建
  • 链表的插入:头插和尾插
  • 链表节点的删除
  • 链表的销毁
  • 链表的遍历

三、单向链表的实现

1、无头节点的单向链表

链表的数据定义

typedef struct {
    uint8_t id;
} USER_DATA_T;

typedef struct list{
    struct list *next;
    USER_DATA_T data;
} LIST_T;

LIST_T *head_list = NULL; //链表头节点指针

链表的创建

// 创建链表
int list_creat(void)
{
    if (NULL == head_list) {
        head_list = (LIST_T *)malloc(sizeof(LIST_T));
        if (NULL == head_list) {
            return -1;
        }
        head_list->next = NULL;
    }
    return 0;
}

链表的插入:头插和尾插

//链表的头插,在头节点后面插入数据
int list_inset_head(USER_DATA_T *data)
{
    if (NULL == head_list) {
        if (list_creat() != 0) {
            return -1;
        }
    }

    LIST_T *p_node = (LIST_T *)malloc(sizeof(LIST_T));
    memset(p_node, 0, sizeof(LIST_T));
    memcpy(&p_node->data, data, sizeof(USER_DATA_T));
    p_node->next = head_list->next;
    head_list->next = (struct list *)p_node;
    return 0;
}

//链表的尾插,在最后节点插入数据
int list_inset_tail(USER_DATA_T *data)
{
    if (NULL == head_list) {
        if (list_creat() != 0) {
            return -1;
        }
    }
    //创建节点数据
    LIST_T *p_node = (LIST_T *)malloc(sizeof(LIST_T));
    memset(p_node, 0, sizeof(LIST_T));
    memcpy(&p_node->data, data, sizeof(USER_DATA_T));
    p_node->next = NULL;

    LIST_T *p_list = head_list;
    while (p_list) {
        if (p_list->next) {
            p_list = (LIST_T *)p_list->next;
            continue;
        }
        p_list->next = (struct list *)p_node;
        return 0;
    }
}

链表的删除:根据数据内容或者节点序号

// 根据数据内容删除节点
void list_node_del_by_data(USER_DATA_T *data)
{
    if (NULL == head_list) {
        return;
    }

    LIST_T *p_list = (LIST_T *)head_list;
    LIST_T *q = NULL;
    while (p_list->next) {
        if (0 == memcmp(data, &p_list->next->data, sizeof(USER_DATA_T))) {
            q = (LIST_T *)p_list->next;
            p_list->next = q->next;
            free(q);
            q = NULL;
        }
        p_list = (LIST_T *)p_list->next;
    }
}

// 根据节点编号删除节点:头节点为0,数据节点从1开始
void list_node_del_by_index(uint8_t index)
{
    if ((NULL == head_list) || (0 == index)) {
        return;
    }
    uint8_t cnt = 1;
    LIST_T *q = NULL;
    LIST_T *p_list = (LIST_T *)head_list;
    while (p_list) {
        if (cnt == index) {
            q = p_list->next;
            p_list->next = q->next;
            free(q);
            q = NULL;
            return;
        }
        p_list = (LIST_T *)p_list->next;
        cnt++;
    }
}

链表的销毁

//链表销毁
int list_destory(void)
{
    if (NULL == head_list) {
        return -1;
    }

    LIST_T *p_list = NULL, *q = NULL;
    for (p_list = head_list->next; p_list != NULL; p_list = q) {
        q = p_list->next;
        free(p_list);
    }
    free(head_list);
    return 0;
}

链表的遍历

//遍历链表
void list_traverse(void)
{
    if ((NULL == head_list) || (NULL == head_list->next)) {
        return;
    }

    LIST_T *p_node = (LIST_T *)head_list->next;
    while (p_node) {
        printf("node data:%d\r\n", p_node->data);
        p_node = (LIST_T *)p_node->next;
    }
}

2、有头节点的单向链表

链表的创建

// 创建链表
int list_creat(USER_DATA_T *data)
{
    if (NULL == head_list) {
        head_list = (LIST_T *)malloc(sizeof(LIST_T));
        if (NULL == head_list) {
            return -1;
        }

        memcpy(&head_list->data, data, sizeof(USER_DATA_T));
        head_list->next = NULL;
    }
    return 0;
}

链表的插入:头插和尾插

//链表的头插,在头节点前面插入数据
int list_inset_head(USER_DATA_T *data)
{
    if (NULL == head_list) {
        return -1;
    }
    
    LIST_T *p_node = (LIST_T *)malloc(sizeof(LIST_T));
    memset(p_node, 0, sizeof(LIST_T));
    memcpy(&p_node->data, data, sizeof(USER_DATA_T));

    p_node->next = head_list;
    head_list = p_node;
    return 0;
}

//链表的尾插,在最后节点插入数据
int list_inset_tail(USER_DATA_T *data)
{
    if (NULL == head_list) {
        return -1;
    }

    //创建节点数据
    LIST_T *p_node = (LIST_T *)malloc(sizeof(LIST_T));
    memset(p_node, 0, sizeof(LIST_T));
    memcpy(&p_node->data, data, sizeof(USER_DATA_T));
    p_node->next = NULL;

    LIST_T *p_list = head_list;
    while (p_list) {
        if (p_list->next) {
            p_list = (LIST_T *)p_list->next;
            continue;
        }
        p_list->next = (struct list *)p_node;
        return 0;
    }
}

链表节点的删除:根据数据或者节点序号删除

// 根据数据内容删除节点
void list_node_del_by_data(USER_DATA_T *data)
{
    if (NULL == head_list) {
        return;
    }

    LIST_T *p_list = (LIST_T *)head_list;
    LIST_T *q = NULL;
    while (p_list) {
        if (0 == memcmp(data, &p_list->data, sizeof(USER_DATA_T))) {
            if (p_list == head_list) {
                q = head_list;
                head_list = head_list->next;
                free(q);
                q = NULL;
                p_list = head_list;
            } else {
                q->next = p_list->next;
                free(p_list);
                p_list = NULL;
                q = (LIST_T *)q->next;
            }
            continue;
        }
        q = p_list;
        p_list = (LIST_T *)p_list->next;
    }
}

// 根据节点编号删除节点:头节点为0
void list_node_del_by_index(uint8_t index)
{
    if (NULL == head_list) {
        return;
    }

    uint8_t cnt = 0;
    LIST_T *q = NULL;
    LIST_T *p_list = (LIST_T *)head_list;

    while (p_list) {
        if (cnt == index) {
            if (0 == index) {
                q = head_list;
                head_list = head_list->next;
                free(q);
                q = NULL;
            } else {
                q->next = p_list->next;
                free(p_list);
                p_list = NULL;
            }
            return;
        }
        q = p_list;
        p_list = (LIST_T *)p_list->next;
        cnt++;
    }
}

链表节点的查找

// 根据数据内容查找节点
LIST_T *list_node_find(USER_DATA_T *data)
{
    if (NULL == head_list) {
        return;
    }

    LIST_T *p_list = (LIST_T *)head_list;
    while (p_list) {
        if (0 == memcmp(data, &p_list->data, sizeof(USER_DATA_T))) {
            return p_list;
        }
        p_list = (LIST_T *)p_list->next;
    }
}

3、demo程序

demo测试代码

int main(void)
{
    USER_DATA_T data = {0};
    list_creat();
    data.id = 3;
    list_inset_head(&data);
    data.id = 2;
    list_inset_head(&data);
    data.id = 4;
    list_inset_tail(&data);
    data.id = 5;
    list_inset_tail(&data);
    data.id = 1;
    list_inset_head(&data);
    data.id = 3;
    list_inset_head(&data);
    data.id = 3;
    list_node_del_by_data(&data);
    list_node_del_by_index(3);
    list_traverse();
    list_destory();
    list_traverse();
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

坚持学习的小王同学

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

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

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

打赏作者

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

抵扣说明:

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

余额充值