C语言单链表

链表的优点:进行插入删除的读写数据操作速度快,而且内存空间利用率充足,不需要指定特定的空间大小,相对灵活。

链表的缺点:进行查找数的操作,速度要慢于随机存储。删除节点需要释放内存,开辟节点需要开辟空间,多了很多操作,容易造成内存泄漏的风险。

必要的头文件:stdlib.h 建议头文件: stddef.h , string.h

单链表的组成:头指针,头结点(可以没有),普通结点。普通结点又分为数据域和指针域,指针域存放的是下一个结点的地址。头指针存放在栈空间中,但是指向的是堆中的节点地址。其他结点均使用malloc函数存放于堆空间中。

带头结点和不带头结点的区别:

  1. 头结点可以存储一些链表的基础信息,使用头指针来访问头结点,头结点的指针域存放的才是真正的链表的第一个指针。

  2. 使用头结点可以大大减少二级指针的使用,只有修改头指针的指向时才需要二级指针,其他情况全是结构体指针即可完成链表的增删查改,因为头指针始终是指向头结点的,一般不需要移动头结点。

  3. 不带头结点,因为第一个就是真正的数据,所以增删查改都需要使用二级指针来访问,修改头指针的指向。

链表的结构体与自定义类型

typedef int dataType;
typedef int status;

//链表
typedef struct linkList {
    dataType data;
    struct linkList* next;
}linkList;

//头结点
typedef struct head {
    size_t len;
    linkList* phead;
}head;

单链表的创建

head* createLinkList(void)
{
    head* h = malloc(sizeof(head));
    h->len = 0; //长度为0
    h->phead = NULL; //头结点指针为NULL
    return h;
}

单链表头插与尾插数据

头插操作很简单, 速度也很快, 但是插入的数据会颠倒过来。尾插首先需要查找一下尾结点在哪里(指针域为NULL),然后在进行插入。

/**
 * 头插单链表
 * 参数: 头指针
 */
status headInsertList(head* hp, dataType element)
{
    //开辟新节点空间
    linkList* ptemp = (linkList*)malloc(sizeof(linkList));
    if (ptemp == NULL) {
        return ERROR;
    }
    //写入数据
    ptemp->next = hp->phead;
    ptemp->data = element;

    //更新头节点
    hp->phead = ptemp;
    hp->len++;

    return OK;
}

/**
 * 尾插
 * 参数: 头指针,插入的元素
 */
status tailInsert(head* hp, dataType element)
{
    linkList** ptemp = NULL;//二级指针修改尾节点指针域的地址
    //找到尾节点,特点是下一个指针指向NULL
    if (hp->phead == NULL) {
        ptemp = &(hp->phead);
    }
    else{
        ptemp = &(hp->phead->next);
    }
    while (*ptemp != NULL) {
        ptemp = &((*ptemp)->next);
    }
    //插入数据,开辟一个空间,让尾节点指向它
    linkList* ptemp2 = (linkList*)malloc(sizeof(linkList));
    if (ptemp2 == NULL) {
        return ERROR;
    }
    ptemp2->data = element;
    ptemp2->next = NULL;
    *ptemp = ptemp2;

    //更新头结点的信息
    ++hp->len;

    return OK;
}

 

单链表的删除节点

删除节点一般是不需要删除头节点的,因此使用一级指针即可。首先需要找到待删除结点的地址,和删除结点指向的下一个地址,然后将这两个连接起来,free掉删除结点。

/**
 * 删除链表一个结点
 * 参数: 头指针, 删除节点的位置, 从1开始
 * 注意: 头结点不能删除
 */
void delList(head* hp, size_t num)
{
    if (num > hp->len || num < 1) {
        return;
    }

    linkList* ptemp1 = hp->phead;//删除节点的前一个节点
    linkList* ptemp2 = hp->phead;//待删除节点

    //找到该结点
    for (int i = 0; i < num-2; i++) {
        ptemp1 = ptemp1->next;
    }
    ptemp2 = ptemp1->next;
    ptemp1->next = ptemp2->next;
    free(ptemp2);
    hp->len--;
}

整表的删除

一个一个节点删除,还需要删除释放头节点的内存空间,所以需要使用二级指针,不能够忘记修改头指针的地址为NULL,否则可能出现非法访问的缺陷。

/**
 * 销毁链表
 * 参数: 头指针
 */
void clearList(head** hp)
{
    linkList* ptemp1 = (*hp)->phead;  //下一个节点的位置
    linkList* ptemp2 = (*hp)->phead;  //待销毁的节点
    
    //销毁后面的节点
    while (ptemp1 != NULL) {
        ptemp1 = ptemp1->next;
        free(ptemp2);
        ptemp2 = ptemp1;
    }
    //销毁头指针
    free(*hp);
    *hp = NULL;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值