首页 > 其他分享 >数据结构--单向链表

数据结构--单向链表

时间:2023-07-04 19:36:06浏览次数:35  
标签:Node head NULL -- 链表 数据结构 节点 指针

如果对于顺序表的结构已经大致了解,那么对单向链表的学习就会轻松一些。

顺序存储中的数据因为挤在一起而导致需要成片移动,那很容易想到的解决方案是将数据离散地存储在不同内存块中,然后在用来指针将它们串起来。这种朴素的思路所形成的链式线性表,就是所谓的链表。

顺序表和链表在内存在的基本样态如下图所示

数据结构--单向链表_链表


根据链表中各个节点之间使用指针的个数,以及首尾节点是否相连,可以将链表分为如下种类:

  1. 单向链表 (只有一个指针(一个方向),指向下一个数据的入口地址)
  2. 单向循环链表(只有一个指针,最后一个数据的指针存储的是第一个数据的入口地址)
  3. 双向循环链表 (有两个指针,分别指向下一个数据,以及上一个数据,并首尾节点是相互指向的)
  4. 内核链表 (由系统的头文件提供了所有关于指针的操作,对与指针的操作更为安全)

数据结构--单向链表_链表_02

这些不同链表的操作都是差不多的,只是指针数目的异同。以最简单的单向链表为例,其基本示意图如下所示:

数据结构--单向链表_Data_03

上图中,所有的节点均保存一个指针,指向其逻辑上相邻的下一个节点(末尾节点指向空)。另外注意到,整条链表用一个所谓的头指针 head 来指向,由 head 开始可以找到链表中的任意一个节点。head 通常被称为头指针

链表的基本操作,一般包括:

  1. 节点设计

数据结构--单向链表_链表_04

  1. 初始化空链表

数据结构--单向链表_链表_05

  • 无头节点的链表

// 无头节点的初始化P_Node head = NULL ;

数据结构--单向链表_数据_06

  • 有头节点的链表
// 初始化有头结点的链表P_Node head = NewNode( NULL );
// 创建一个新节点作为头节点,但是头结点不需要有数据
// 参数给NULL 表示不需要对数据域进行操作

数据结构--单向链表_链表_07

  1. 增删节点

数据结构--单向链表_链表_08

P_Node GetNewNode( DataType * NewData ){    if ( NewData == NULL )    {        printf("请传递正确的数据地址..\n");        return NULL ;    }

    // 申请一个新的节点 (堆内存)
    P_Node NewNode = calloc(  1 , sizeof(Node) );
    if (NewNode == NULL)
    {
        perror("calloc NewNode error") ;
        return NULL ;
    }
    

    // 对该新节点进行初始化 (数据域, 指针域)
    memcpy( &NewNode->Data , NewData , sizeof(DataType));

    NewNode->Next = NULL ;
    
    return NewNode ;
}

P_Node Add2List( P_Node head , P_Node NewNode  )
{
    // 让新节点的后继指针,指向头指针所指向的节点(指定第一个有效数据)
    NewNode->Next = head ;

    // 让头指针指向新节点
    head = NewNode ;

    return head;
}

数据结构--单向链表_Data_09

void Add2List( P_Node head , P_Node New )
{  
// 让新节点的后继指针, 指向第一个有效数据  
New->Next =  head->Next ;  
// 让头节点的后继指针 , 指向新节点 
head->Next = New ;
}
  1. 链表遍历
// 无头节点遍历链表void DisplayList( P_Node head ){    if (head == NULL)    {        printf("当前链表为空....\n");        return  ;    }
    
    // 通过临时指针tmp遍历整个链表 ,只要tmp 不指向NULL 就说明链表还没遍历结束
    for (P_Node tmp = head ; tmp != NULL ; tmp = tmp->Next )
    {
        printf("TMP:%d\n" , tmp->Data );
    }
    
    return ;
}


// 有头节点遍历链表
void DisplayList( P_Node head)
{

    if (head->Next == NULL)
    {
        printf("当前链表为空..\n");
        return ;
    }
    
    //   让 tmp 指向第一个有效数据
    for (P_Node tmp = head->Next ; tmp != NULL ; tmp = tmp->Next )
    {
        printf("tmp:%d\n" , tmp->Data );
    }
}
  1. 销毁链表

销毁的方式可以有多种,比如循环进行销毁,或者递归实现。

递归思路:

通过递归不断往链表末尾移动,当递归到链表的末尾节点时进行释放以及返回。

递进时不断往末尾移动,回归时从末尾不断释放节点。

P_Node DestroyList( P_Node ptr ){    // 检查当前指针ptr 是否指向最后一个节点    if (ptr->Next == NULL )    {        printf("Name:%s:%d\n" , ptr->Data.Name , ptr->Data.Num );        free(ptr);        return NULL ;
    }
    // 如果当前节点不是末尾节点,则递进向链表末尾
    DestroyList( ptr->Next );
    printf("Name:%s:%d\n" , ptr->Data.Name , ptr->Data.Num );

    free(ptr);

    return NULL ;
}

链表插入的变形:

数据结构--单向链表_链表_10

数据结构--单向链表_链表_11

数据结构--单向链表_数据_12

循环链表:

初始化循环链表:

数据结构--单向链表_数据_13

P_Node NewNode( DataType * NewData )
{
    // 为新节点申请堆内存
    P_Node New = calloc(1, sizeof(Node) );

    
    // 判断是否需要初始化数据域
    if ( NewData )
    {
        memcpy( &New->Data , NewData );
    }
    
    // 初始化指针域
    New->Next = New ;

    return New ;
}

到此,单向链表的知识点分布就结束了,如下为样例代码

//因为单项链表可写为无头、有头、循环,太多就不放代码了

标签:Node,head,NULL,--,链表,数据结构,节点,指针
From: https://blog.51cto.com/u_15903599/6624504

相关文章

  • PAT乙级【Java题解合集】
    ✨说在前面       这个暑假博主用大概两周不到的闲暇时间把PAT乙级的110道算法题全部肝完了,个人感觉题目的难度大部分在中等偏下,大概有二十道左右的题目还是蛮有意思的,值得细细去钻研,本专栏非常适合新手入门算法,也适合Java算法老手巩固一些基本知识点,由于C站上关于PAT乙级J......
  • Android 4.0 SDK的离线方式安装
     昨天看新闻得知新版本的android系统发布了,android4.0是人们期盼多时的版本了。作为一个IT技术人员,迫不及待地就奔向了http://developer.android.com去看看有没有新的SDK公布出来,当时是上午,没见到有更新,心想一定是若干天后才会发布。没想到同事下午告诉我,新版的SDK已经发布了。......
  • 梯度下降法——得到的结果可能是局部最优值,如果凸函数则可保证梯度下降得到的是全局最
    梯度下降法(GradientDescent)是一种常见的最优化算法,用于求解函数的最大值或者最小值。梯度下降在高数中,我们求解一个函数的最小值时,最常用的方法就是求出它的导数为0的那个点,进而判断这个点是否能够取最小值。但是,在实际很多情况,我们很难求解出使函数的导数为0的方程,这个时候就可以......
  • 面试常问集锦——MySQL部分数据库的隔离级别
    聚集索引与非聚集索引的区别https://zhuanlan.zhihu.com/p/113917726Myisam引擎采用非聚集索引,索引与数据分开,叶子结点存放数据的地址。Innodb采用聚集索引,主键索引树的叶子结点存放真实数据,非主键索引树的叶子结点存放主键值索引底层的实现,为什么不选红黑树、B树等?总结(1)哈希表 ......
  • 深入浅出时序数据库之预处理篇——批处理和流处理,用户可定制,但目前流行influxdb没有做
    时序数据是一个写多读少的场景,对时序数据库以及数据存储方面做了论述,数据查询和聚合运算同样是时序数据库必不可少的功能之一。如何支持在秒级对上亿数据的查询分组聚合运算成为了时序数据库产品必须要面对的挑战。 本文会从时序数据库的查询以及聚合运算角度展开,最后会从如何解决......
  • 面试再问MySQL存储过程和触发器就把这篇文章给他
    Mysql存储过程及触发器trigger存储过程一、一个简单的存储过程1,一个简单的存储过程delimiter$$ createproceduretesta() begin Select*fromemp; Select*fromdept; End; $$; delimiter; --调用存储过程 calltesta();存储过程的结构组成:1,创建格式:createpr......
  • JeecgBoot低代码开发平台与达梦数据完成兼容性互认证
    近日,JeecgBoot与达梦数据库管理系统V8完成兼容性认证测试;通过双方共同测试表明,Jeecgboot低代码开发平台与达梦数据库管理系统V8,相互兼容,系统功能运行稳定,能够满足用户更多的性能需求;并签署产品兼容互认证明。JeecgBoot将持续进行更多的国产化软件及国产化服务器的兼容性测试,将会不......
  • FreeWheel基于Go的实践经验漫谈——GC是大坑(关键业务场景不用),web框架尚未统一,和c++性
    Go语言是FreeWheel公司目前主要力推的一个方向,在其看来,面向服务的架构的大环境中,Go非常适合做一些功能相对独立、功能比较明确的微服务的语言。在结合已有的各种编程语言,计算框架(如Hadoop、Java、Ruby、C++)的基础上,FreeWheel把Go语言定位成用来实现轻量级服务或API的缺省编程语言,将......
  • Servlet 执行流程及文件配置。
    目录 1、基本介绍 2、执行流程 3、生命周期 4、Servlet的5个方法 5、体系结构 6、urlPattern配置 7、XML配置1、基本介绍▶简介 Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。使用Servlet就可以实现,根据不同的登录用户在页面上动态显示不同内容......
  • CockroachDB——类似spanner的开源版,底层使用rocksdb存储
    摘自:https://github.com/cockroachdb/cockroach/blob/master/docs/design.mdCockroachDBisadistributedSQLdatabase.Theprimarydesigngoalsare scalability, strongconsistency and survivability(hencethename).CockroachDBaimstotoleratedisk,machine,ra......