也没想象中那么神秘的数据结构-一环扣一环的“链表”(单链表)

概念

单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素(数据元素的映象) + 指针(指示后继元素存储位置),元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。

注意

1、单链表的结构:由节点构成,每个节点包含数据域(存储元素内容)和指针域(指向后继元素)两部分。

2、头指针:永远指向链表第一个节点的位置。用于指明链表的位置,便于后期找到链表并使用表中的数据。

3、头节点:通常作为链表的第一个节点。并非必须的,只是为了方便解决某些实际问题而引入的,另外头节点数据域可以用来存储链表元素个数

4、首元节点:头节点的下一个节点,第一个真正意义上存储了数据的节点。

5、最后一个节点没有后继节点,指向NULL,也是链表结束条件的唯一判据

核心

单链表由指针域和数据域构成,并且声明头指针指明链表的位置,节点与节点之间通过指针链接如下图代码和图形所示。

/* 链表元素定义 */
struct t_node{
    int data;                   /* 元素值 */
    struct t_node *p_next;      /* 指向下一个元素 */
};

/* 链表定义 */
struct t_link{
    struct t_node *p_node;      /* 指向链表头结点 */
};

单链表的插入分为在链表头插入和链表尾插入两种,因为链表头的位置可以很容易得到,所以链表头插入相对比较简单。

链表头插入:整个过程分为三步,a、创建节点;b、找到链表头位置;c、插入节点,具体执行如下图代码和图形所示。

/**
 * @在链表头增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_front(struct t_link *p_link, int data)
{
    struct t_node *p_node = NULL;

    if (link_is_full(p_link))
        return 0;

    if ((p_node = creat_node(data)) == NULL)
        return 0;

    p_node->p_next = p_link->p_node->p_next;
    p_link->p_node->p_next = p_node;
    p_link->p_node->data++;

    return 1;
}

链表尾插入:过程同链表头插入一样,只不过在寻找链表尾的时候需要把整个链表遍历一遍,具体执行如下图代码和图形所示。

/**
 * @在链表尾增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_rear(struct t_link *p_link, int data)
{
    struct t_node *p_rear = p_link->p_node;
    struct t_node *p_node = NULL;

    if (link_is_full(p_link))
        return 0;

    /* 1. 创建节点 */
    if ((p_node = creat_node(data)) == NULL)
        return 0;

    /* 2. 查找尾节点 */
    while(p_rear->p_next != NULL)
        p_rear = p_rear->p_next;

    /* 3. 插入元素 */
    p_rear->p_next = p_node;
    p_node->p_next = NULL;
    p_link->p_node->data++;

    return 1;
}

单链表的删除同样包括删除链表头和删除链表尾,删除的核心在于让目标节点的前驱节点和后继节点直接相连,然后删除目标节点即可。

删除链表头:代码和示意图如下所示。

/**
 * @删除链表头元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_head(struct t_link *p_link)
{
    struct t_node *p_node = NULL;

    if (link_is_empty(p_link))
        return 0;

    p_node = p_link->p_node->p_next;  
    p_link->p_node->p_next = p_node->p_next;

    p_node->p_next = NULL;
    free(p_node);
    p_node = NULL;
    p_link->p_node->data--;

    return 1;
}

删除链表尾:代码和示意图如下所示。

/**
 * @删除链表尾元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_rear(struct t_link *p_link)
{
    struct t_node *p_rear = p_link->p_node, *p_prev = NULL;

    if (link_is_empty(p_link))
        return 0;

    /* 查找尾节点 */
    while(p_rear->p_next != NULL) {
        p_prev = p_rear;    /* 保存倒数第二个节点 */
        p_rear = p_rear->p_next;
    }

    p_rear->p_next = NULL;    
    free(p_rear);
    p_rear = NULL;
    p_link->p_node->data--;   
    p_prev->p_next = NULL;

    return 1;
}

其余操作相对比较简单,这里就不再一一阐述了,具体参见示例代码,相信大家应该能看懂。

示例

★包含头文件sll.h和源文件sll.c(均已验证通过),sll = Singly Linked List。

 sll.h

/**
 * @Filename : sll.h
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 单链表示例(sll = Singly Linked List)
**/

#ifndef __SLL_H__
#define __SLL_H__

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

#define MAX_SIZE    15          /* 链表最大元素个数 */

/* 链表元素定义 */
struct t_node{
    int data;                   /* 元素值 */
    struct t_node *p_next;      /* 指向下一个元素 */
};

/* 链表定义 */
struct t_link{
    struct t_node *p_node;      /* 指向链表头结点 */
};

/**
 * @初始化链表
 * @p_link:链表
 * @创建头结点,让链表头指针指向头结点,头结点数据存储链表数据长度
**/
void link_init(struct t_link *p_link);

/**
 * @清除链表
 * @p_link:链表
**/
void link_deinit(struct t_link *p_link);

/**
 * @判断链表是否已满
 * @p_link:链表
 * @返回1表示已满,0表示未满
**/
int link_is_full(const struct t_link *p_link);

/**
 * @判断链表是否已空
 * @p_link:链表
 * @返回1表示已空,0表示未空
**/
int link_is_empty(const struct t_link *p_link);

/**
 * @获取链表元素个数
 * @p_link:链表
 * @返回元素个数
**/
int link_get_size(const struct t_link *p_link);

/**
 * @在链表头增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_front(struct t_link *p_link, int data);

/**
 * @在链表尾增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_rear(struct t_link *p_link, int data);

/**
 * @删除链表头元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_head(struct t_link *p_link);

/**
 * @删除链表尾元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_rear(struct t_link *p_link);

/**
 * @获取链表头元素
 * @p_link:链表      p_data:保存链表头元素值
 * @返回1表示成功,0表示失败
**/
int link_get_front(const struct t_link *p_link, int *p_data);

/**
 * @获取链表尾元素
 * @p_link:链表      p_data:保存链表尾元素值
 * @返回1表示成功,0表示失败
**/
int link_get_rear(const struct t_link *p_link, int *p_data);

/**
 * @获取链表第n个元素
 * @p_link:链表      p_data:保存元素值     n:第n个元素
 * @返回1表示成功,0表示失败
**/
int link_get_nth(const struct t_link *p_link, int *p_data, int n);

/**
 * @打印链表元素内容
 * @p_link:链表
**/
void link_print(const struct t_link *p_link);

#endif

 sll.c

/**
 * @Filename : sll.c
 * @Revision : $Revision: 1.0 $
 * @Author : Feng(更多编程相关的知识和源码见微信公众号:不只会拍照的程序猿,欢迎订阅)
 * @Description : 单链表示例(sll = Singly Linked List)
**/

#include "sll.h"

/**
 * @创建节点
 * @data:元素值
 * @返回节点地址,NULL表示失败
**/
static struct t_node *creat_node(int data)
{
    struct t_node *p_node = (struct t_node *)malloc(sizeof(struct t_node));

    if (p_node == NULL)
        return NULL;

    p_node->data = data; 
    p_node->p_next = NULL;

    return  p_node;  
}

/**
 * @初始化链表
 * @p_link:链表
 * @创建头结点,让链表头指针指向头结点,头结点数据存储链表数据长度
**/
void link_init(struct t_link *p_link)
{
    struct t_node *p_head = creat_node(0);  /* 头结点 */

    if (p_head == NULL)
        return;

    p_link->p_node = p_head;
}

/**
 * @清除链表
 * @p_link:链表
**/
void link_deinit(struct t_link *p_link)
{
    struct t_node *p_tmp = NULL, *p_node = p_link->p_node->p_next;

    while(p_node) {
        p_tmp = p_node;
        p_node = p_node->p_next;
        free(p_tmp);    
    }
    p_link->p_node->p_next = NULL;
    p_link->p_node->data = 0;
}

/**
 * @判断链表是否已满
 * @p_link:链表
 * @返回1表示已满,0表示未满
**/
int link_is_full(const struct t_link *p_link)
{
    return (p_link->p_node->data >= MAX_SIZE);
}

/**
 * @判断链表是否已空
 * @p_link:链表
 * @返回1表示已空,0表示未空
**/
int link_is_empty(const struct t_link *p_link)
{
    return (p_link->p_node->data == 0);
}

/**
 * @获取链表元素个数
 * @p_link:链表
 * @返回元素个数
**/
int link_get_size(const struct t_link *p_link)
{
    return (p_link->p_node->data);
}

/**
 * @在链表头增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_front(struct t_link *p_link, int data)
{
    struct t_node *p_node = NULL;

    if (link_is_full(p_link))
        return 0;

    if ((p_node = creat_node(data)) == NULL)
        return 0;

    p_node->p_next = p_link->p_node->p_next;
    p_link->p_node->p_next = p_node;
    p_link->p_node->data++;

    return 1;
}

/**
 * @在链表尾增加元素
 * @p_link:链表       data:元素值
 * @返回1表示成功,0表示失败
**/
int link_add_rear(struct t_link *p_link, int data)
{
    struct t_node *p_rear = p_link->p_node;
    struct t_node *p_node = NULL;

    if (link_is_full(p_link))
        return 0;

    /* 1. 创建节点 */
    if ((p_node = creat_node(data)) == NULL)
        return 0;

    /* 2. 查找尾节点 */
    while(p_rear->p_next != NULL)
        p_rear = p_rear->p_next;

    /* 3. 插入元素 */
    p_rear->p_next = p_node;
    p_node->p_next = NULL;
    p_link->p_node->data++;

    return 1;
}

/**
 * @删除链表头元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_head(struct t_link *p_link)
{
    struct t_node *p_node = NULL;

    if (link_is_empty(p_link))
        return 0;

    p_node = p_link->p_node->p_next;  
    p_link->p_node->p_next = p_node->p_next;

    p_node->p_next = NULL;
    free(p_node);
    p_node = NULL;
    p_link->p_node->data--;

    return 1;
}

/**
 * @删除链表尾元素
 * @p_link:链表
 * @返回1表示成功,0表示失败
**/
int link_del_rear(struct t_link *p_link)
{
    struct t_node *p_rear = p_link->p_node, *p_prev = NULL;

    if (link_is_empty(p_link))
        return 0;

    /* 查找尾节点 */
    while(p_rear->p_next != NULL) {
        p_prev = p_rear;    /* 保存倒数第二个节点 */
        p_rear = p_rear->p_next;
    }

    p_rear->p_next = NULL;    
    free(p_rear);
    p_rear = NULL;
    p_link->p_node->data--;   
    p_prev->p_next = NULL;

    return 1;
}

/**
 * @获取链表头元素
 * @p_link:链表      p_data:保存链表头元素值
 * @返回1表示成功,0表示失败
**/
int link_get_front(const struct t_link *p_link, int *p_data)
{

    if (link_is_empty(p_link))
        return 0;

    *p_data = p_link->p_node->p_next->data;

    return 1;
}

/**
 * @获取链表尾元素
 * @p_link:链表      p_data:保存链表尾元素值
 * @返回1表示成功,0表示失败
**/
int link_get_rear(const struct t_link *p_link, int *p_data)
{
    struct t_node *p_rear = p_link->p_node;

    if (link_is_empty(p_link))
        return 0;

    /* 查找尾节点 */
    while(p_rear->p_next != NULL)
        p_rear = p_rear->p_next;

    *p_data = p_rear->data;

    return 1;
}

/**
 * @获取链表第n个元素
 * @p_link:链表      p_data:保存元素值     n:第n个元素
 * @返回1表示成功,0表示失败
**/
int link_get_nth(const struct t_link *p_link, int *p_data, int n)
{
    int cnt = 0;
    const struct t_node *p_tmp = p_link->p_node;

    if (link_get_size(p_link) < n)
        return 0;

    while (p_tmp->p_next != NULL) {
        p_tmp = p_tmp->p_next;
        if (++cnt == n) {
            *p_data = p_tmp->data;
            return 1;
        }
    }
    return 0;
}
/**
 * @打印链表元素内容
 * @p_link:链表
**/
void link_print(const struct t_link *p_link)
{
    struct t_node *p_tmp = NULL;

    for (p_tmp=p_link->p_node->p_next; p_tmp!=NULL; p_tmp=p_tmp->p_next)
        printf("%d ", p_tmp->data);

    printf("\n");
}

/**
 * @单链表测试代码
**/
int main(void)
{
    int val;
    struct t_link mylink;

    link_init(&mylink);

    link_add_front(&mylink, 25);
    link_add_front(&mylink, 41);
    link_add_front(&mylink, 16);
    link_add_front(&mylink, 7);
    link_add_rear(&mylink, 8);
    link_add_rear(&mylink, 19);
    link_add_front(&mylink, 22);
    link_add_front(&mylink, 16);
    link_add_front(&mylink, 7);
    link_add_rear(&mylink, 8);
    link_add_rear(&mylink, 19);
    link_add_front(&mylink, 22);

    /* 插入元素,链表:22 7 16 22 7 16 41 25 8 19 8 19,元素个数为12 */     
    printf("the size of link is: %d\n", link_get_size(&mylink));
    link_print(&mylink);
    printf("---------------------------------------\n");

    /* 删除首尾元素,链表:7 16 22 7 16 41 25 8 19 8,元素个数为10 */     
    link_del_head(&mylink);
    link_del_rear(&mylink); 
    printf("the size of link is: %d\n", link_get_size(&mylink));
    link_print(&mylink);       
    printf("---------------------------------------\n");

    /* 第一个元素7,最后一个元素8,第三个元素22 */   
    if (link_get_front(&mylink, &val))
        printf("the front link data is: %d\n", val);

    if (link_get_rear(&mylink, &val))
        printf("the tail link data is: %d\n", val);

    if (link_get_nth(&mylink, &val, 3))
        printf("the 3th link data is: %d\n", val);

    return 0;

}

结论

feng:link$ gcc -o sll sll.c
feng:link$ ./sll
the size of link is: 12
22 7 16 22 7 16 41 25 8 19 8 19 
---------------------------------------
the size of link is: 10
7 16 22 7 16 41 25 8 19 8 
---------------------------------------
the front link data is: 7
the tail link data is: 8
the 3th link data is: 22
feng:link$ 

本示例仅为单链表示例,公众号也提供双向链表示例《也没想象中那么神秘的数据结构-一环扣一环的“链表”(双链表)》、循环链表示例《也没想象中那么神秘的数据结构-一环扣一环的“链表”(循环链表)》以及链表的典型应用示例。

往期 · 推荐

也没想象中那么神秘的数据结构-先来后到的“队列”(链式队列)

也没想象中那么神秘的数据结构-先来后到的“队列”(循环队列)

也没想象中那么神秘的数据结构-后来居上的“栈”

我用C语言玩对象,观察者模式应用2-热水的用途

我用C语言玩对象,状态模式应用1-水的三态

关注

更多精彩内容,请关注微信公众号:不只会拍照的程序猿,本人致力分享linux、设计模式、C语言、嵌入式、编程相关知识,也会抽空分享些摄影相关内容,同样也分享大量摄影、编程相关视频和源码,另外你若想要本文章源码请关注公众号:不只会拍照的程序猿,后台回复:数据结构源码,也可点击此处下载

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不只会拍照的程序猿

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

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

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

打赏作者

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

抵扣说明:

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

余额充值