初见链表(C语言)

本文详细介绍了链表的逻辑和存储结构,阐述了链表的优点和缺点,并通过C语言实现了链表的基本操作,包括创建、遍历、判断空、获取长度、插入、删除、修改、查询、排序、反转等。同时,提供了链表排序的示例代码,帮助读者深入理解链表这一重要的数据结构。
摘要由CSDN通过智能技术生成

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


前言

链表是一种物理存储结构上非连续,非顺序的存储结构,通过链表节点中存储的指针单元可以完成节点单元间的链接

一、链表简介

逻辑结构:链表是线性的,即首元素没有直接前驱,尾元素没有直接后继,其余元素有且只有一个前驱和后继。

存储结构:以链式结构进行存储(元素间是非连续的)。

优点:

1.链表的内存是动态分配的,内存大小不固定,方便对它拓展,提高了内存利用率,减少浪费。

2.对链表中元素的插入和删除更方便,通过指针的指向就可以进行操作。

缺点:

1.相比较数组查找的效率较低,总是需要从头节点开始遍历链表查找数据。

2.结构比数组复杂一些

二、算法及相应代码

链表的基础操作有增删改查等

1.链表的定义

代码如下:

typedef int linklist_data_t;//数据类型重命名
typedef struct linklist //定义一个结构体,分为数据域和指针域
{
    int data;
    struct linklist *next;//保存了下一个节点的地址
}linkl_node,* linkl_pnode;

2.创建链表头节点

代码如下:

linkl_pnode linkl_creat()
{
    linkl_pnode H=(linkl_pnode)malloc(sizeof(linkl_node));//在堆区开辟空间
    if(NULL == H)
    {
        printf("内存分配失败\n");
        return NULL;
    }
    H->next = NULL;//末尾节点的指针域为NULL
    return H;    
}

3.链表的遍历,判空,长度

//长度(有效数据节点个数)
int linkl_len(linkl_pnode H)
{
    int count=0;
    while(H->next)
    {
        H=H->next;
        count++;
    }
    return count;
}
//判空
int linkl_empty(linkl_pnode H)
{
    if(H->next==NULL)
        return 0;
    else
        return -1;
}
//遍历输出
int linkl_traverse(linkl_pnode H)
{
    if(0==linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return -1;
    }
    while(H->next)
    {
        H=H->next;
        printf("data=%d\n",H->data);
    }
    return 0;
}

3.增加链表节点

//插入新节点
/*pos值这里为节点下标,人为的认为链表的第一个数据节点下标为0。
头节点并不是有效数据节点,它指向的下一个节点为第一个有效数据节点
*/
int linkl_inser(linkl_pnode H,int pos,linklist_data_t data)
{
    linkl_pnode new= linkl_creat();//给新的节点在堆区分配空间
    if(pos<0 || pos>linkl_len(H))
    {
        printf("pos值非法\n");
        return -1;
    }
    while(pos--) //H走到下标为pos的前一个节点
        H=H->next;
    new->data=data;
    new->next=H->next;//新节点指向插入位置的下一个节点
    H->next=new;//插入位置的前一个节点指向新节点
}

4.删除节点

//按照下标删除
int linkl_delete(linkl_pnode H,int pos)
{
    linkl_pnode p;
    if(0==linkl_empty(H))
    {
        printf("链表为空\n");
        return -1;
    }
    if(pos<0 || pos>=linkl_len(H))
    {
        printf("Err:位置值非法\n");
        return -2;
    }
    while(pos--)
        H=H->next;
    p=H->next;//保存要删除的元素
    H->next=p->next;//得到删除元素下一个元素的位置
    free(p);//释放删除节点的空间
    p=NULL;//指向NULL,避免野指针
    return 0;
}
//按照值删除
int linkl_delete_data(linkl_pnode H,linklist_data_t data)
{
    if(0==linkl_empty(H))
    {
        printf("链表为空\n");
        return -1;
    }
    int pos=-1;
    linkl_pnode L=H;//保存头节点
    //记录数据对应的节点下标
    while(L->data!=data && L->next)
    {
        pos++;
        L=L->next;
    }
    if((pos==linkl_len(H)-1) && L->data!=data)
    {
        printf("Err:There is no such data\n");
        return -2;
    }
    while(pos--)
        H=H->next;
    L=H->next;
    H->next=L->next;
    free(L);
    L=NULL;
    return 0;
}

5.改变节点数据

//按照下标改变内容
int linkl_change_pos(linkl_pnode H,int pos,linklist_data_t data)
{
    if(0==linkl_empty(H))
    {
        printf("链表为空\n");
        return -1;
    }
    if(pos<0 || pos>=linkl_len(H))
    {
        printf("Err:位置值非法\n");
        return -2;
    }
    while(pos--)
        H=H->next;
    H->next->data=data;
    return 0;
}
//按照值改变
int linkl_change(linkl_pnode H,linklist_data_t old_data,linklist_data_t new_data)
{
    if(0==linkl_empty(H))
    {
        printf("链表为空\n");
        return -1;
    }
    int flag=0;
    while(H->next)
    {
        if(H->data==old_data)
        {
            H->data=new_data;
            return 0;
        }
        H=H->next;
    }
    if(flag==0)
    {
        printf("Err:没有要修改的值\n");
        return -2;
    }
}

6.查询节点数据

//按值查询 链表中可能会有多个节点存储有满足要查询的值,所以返回一个数组
linkl_pnode *linkl_search(linkl_pnode H,linklist_data_t data,linkl_pnode *pos)
{
    if(0==linkl_empty(H))
    {
        printf("Err:链表为空\n");
        *pos=(linkl_pnode)(-1);
        return pos;
    }
    linkl_pnode L=H;
    int poss=-1,i=0,j=0;
    int pos1[100];//保存下标
    while(L->next)
    {
        poss++;
        L=L->next;
        if(L->data==data)
        {
            pos1[i]=poss;
            i++;
        }
    }
    if(0==i)//判断节点中是否有要查询的数据
    {
        printf("Err:There is no such data\n");
        *pos=(linkl_pnode)(-2);
        return pos;
    }
    pos1[i]=-2;//作为结束位,这里存放的数据已经不是合法的节点下标值了
    for(i=0;pos1[i]>=0;i++)
    {
        L=H;
        while(pos1[i]--)
        {
            L=L->next;
        }
        pos[j++]=L->next;//将满足数值的节点地址给到数组中去
    }
    pos[j]=NULL;
    return pos;
}
//按下标查询
linklist_data_t linkl_search_pos(linkl_pnode H,int pos)
{
    if(0==linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return -1;
    }
    if(pos<0 || pos>=linkl_len(H))
    {
        printf("Err:位置值非法\n");
        return -2;
    }
    while(pos--)
        H=H->next;
    return H->next->data;
}

7.链表的清空与销毁

//清空 除头节点外的节点全部删除
int linkl_clean(linkl_pnode H)
{
    if(0==linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return -1;
    }
    while(linkl_empty(H))//不断删除下标为0的节点,直到头节点中next指向NULL
    {
        linkl_delete(H,0);
    }
    return 0;
}
//销毁:链表的所有节点全部删除
int linkl_destroy(linkl_pnode *H)//要改变头节点H,便需要传递H的地址。
{
    if(0==linkl_empty(*H))
    {
        free(*H);
        *H=NULL;
    }
    else
    {
        linkl_clean(*H);
        free(*H); 
        *H=NULL;
    }
    return 0;
}

8.链表的排序

//排序 
int linkl_sort(linkl_pnode H)
{
    if(0==linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return -1;
    }
    int len=linkl_len(H);
    linkl_pnode p,q;
    int temp=0;
    for(p=H->next;p!=NULL;p=p->next)//可以和下面的选择排序对应理解
    {
        for(q=p->next;q!=NULL;q=q->next)
        {
            if(p->data>q->data)
            {
                temp=p->data;
                p->data=q->data;
                q->data=temp;
            }
        }
    }
    return 0;
   /* 选择排序
    int i,j,t=0;
    for(i=0;i<len;i++)
    {
        for(j=i;j<len;j++)
        {
            if(a[i]>a[j])
            {
                t=a[i];
                a[i]=a[j];
                a[j]=t;
            }
        }
    }
    */
}

9.链表的逆序

逆序有多种方法,这里介绍两种较为简单的方法

//逆序通过头插法实现
/*创建一个新的头节点,遍历原链表,得到的节点不断在新头节点位置做头插*/
linkl_pnode linkl_reversed_odd(linkl_pnode H)
{
    if(0 == linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return (linkl_pnode)(-1);
    }
    linkl_pnode L=linkl_creat();
    while(H->next)
    {
        linkl_inser(L,0,H->next->data);
        H=H->next;
    }
    return L;
}

//逆序(双指针得到)
linkl_pnode linkl_reversed_odd1(linkl_pnode H)
{
    if(0 == linkl_empty(H))
    {
        printf("Err:链表为空\n");
        return (linkl_pnode)(-1);
    }
    linkl_pnode q=NULL;//保存前一个节点
    linkl_pnode var=H;//保存当前节点
    linkl_pnode p;  //保存后一个节点
    while(var)
    {
        p=var->next;
        var->next=q;//指向前一个节点
        q=var;      //后移
        var=p;
    }
    return q;
}

总结

以上的内容都是对单链表的基础操作,链表还有循环和双链表等种类等待你的了解

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值