链表操作实例(创建节点,增加节点,倒序链表,销毁链表等)

链表操作实例

    该实例为:不断输入学生信息(学生姓名,性别,年龄,分数)后,打印输出所有的结果。其中用到了链表的初始化,创建,长度计算,增加节点,倒序链表内容等。这里简单介绍几个重要的链表操作函数。

    github下载地址:https://github.com/Edroid/student_linklist

一 简要了解

先简单了解一下链表的基础知识
什么是链表,书上给出的定义是:链表由一系列节点(链表中每一个元素称为节点)组成,节点可以在运行时动态生成。每个节点包括两个部分:一个是存储 数据元素 的数据域,另一个是存储下一个节点地址的指针。如图:


我们首先定义一个结构体类型 link_note_t

typedef student_t          Item;
typedef struct link_node_s
{
    Item                   item;
    struct  link_node_s    *next;
} link_node_t;  /*---  end of struct link_node_s  ---*/

下面我们先来弄清楚几个变量
link_node_t head;  //head为结构体变量,其存放的是结构体内容,我们假设其地址为0x0001
link_node_t *head1;  //head1 为结构体指针变量,其存放的是结构体的地址(0x0001),假设其本身地址为0x0008
link_node_t **head2;  //head2为结构体指针的指针,其存放的是结构体指针变量的地址(0x0008),假设其本身地址为0x000b。

二,链表操作实例

1.统计链表长度,即链表中节点的个数。

int list_item_count(link_node_t *head)  //传入形参为指向结构体的指针
{
    int              count = 0;
    link_node_t      *pnode = head;


    if(!head)    //异常处理:如果链表为空,直接返回0
        return 0;

    while(pnode != NULL)//链表不为空时,计算并返回count的值
    {
        ++count;
        pnode = pnode->next;
    }

    return count;
}

2.增加节点
   向链表中添加新的节点,该其指针域的指针指向的是新的结构体变量
int list_add_item(link_node_t **head, Item item) //传入形参为指向节点的指针和新的结构体变量中的内容
{
    link_node_t        *pnew; //定义新的结构体指针用来指向新的结构体
    link_node_t        *pnode = *head; //定义一个结构体指针用来找到尾节点

    pnew = (link_node_t *)malloc(sizeof(*pnew));//开辟新的内存空间用来存放新的节点
    if(pnew == NULL)  
        return -1;  

    memset(pnew, 0, sizeof(*pnew));//清空pnew,Memset 用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘ ’或‘/0’;
    pnew->item = item;

    if(NULL == *head) //如果第一个节点为空,则将头节点作为新的节点
    {
        *head = pnew;
    }
    else //否则,找到链表中最后一个节点并设置其为新的节点
    {
        while(pnode->next != NULL)
        {
            pnode = pnode->next;
        }

        pnode->next = pnew;
    }

    return 0;
}



3.对链表中节点使用函数
void list_traverse(link_node_t *head, void (*pfunc)(Item item)) //传入形参为节点和函数指针
{
    link_node_t   *pnode = head;

    while(pnode != NULL)
    {
        pfunc(pnode->item);
        pnode = pnode->next;
    }
}
说明:void (*pfunc)(Item item)为函数指针,该函数返回值为空,函数形参为结构体Item中的item


4.倒序链表中的内容
void link_reverse(link_node_t **head)
{
   link_node_t        *revsd_ptr;  /* Last one time reversed linker pointer*/
   link_node_t        *cur_ptr;    /* Currently need reverse node pointer*/
   link_node_t        *next_ptr;   /* Next need revers node pointer*/


   revsd_ptr = NULL;
   cur_ptr = *head;
   while( cur_ptr != NULL)
   {
   /* first circulating                             second circulating          
    * Step1:
    * revsd 
    */ cur  next                                    revsd  cur  next
    *         |    |                                   |    |    |
    *  NULL   A -> B -> C -> D -> E            NULL <- A    B -> C -> D -> E
       next_ptr = cur_ptr->next;

   /* Step2:
    * revsd   cur  next                             revsd  cur  next
    *          |    |                                  |    |    |
    *  NULL <- A    B -> C -> D -> E           NULL <- A <- B    C -> D -> E
     */
       cur_ptr->next = revsd_ptr;

   /* Step3:
    *         revsd cur/next                             revsd cur/next
    *          |      |                                    |     |   
    * NULL <-  A      B  ->  C -> D -> E      NULL <- A <- B     C -> D -> E
    */
       revsd_ptr =  cur_ptr;
       cur_ptr = next_ptr;
   }


5.销毁链表
int list_destroy(link_node_t **head)
{
    link_node_t   *psave;

    while( *head )//当节点不为NULL时
    {
        psave = (*head)->next;

        free(*head);  //释放内存

        *head = psave;
    }
}

关于什么时候形参为节点(link_note_t *head),什么时候形参为节点地址(link_note_t **head) 的问题:
  
刚开始学链表的时候最难理解的地方就是形参的确定,不妨对比一下经典的swap函数
void swap(int *a, int *b)
{
   int c;
   c = *a;
   *a = *b;
   *b =*a;
}

int main(void)
{
  int a = 2,b = 3;
  swap(&a,&b);
  printf("%d,%d\n",a,b)
}
只有给swap传入a, b 的地址的时候才能真正改变其内容本身,如果只是传入变量a,b本身,swap函数中改变的是形参也就是swap中的局部变量,并没有改变a,b本身的内容。对比一下就会发现,其实链表的操作也是一样的,如果涉及到链表本身内容的改变,我们需要传入节点的地址,即 link_note_t **head,如果不改变链表内容本身,则传入节点 link_note_t *head。


本人能力有限,以上只是个人学习链表之后的一个总结和看法,难免有错误之处。若发现错误欢迎指正,感激不尽。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值