单链表的C/C++语言描述实现模板


前言

数据结构中单链表的C/C++语言描述实现模板,有详细的步骤解析及使用示例。


代码仓库


linkList.cpp

//头文件————————————————————
#include <iostream>
using namespace std;

//自定义数据类型————————————————————
typedef int ELEM_TYPE; //数据的(数据)类型 依据数据的实际类型定义

//结构体
//定义
//结点
typedef struct LNode
{
    ELEM_TYPE data;     //数据
    struct LNode *next; //指向直接后继结点的指针
} LNode;
// struct LNode:结构体数据类型的名称
//如创建一个结点的语句:struct LNode node;
//若结构体数据类型内存在指向该结构体的指针数据类型,则必须完整命名结构体数据类型的名称
//如结点结构体LNode内存在指向该结构体的指针数据类型next,则命名语句为:typedef struct LNode而不是typedef struct
// LNode:typedef给结构体数据类型起的别名,可简化语句
//如创建一个结点的语句:LNode node;
//结构体数据类型的名称和别名尽量一致,以方便记忆、使用

//函数声明————————————————————
void use_example();                                                  //使用示例
void create_list_head(LNode *&head, ELEM_TYPE value[], int count);   //创建  头插法
void create_list_tail(LNode *&head, ELEM_TYPE value[], int count);   //创建  尾插法
LNode *find_node(LNode *head, ELEM_TYPE elem);                       //按值查找
LNode *get_node(LNode *head, int i);                                 //按序查找
void insert_node(LNode *&head, int posi, LNode *node);               //插入  前插
void insert_node_exten(LNode *&head, int posi, LNode *node);         //插入  扩展:前插转后插
void delele_node(LNode *&head, int posi);                            //删除  前删
void delele_node_exten(LNode *&head, int posi);                      //删除  前删转后删
int get_length(LNode *head);                                         //获取表长
void clear_list(LNode *&head);                                       //清空
void merge_list_increase(LNode *head1, LNode *head2, LNode *&head3); //合并两递增表成递增表
void merge_list_reduce(LNode *head1, LNode *head2, LNode *&head3);   //合并两递增表成递减表

//主函数————————————————————
int main()
{
    use_example(); //使用示例

    return 0;
}

//函数定义————————————————————
//使用示例
void use_example()
{
    //创建
    struct LNode *head; //头指针,指向头结点或第一个结点
    //或LNode *head;
    //一般头指针变量名为链表表名    这里用head见明知意,表示是头指针

    ELEM_TYPE value[3]; //数据数组
    value[0] = 100;
    value[1] = 200;
    value[2] = 300;

    create_list_tail(head, value, 3); //创建    尾插法  元素在表中的排列:100,200,300

    //按值查找
    LNode *node; //结点指针

    node = find_node(head, 100); //按值查找

    cout << node->data << endl; //输出:100

    //按序查找
    node = get_node(head, 2); //按序查找

    cout << node->data << endl; //输出:200

    //插入  前插
    node = (LNode *)malloc(sizeof(LNode)); //创建新结点   结点指针指向新结点
    node->data = 400;                      //初始化新结点的数据域

    insert_node(head, 3, node); //插入 前插 元素在表中的排列:100,200,400,300

    //插入  扩展:前插转后插
    node = (LNode *)malloc(sizeof(LNode)); //创建新结点   结点指针指向新结点
    node->data = 500;                      //初始化新结点的数据域

    insert_node_exten(head, 3, node); //插入    扩展:前插转后插    元素在表中的排列:100,200,500,400,300

    LNode *p;       //移动结点指针
    p = head->next; //初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) //遍历
    {
        cout << p->data << endl; //输出:100,200,500,400,300

        p = p->next; //移动结点指针
    }

    //删除  前删
    delele_node(head, 3); //删除  前删  元素在表中的排列:100,200,400,300

    //删除  前删转后删
    delele_node_exten(head, 3); //删除  前删转后删  元素在表中的排列:100,200,300

    p = head->next; //初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) //遍历
    {
        cout << p->data << endl; //输出:100,200,300

        p = p->next; //移动结点指针
    }

    //获取表长
    int list_length = 0; //表长

    list_length = get_length(head);

    cout << list_length << endl; //输出:3

    //清空
    clear_list(head);

    list_length = get_length(head);

    cout << list_length << endl; //输出:0

    //合并两递增表成递增表
    struct LNode *head1; //递增表1的头指针,指向头结点或第一个结点

    ELEM_TYPE value1[3]; //数据数组1
    value1[0] = 100;
    value1[1] = 300;
    value1[2] = 500;

    create_list_tail(head1, value1, 3); //创建    尾插法  元素在表中的排列:100,300,500

    struct LNode *head2; //递增表2的头指针,指向头结点或第一个结点

    ELEM_TYPE value2[3]; //数据数组2
    value2[0] = 200;
    value2[1] = 400;
    value2[2] = 600;

    create_list_tail(head2, value2, 3); //创建    尾插法  元素在表中的排列:200,400,600

    struct LNode *head3; //新递增表的头指针,指向头结点或第一个结点

    merge_list_increase(head1, head2, head3); //合并两递增表成递增表

    p = head3->next;  //移动结点指针    指向新递增表的第一个结点
    while (p != NULL) //遍历
    {
        cout << p->data << endl; //输出:100,200,300,400,500,600

        p = p->next; //移动结点指针
    }

    //合并两递增表成递减少表
    create_list_tail(head1, value1, 3); //创建    尾插法  元素在表中的排列:100,300,500
    create_list_tail(head2, value2, 3); //创建    尾插法  元素在表中的排列:200,400,600
    clear_list(head3);                  //清空

    merge_list_reduce(head1, head2, head3); //合并两递增表成递减表

    p = head3->next;  //移动结点指针    指向新递减表的第一个结点
    while (p != NULL) //遍历
    {
        cout << p->data << endl; //输出:600,500,400,300,200,100

        p = p->next; //移动结点指针
    }

    return;
}

//函数定义————————————————————
//创建  头插法
//时间复杂度:O(n)
//注意:参数使用*&,指向指针的引用数据类型
//若只使用*,指针数据类型,在函数体中并未进行解引用调用修改*head的值,使用malloc()修改的是head的值
//所以是值拷贝,函数返回后数据丢失。地址拷贝需要引用数据类型的配合
void create_list_head(LNode *&head, ELEM_TYPE value[], int count) //参数:头指针,数据数组,数据数量
{
    head = (LNode *)malloc(sizeof(LNode)); //创建头结点,头指针指向头结点
    head->next = NULL;                     //初始化头结点的指针域

    LNode *node; //新结点指针

    for (int i = 0; i < count; i++) //循环创建链表
    {
        node = (LNode *)malloc(sizeof(LNode)); //创建新结点,新结点指针指向新结点
        node->data = value[i];                 //初始化新结点的数据域

        node->next = head->next; //初始化新结点的指针域
        head->next = node;
        //插入到链表的头结点和前一新结点之间
        //链表描述:头结点->当前新结点->前一新结点->...->空
        //若无头结点:head=node;
    }

    return;
}

//创建  尾插法
//时间复杂度:O(n)
void create_list_tail(LNode *&head, ELEM_TYPE value[], int count) //参数:头指针,数据数组,数据数量
{
    head = (LNode *)malloc(sizeof(LNode)); //创建头结点,头指针指向头结点
    head->next = NULL;                     //初始化头结点的指针域

    LNode *tail; //尾指针,尾指针始终指向最后一个结点
    tail = head; //初始化尾指针  指向最后一个结点(头结点)

    LNode *node; //新结点指针

    for (int i = 0; i < count; i++) //循环创建链表
    {
        node = (LNode *)malloc(sizeof(LNode)); //创建新结点,新结点指针指向新结点
        node->data = value[i];                 //初始化新结点的数据域

        tail->next = node; //插入到链表的头结点和前一新结点之间
        tail = tail->next; //移动尾指针
        //或者tail=node;
    }

    tail->next = NULL; //尾指针指向空   创建完成
    //链表描述:头结点->...->前一新结点->当前新结点->空

    return;
}

//按值查找
//时间复杂度:O(n)
LNode *find_node(LNode *head, ELEM_TYPE elem) //参数:头指针,元素  返回值:结点指针
{
    LNode *p;       //移动结点指针
    p = head->next; //初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    while (p != NULL) //遍历
    {
        if (p->data == elem) //匹配
        {
            break; //退出查找
        }

        p = p->next; //若不匹配,移动结点指针
    }

    return p; //若找到返回结点指针,否则返回空指针
}

//按序查找
//时间复杂度:O(n)
LNode *get_node(LNode *head, int i) //参数:头指针,元素查找的位置   返回值:结点指针
{
    LNode *p;       //移动结点指针
    p = head->next; //初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    if (i < 0) //不存在负数的位置
    {
        return NULL; //返回空指针
    }

    if (i == 0) //第0个位置是头结点
    {
        return head; //返回头指针
    }

    for (int j = 1; j < i; j++) //遍历
    {

        if (p != NULL) //不匹配
        {
            p = p->next; //移动结点指针
        }
        else //匹配
        {
            break; //退出查找
        }
    }

    return p; //若找到返回结点指针,否则返回空指针
}

//插入  前插
//时间复杂度:O(n)
//因为需遍历找到元素插入位置之前的结点
void insert_node(LNode *&head, int posi, LNode *node) //参数:头指针,元素插入的位置,结点
{
    LNode *p;                     //移动结点指针
    p = get_node(head, posi - 1); //初始化移动结点指针 指向第posi-1个位置的结点

    node->next = p->next; //插入 在第posi-1个位置的结点之后,占据第posi个位置,在原第posi个位置的结点之前
    p->next = node;

    return;
}

//插入  扩展:前插转后插
//时间复杂度:O(1)
//直接在元素插入位置之后的结点插入,再交换结点数据
void insert_node_exten(LNode *&head, int posi, LNode *node) //参数:头指针,元素插入的位置,结点
{
    LNode *p;                 //移动结点指针
    p = get_node(head, posi); //初始化移动结点指针 指向第posi个位置的结点

    node->next = p->next; //插入 在第posi个位置的结点之后
    p->next = node;

    ELEM_TYPE temp; //临时数据

    temp = p->data; //交换第posi个位置的结点和新结点的数据
    p->data = node->data;
    node->data = temp;

    return;
}

//删除  前删
//时间复杂度:O(n)
//因为需遍历找到元素删除位置之前的结点
void delele_node(LNode *&head, int posi) //参数:头指针,元素删除的位置
{
    LNode *p;                     //移动结点指针
    p = get_node(head, posi - 1); //初始化移动结点指针 指向第posi-1个位置的结点

    LNode *q;    //被删结点指针
    q = p->next; //初始化被删结点指针 指向第posi个位置的结点

    p->next = q->next; //修改指针指向   第posi-1个位置的结点的指针指向第posi+1个位置的结点
    //或者p->next=p->next->next;

    free(q); //删除被删结点指针指向的结点,释放内存
}

//删除  前删转后删
//时间复杂度:O(1)
//交换结点数据,直接删除元素删除位置之后的结点
void delele_node_exten(LNode *&head, int posi) //参数:头指针,元素删除的位置
{
    LNode *p;                 //移动结点指针
    p = get_node(head, posi); //初始化移动结点指针 指向第posi个位置的结点

    LNode *q;    //被删结点指针
    q = p->next; //初始化被删结点指针 指向第posi+1个位置的结点

    p->data = q->data; //交换第posi个位置和第posi+1个位置结点的数据 此时需删除的是第posi+1个位置的结点
    //或者p->data=p->next->data
    p->next = q->next; //修改指针指向   第posi个位置的结点的指针指向第posi+2个位置的结点
    //或者p->next=p->next->next;

    free(q); //删除被删结点指针指向的结点,释放内存
}

//获取表长
int get_length(LNode *head) //参数:头指针  返回值:表长
{
    LNode *p;       //移动结点指针
    p = head->next; //初始化移动结点指针 指向第一个结点(头结点的下一个结点)

    int length = 0; //表长

    while (p != NULL) //遍历 有结点
    {
        length++;    //表长递增
        p = p->next; //移动指针
    }

    return length; //返回表长
}

//清空
void clear_list(LNode *&head) //参数:头指针
{
    LNode *p;       //被删结点指针
    p = head->next; //初始化被删结点指针 指向第一个结点(头结点的下一个结点)

    LNode *q; //记录结点指针 指向被删结点的下一个结点

    while (p != NULL) //遍历    有结点
    {
        q = p->next; //记录被删结点的下一个结点

        free(p); //删除被删结点指针指向的结点,释放内存

        p = q; //移动指针    指向下一个被删结点
    }

    head->next = NULL; //头结点的指针域置空 当退出遍历时已无数据结点
}

//合并两递增表成递增表
//思路:遍历两递增表,比较结点的数值域数据,使用尾插法创建新递增表
//参数:递增表1的头指针,递增表2的头指针,新递增表的头指针
void merge_list_increase(LNode *head1, LNode *head2, LNode *&head3)
{
    LNode *p;        //移动结点指针1
    p = head1->next; //初始化移动结点指针1 指向递增表1的第一个结点(头结点的下一个结点)

    LNode *q;        //移动结点指针2
    q = head2->next; //初始化移动结点指针2 指向递增表2的第一个结点(头结点的下一个结点)
    free(head2);     //释放递增表2的头结点  因为已用移动结点指针2记录递增表2的第一个结点,数据不会丢失

    head3 = head1;      //初始化新递增表的头指针 指向递增表1的头结点,用递增表1的头结点作为新递增表的头结点
    head3->next = NULL; //初始化新递增表头结点的指针域,置空  因为已用移动结点指针1记录递增表1的第一个结点,数据不会丢失

    LNode *tail;  //新递增表的尾指针    始终指向新递增表的终端结点
    tail = head3; //初始化新递增表的尾指针  初始时新递增表为空,尾指针指向头结点

    while ((p != NULL) && (q != NULL)) //遍历两递增表
    {
        if (p->data <= q->data) //比较结点的数值域数据
        {
            tail->next = p; //将数据域数值小的结点插入新递增表表尾

            p = p->next;       //递增表1的当前结点已插入新递增表,更新移动结点指针1
            tail = tail->next; //更新新递增表的尾指针  始终指向新递增表的终端结点
        }
        else //同理
        {
            tail->next = q;

            q = q->next;
            tail = tail->next;
        }
    }

    tail->next = NULL;
    //可以没有,因为最后还要将未插入的结点插入新递增表表尾,未插入结点的终端结点指针域为空

    if (p != NULL) //若递增表2插入完成,递增表1还有未插入结点,则将剩余结点插入新递增表尾部
    {
        tail->next = p;
    }
    if (q != NULL) //若递增表1合并完成,递增表2还有未插入结点,则将剩余结点插入新递增表尾部
    {
        tail->next = q;
    }

    return;
}

//合并两递增表成递减表
//思路:遍历两递增表,比较结点的数值域数据,使用头插法创建新递减表
//参数:递增表1的头指针,递增表2的头指针,新递增表的头指针
void merge_list_reduce(LNode *head1, LNode *head2, LNode *&head3)
{
    LNode *p;        //移动结点指针1
    p = head1->next; //初始化移动结点指针1 指向递增表1的第一个结点(头结点的下一个结点)

    LNode *q;        //移动结点指针2
    q = head2->next; //初始化移动结点指针2 指向递增表2的第一个结点(头结点的下一个结点)
    free(head2);     //释放递增表2的头结点  因为已用移动结点指针2记录递增表2的第一个结点,数据不会丢失

    head3 = head1;      //初始化新递减表的头指针 指向递增表1的头结点,用递增表1的头结点作为新递减表的头结点
    head3->next = NULL; //初始化新递减表头结点的指针域  因为已用移动结点指针1记录递增表1的第一个结点,数据不会丢失

    LNode *temp; //临时结点指针 指向当前要插入新递减表表头的结点

    while ((p != NULL) && (q != NULL)) //遍历两递增表
    {
        if (p->data <= q->data) //比较结点的数值域数据
        {
            temp = p;    //将临时结点指针指向数据域数值小的结点
            p = p->next; //递增表1的当前结点已被临时结点指针记录,更新移动结点指针1

            temp->next = head3->next; //将临时结点指针指向头结点的下一个结点
            head3->next = temp;       //将头指针指向当前要插入新递减表表头的结点
        }
        else //同理
        {
            temp = q;
            q = q->next;

            temp->next = head3->next;
            head3->next = temp;
        }
    }

    if (p != NULL) //若递增表2插入完成,递增表1还有未插入结点,则取剩余结点一个个插入新递减表表头
    {
        temp = p;
        p = p->next;

        temp->next = head3->next;
        head3->next = temp;
    }
    if (q != NULL) //若递增表1插入完成,递增表2还有未插入结点,则取剩余结点一个个插入新递减表表头
    {
        temp = q;
        q = q->next;

        temp->next = head3->next;
        head3->next = temp;
    }

    return;
}

总结

不同参考资料中,单链表的描述实现各不相同,但基本思想是一致的。作者使用规范的变量命名、提供详细的步骤解析及使用示例,应用C/C++语言将其整合成模板,以帮助理解记忆。


作者的话

  • 作者:夜悊
  • 版权所有,转载请注明出处,谢谢~
  • 如果文章对你有帮助,请点个赞或加个粉丝吧,你的支持就是作者的动力~
  • 文章在描述时有疑惑的地方,请留言,定会一一耐心讨论、解答
  • 文章在认识上有错误的地方, 敬请批评指正
  • 望读者们都能有所收获

参考资料

  • 《2023版数据结构高分笔记》主编:率辉
  • 《2023年计算机组成原理考研复习指导》组编:王道论坛
  • 《大话数据结构》作者:程杰
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值