【C语言数据结构】01.单链表

前言

  • 本节主要进行C语言数据结构单链表的学习。
  • 继【嵌入式C语言】专题结束后,继续C语言的学习,本专题主要针对数据结构的C语言实现进行学习整理。
  • 相关资料主要为书籍《数据结构与算法分析:C语言描述_原书第2版_高清版》与链接: 手撕数据结构全集(C语言版)

  • 最近编码练习受益良多,个人建议荐逛博客学习的同时一定要多写代码多刷题,网上就有很多在线编码的网站。
  • 个人比较推荐在牛客网刷题(点击可以跳转),主要它登陆后会保存刷题记录进度,重新登录时写过的题目代码不会丢失,我觉得这一点还挺好的。
  • 个人刷题练习系列专栏:个人CSDN牛客刷题专栏
  • 牛客数据结构题目位置如下:
    在这里插入图片描述


0.数据结构概述

0.1数据结构的基本概念

  • 1.数据:数据是信息的载体,是描述客观事物属性的数、字符以及所有能输入到计算机中并被程序识别和处理的符号的集合。

  • 2.数据元素:数据元素是数据的基本单位,通常作为一个整体进行考虑和处理。一个数据元素可由若干数据项组成,数据项是构成数据元素的不可分割的最小单位。例如,学生记录就是一个数据元素,它由学号、姓名、性别等数据项组成。

  • 3.数据对象:数据对象是具有相同性值的数据元素的集合,是数据的一个子集。

  • 4.数据类型:数据类型是一个值的集合和定义再此集合上的一组操作的总称。
    1)原子类型。其值不可再分的数据类型。如bool 和int 类型。
    2)结构类型。其值可以再分解为若干成分(分量)的数据类型。
    3)抽象数据类型。抽象数据组织及与之相关的操作。

  • 5.数据结构:数据结构是相互之间存在一种或多种特定关系的数据元素的集合。

0.2数据结构的三要素

0.2.1数据的逻辑结构:

逻辑结构是指数据元素之间的逻辑关系,即从逻辑关系上描述数据
逻辑结构包括:

  • 集合结构:结构中的数据元素之间除“同属一个集合”外,别无其它关系。
  • 线性结构:结构中的数据元素之间只存在一对一的关系,除了第一个元素,所有元素都有唯一前驱;除了最后一个元素,所有元素都有唯一后继。
  • 树形结构:结构中数据元素之间存在一对多的关系。
  • 图状结构:数据元素之间是多对多的关系。

0.2.2数据的存储结构(物理结构):

存储结构是指数据结构在计算机中的表示(又称映像),也称物理结构
存储结构包括:

顺序存储:把逻辑上相邻的元素存储在物理位置也相邻的存储单元中,元素之间的关系由存储单元的邻接关系来体现。
链式存储:逻辑上相邻的元素在物理位置上可以不相邻,借助指示元素存储地址的指针来表示元素之间的逻辑关系。
索引存储:在存储元素信息的同时,还建立附加的索引表,索引表中的每项称为索引项,索引项的一般形式是(关键字,地址)
散列存储:根据元素的关键字直接计算出该元素的存储地址,又称哈希(Hash)存储。

0.2.3数据的运算:

施加在数据上的运算包括运算的定义何实现。运算的定义是针对逻辑结构的,指出运算的功能;运算的实现是针对存储结构的,指出运算的具体操作步骤

0.3算法的基本概念

程序=数据结构+算法

  • 算法(algorithm)是对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或多个操作。

算法的特性

  • 1.有穷性:一个算法必须总在执行有穷步之后结束,且每一步都可在有穷时间内完成。
  • 2.确定性:算法中每条指令必须有确定的含义,对于相同的输入只能得到相同的输出。
  • 3.可行性:算法中描述的操作都可以通过已经实现的基本运算执行有限次来实现。
  • 4.输入:一个算法有零个或多个输入,这些输入取自于某个特定的对象的集合。
  • 5.输出:一个算法有一个多个输出,这些输出是与输入有着某种特定关系的量。

好的算法达到的目标

  • 正确性:算法应能够正确的求接问题。
  • 可读性:算法应具有良好的可读性,以帮助人们理解。
  • 健壮性:输入非法数据时,算法能适当地做出反应或进行处理,而不会产生莫名奇妙地输出结果。
  • 效率与低存储量需求:效率是指算法执行的时间,存储量需求是指算法执行过程中所需要的最大存储空间,这两者都与问题的规模有关。

0.4算法的时间复杂度

  • 一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数f(n),算法的时间量度记作T(n)=O(n),它表示随问题规模n的增大而增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐近时间复杂度,简称时间复杂度。

0.5算法的空间复杂度

  • 算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它是问题规模n的函数。记为S(n)=O(g(n))。

1.单链表概述

  • 线性表:1.有限的序列2.序列中的每一个元素都有唯一的前驱和后继,除了开头和结尾两个节点。

  • 顺序表;分配一块连续的内存去存放这些元素,例如编程语言中的数组

  • 链表:内存是不连续的,元素会各自被分配一块内存,内存和内存之间用指针进行相连。
    在这里插入图片描述

  • 单链表结构:
    在这里插入图片描述
    在这里插入图片描述

2.单链表操作

链表实现时,通常增加一个头节点,头节点数据域存储链表个数,next域指向真正的表头,链表真正的第一个节点从第二个开始。

2.1增加

2.1.1头插法

所谓头插法建立单链表是说将新结点插入到当前链表的表头,即头结点之后。如图所示:
在这里插入图片描述

  • 算法思想:首先初始化一个单链表,其头结点为空,然后循环插入新结点*s:将s的next指向头结点的下一个结点,然后将头结点的next指向s。
  • 需要指出的是,头插法建立的单链表中结点的次序和输入数据的顺序不一致,是相反的。若希望两者的顺序是一致的,则可采用尾插法建立单链表。

2.1.2尾插法

所谓尾插法建立单链表,就是将新结点插入到当前链表的表尾。如下图所示:
在这里插入图片描述

2.2删除

  • 将单链表的第 i 个结点删除。需要找到对应结点,将对应结点的前一个结点指向这个结点的后继,只操作1个指针

  • 算法思想:先检查删除位置的合法性,然后从头开始遍历,找到表中的第 i-1 个结点,即被删除结点的前驱结点p,被删除结点为q,修改p的指针域,将其指向q的下一个结点,最后再释放结点*q的存储空间。

3.单链表实现

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

typedef struct Node//定义数据结构,包含数据域域next域
{
    int data;//data域
    struct Node* next;//指针域,指向了同样类型的下一个
}Node;

Node* initList()//初始化头节点
{
    Node* list=(Node*)malloc(sizeof(Node));//开辟空间,新建节点
    list->data=0;//链表默认个数为0
    list->next=NULL;//链表为空,故头节点next域为空
    return list;
}

void headINSERT(Node* list,int data)//头插法
{
    Node* node = (Node*)malloc(sizeof(Node));//开辟空间,新建节点
    node->data = data;//data给到新建节点的数据域
    node->next = list->next;//新建节点的next指向的是原先链表的第一个节点,也就是头节点指向的节点
    list->next = node;//头节点指向新建节点
    list->data++;//插入后链表元素加一
}

void tailINSERT(Node* list,int data)//尾插法
{
    Node* head=list;//保存头节点的地址
    Node* node = (Node*)malloc(sizeof(Node));//开辟空间,新建节点
    node->data = data;//data给到新建节点的数据域
    node->next = NULL;//新建节点指向NULL
    //list = list->next;//list指向第一个节点,准备寻找(初版跟着视频走这行是加上的);更正:此行不能要,否则为空时直接尾插法会出错
    while(list->next)//寻找最后一个节点
    {
        list=list->next;
    }
    list->next=node;//最后一个节点指向新建节点
    head->data++;//头节点里的data域节点数加一
}

void delete(Node* list,int data)//删除
{
    Node* pre=list;//保存头节点,它是current的前一个节点
    Node* current=list->next;//保存第一个节点开始,从它开始遍历
    while(current)//遍历,可使用头节点data进行for循环遍历
    {
        if(current->data==data)//找到data
        {
            pre->next=current->next;//将前一个节点指向要要删除节点的下一个节点
            free(current);//释放要铲除节点的堆内存空间
            break;//退出循环
        }
        pre = current;//未找到data则往后继续遍历
        current = current->next;//未找到data则往后继续遍历
    }
    list->data--;
}

void printList(Node* list)//遍历操作
{
    list=list->next;//指向头节点
    while(list)//遍历打印
    {
        printf("%d",list->data);
        list=list->next;        
    }
    printf("\n");
}

int main()
{
    Node* list= initList();//初始化头节点,创捷链表节点
    headINSERT(list,5);
    headINSERT(list,4);
    headINSERT(list,3);
    headINSERT(list,2);
    headINSERT(list,1);
    tailINSERT(list,6);
    tailINSERT(list,7);
    tailINSERT(list,8);
    tailINSERT(list,9);
    tailINSERT(list,10);
    printList(list);
    printf("\n");
    delete(list,1);
    delete(list,6);
    delete(list,10);
    printList(list);
    printf("\n");
    system("pause");
}

在这里插入图片描述

结束语

  • 以上就是C语言数据结构单链表的内容,核心还是结构体指针以及堆空间实现。可以在牛客尝试刷几道单链表题目来练习实践。牛客网刷题(点击可以跳转),可以尝试注册使用。
    在这里插入图片描述

参考资料:
链接1: UP从0到1带你手撕数据结构全集(C语言版)
链接2: 《王道》数据结构笔记整理2022
链接3: 单链表——单链表的定义及基本操作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

不僈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值