数据结构之单向链表

基本概念

顺序表:顺序存储的线性表。

链式表:链式存储的线性表,简称链表。

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

使用线性逻辑(数据与数据之间的关系)+链式存储

链表的分类:

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

1.单向链表

2.单向循环链表

3.双向循环链表

4.内核链表(由系统内核提供的链表操作数)

这些不同的链表的操作都是差不多的,只是指针数数目的异同。

在链表中,指针所处的代码块一般称之为节点,而节点在c语言中是用结构体来实现的,代码如下:

typedef   int  TYPEDATA;   //取别名,为以后更换数据类型方便 

typedef struct LinkListNode
{
    //数据域
    TYPEDATA data;  //节点所带的数据

    //指针域
    strct LinkListNode *next; //后继指针

}Node, *P_Node;//为结构体定义别名,方便定义参数

链表头设计:

链表头分为带头节点和不带头节点,其中不带头节点的直接令头指针指向空就可以初始化了;

带头节点的链表需要定义一个新的节点,里面的数据域为空,指针域指向下一的节点;

下面为定义新节点的代码:

/****************

功能:创建一个新的节点并初始化

函数名:NewNode(TYPEDATA data)

参数:TYPEDATA data 传递一个TYPEDATA类型的参数,用于给节点赋值

返回值:返回一个P_Node类型的结构体指针,也就是返回一个新节点

****************/

P_Node NewNode(TYPEDATA data)
{
    //申请一块Node大小的堆空间并让newnode指向该空间
    P_Node newnode = calloc(1, sizeof(Node));
    //初始化数据域
    newnode->data = data;
    //初始化指针
    new->next = NULL;
    //返回堆空间的地址(新节点的入口地址) 
    return newnode;
}

创建链表头之后需要增加节点,删除节点,移动节点等操作,也就是所谓的增删改查;

而增加节点在只有头节点位置的情况下,有头部插入节点和尾部插入节点两种基本操作

代码如下:

头插法

/****************

功能:从已知节点使用头插法插入到链表中

函数名:AddToList(const P_Node head, TYPEDATA data)

参数:const P_Node head为只读类型的节点,TYPEDATA data为TYPEDATA类型的数据

返回值: 成功返回真true,失败返回假false

****************/


bool AddToList(const P_Node head, TYPEDATA data)
{
    //判断头节点是否为空
    if(hrad == NULL)
    {
        printf("list hrad error\n");
        return false;
    }
    //使用当前data创建一个节点
    P_Node newdata = NewNode(data);
    //将新节点插入到链表中去
    //1.新节点指向头节点的后继节点
    newdata->next = head->next;
    //2.头节点的后继节点指向新节点
    head->next = newdata;
    
    return true;


}

尾插法:

/****************

功能:从已知节点使用尾插法插入到链表中

函数名:AddForList(const P_Node head, TYPEDATA data)

参数:const P_Node head为只读类型的节点,TYPEDATA data为TYPEDATA类型的数据

返回值: 成功返回真true,失败返回假false

****************/


bool AddForList(const P_Node head, TYPEDATA data)
{
    //判断头节点是否为空
    if(hrad == NULL)
    {
        printf("list hrad error\n");
        return false;
    }
    //使用当前data创建一个节点
    P_Node newdata = NewNode(data);
    //尾插法需要先到链表的末尾
    P_Node tmp = head;
    for( ; tmp->next != NULL; tmp = tmp->next);
    //让后继节点指向新节点
    tmp->next = newdata;
    
 
    
    return true;


}

当插入链表数据后,想要查看链表数据时,可以重头到尾遍历链表,将链表中的数据打印出来

代码如下:

遍历显示链表

/****************

功能:从已知节点遍历后继节点的数据并且打印出来

函数名:ShowList(const P_Node head)

参数:const P_Node head为只读类型的节点

返回值: 成功返回真true,失败返回假false

****************/


bool ShowList(const P_Node head)
{
    if(head == NULL)
    {
        printf("list head error...\n");
        return false;
    }
    
    //通过头节点开始遍历链表
    for(P_Node tmp = head->next; tmp != NULL; tmp = tmp->next)
    {
        printf("data:%d\n", tmp->data);
        
    }
    return true;

}

当链表数据太多时,想要寻找想要的那个节点,就需要查找节点了;

代码如下:

/****************

功能:从已知节点遍历查找需要的节点数据并且打印出来

函数名:FindForList(const P_Node head, TYPEFATA data)

参数:const P_Node head为只读类型的节点,TYPEFATA data为要查找的数据

返回值: 成功返回要查找的节点,失败返回NULL;

****************/


P_Node FindForList(const P_Node head, TYPEFATA data)
{
    if(head == NULL)
    {
        printf("list head error...\n");
        return NULL;
    }
    
    //通过头节点开始遍历链表
    for(P_Node tmp = head->next; tmp != NULL; tmp = tmp->next)
    {
        if(tmp->data == data)
        {
            printf("find data:%d\n", tmp->data);
            return tmp;//返回查找到的节点    
        }
        
    }
    
    printf("no data\n");
    return NULL;//没有找到节点返回NULL;

}

当碰到链表中有不需要的数据要删除时,可以将节点删除,首先找到节点,再将节点删除,方法有很多,以下为最笨方法:

代码如下:

/****************

功能:从已知节点遍历查找需要删除的节点数据并且打印出来,将节点删除

函数名:RemoveNode(const P_Node head, P_Node del)

参数:const P_Node head为只读类型的节点,P_Node del为要删除的节点数据

返回值: 成功返回要删除的节点,失败返回NULL;

****************/


P_Node RemoveNode(const P_Node head, P_Node del)
{
    if(head == NULL)
    {
        printf("list head error...\n");
        return NULL;
    }
    
    //通过头节点开始遍历链表
    for(P_Node tmp = head->next; tmp != del; tmp = tmp->next)
    {
        if(tmp == del)
        {
            printf("find data:%d\n", tmp->data);
            tmp-next = del->next;
            del->next = NULL;
            return del;//返回查找到的节点    
        }
        
    }
    
    printf("no data\n");
    return NULL;//没有找到节点返回NULL;

}

当想要销毁链表时,可以销毁链表,代码如下:

递归销毁

P_Node RecursionDestructionList( P_Node tmp )
{
    if ( tmp == NULL )
    {
        return NULL ;
    }
    tmp->next = RecursionDestructionList( tmp->next );
    printf("当前即将删除:%d\n" , tmp->data );
    free(tmp);
    return NULL ;
}

双指针销毁

P_Node DestructionList( P_Node head )
{
    if ( head->next == NULL && head != NULL  )
    {
        free(head);
        return NULL ;
    }
    // 搞两个指针先指向第一个和第二个有效数据
    P_Node tmp = head->next ;
    P_Node pos = tmp->next ;
    // 删除结点
    for ( ; tmp != NULL ;  )
    {
        printf("tmp:%d\n" , tmp->data );
        free(tmp);
        tmp=pos;
        if (pos != NULL )
        {
            pos = pos->next;
        }
    }
    return NULL ;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值