【数据结构】单链表的创销增删查改 c语言代码

定义一个单链表

单链表采用结构体的方式定义,元素组成是一个data+一个同类型指针。

最后将结构体重命名,这里的两个重命名基本意思相同,做这样的命名是为了更好的区分链表的节点。

?为什么链表是指针类型,节点是普通类型呢

typedef struct LNode{
    int data;
    struct LNode *next;
}*linklist,LNode;

创建一个单链表

初始化

给头节点分配内存空间,并且让L指针指向头节点

1.为了代码的健壮性,检查分配是否成功

2.置空链表:因为其他表都是以单链表作为基础,因此这里实现的是单链表带头结点的置空。

bool Initlist(linklist *L){
    *L=(LNode*)malloc(sizeof(LNode));//创建头节点
    //L=(linklist)malloc(sizeof(linklist));
    if (L==NULL)
        return false;//分配内存失败
    (*L)->next=NULL;//置空链表
    return true;
}

头插法建立单链表

基本思路:每次来一个新节点都插入头节点和第一个节点之间。

基本方法:给新节点分配存储空间,读入新节点,数据赋值;新节点先连第一个节点,头节点连接新节点

//头插法建立单链表
linklist List_headInsert(linklist *L){
    Initlist(L);
    int a;
    linklist s;
    scanf("%d",&a);//输入一个数值
    //可不可以没有这一句?
    (*L)->next=NULL;
    while(a!=9999)
    {
        s=(LNode*)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=a;//存放数据
        s->next=(*L)->next;
        (*L)->next=s;
        scanf("%d",&a);
    }
    return *L;
}

尾插法建立单链表

基本思路:创建新节点,尾部节点指向新节点;最后,新节点next置空;得有一个p节点始终指向链表尾部,每次循环过后也要移动

//尾插法建立单链表
linklist List_TailInsert(linklist *L){//建立一个正向链表
    Initlist(L);
    int a;
    scanf("%d",&a);//输入一个数值
    LNode *r=*L;//设定两个指针指向头节点
    linklist s;
    while(a!=9999)//终止条件
    {
        s=(LNode *)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=a;//存放数据
        r->next=s;//连接两个节点
        r=s;//移动r指向新节点
        scanf("%d",&a);
    }
    r->next=NULL;//让链表末尾指向空
    return *L;//返回头指针
}

基本操作

创销增删改查六种操作,现在才实现了一种。销毁是删除的循环版,增加和修改的基础是查找,所以我们先讲这两个。

查找

按位查找

基本思路:从头节点开始查找,头节点记为第0位,找到第i位为止;然后如果要后插,就用尾插法,前插就找到第i-1位为止(具体表现在代码里就是j和p的错位,如果找的是i位,就j和p同步;如果找到的是i-1位,就让j比p大1)(前插还有一个骚操作,就是照旧后插,但是交换两个节点的值)(这里带上新节点p主要是为了方便后续插入,如果单纯头插法建立单链表,不需要指针p;p在这里初始化为头节点,在尾插法里初始化为也是头节点,但是概念里它要始终指向尾部节点)

代码是插入位序为i的节点

bool Insert_order(linklist *L,int i,int e){
    if (i>len(L)||i<1)
        return false;
    linklist p=*L;
    //int j=0;
    int j=1;
    while(p&&j<i){//把握要找的节点和插入的地方的区别
        j++;
        p=p->next;
    }
    LNode *s;
    s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}

按值查找

基本思路:按照上面的索引,不过终止条件要修改为p->data==a这种,最后返回一个true或者索引

删除

基本思路:删除任意一个节点,需要把他前面的节点和后面的节点连起来,所以就需要两个指针,找到任意节点的前驱节点,这里要注意找一个临时变量temp来代替被删除的节点,不能直接p=p->next->next(这一步是连接被删除节点的前后两个节点)

bool delete_list(linklist *L,int i)
{
    if (i>len(L)||i<1)
        return false;
    linklist p=*L;
    int j=1;
    while(p&&j<i){//把握要找的节点和插入的地方的区别
        j++;
        p=p->next;
    }
    linklist x=p->next;//有点像temp操作,不能直接对本体操作
    p->next=x->next;//让p的next变成另一个,而不是p=p—>next->next
    free(x);
    return true;
}

销毁

上面那个函数是删除第i个节点,重复上面操作,直到删除所有节点即可

链表逆置

基本思路:从头结点开始遍历链表并取出,然后新建一个链表,以尾插法方式

linklist List_Invert(linklist L,linklist *L2){
    //linklist L2;
    linklist s,p=L->next;//提前声明是s,p的类型
    *L2=(LNode *)malloc(sizeof(LNode));
    (*L2)->next=NULL;
    while(p)
    {
        s=(LNode *)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=p->data;//存放数据
        s->next=(*L2)->next;
        (*L2)->next=s;
        p=p->next;
    }
    return *L2;
}

完整代码

//定义单链表
//建立一个单链表:头插法,尾插法;有头节点,无头结点
//删除某个元素:按值删除,按位删除
//查找某个元素:按值查找,按位查找
//插入某个元素
//修改某个元素
#include<stdio.h>
#include<stdbool.h>//c语言引用bool类型
#include <stdlib.h>
#include <malloc.h>
#include<assert.h>
//定义一个单链表节点数据结构
typedef struct LNode{
    int data;
    struct LNode *next;
}*linklist,LNode;


//带头节点初始化
//linklist *L实际意思就是取L地址,c语言中的特殊表示
bool Initlist(linklist *L){
    *L=(LNode*)malloc(sizeof(LNode));//创建头节点
    //L=(linklist)malloc(sizeof(linklist));
    if (L==NULL)
        return false;//分配内存失败
    (*L)->next=NULL;//置空链表
    return true;
}
//判断是否空链表
bool Empty(linklist L){
    return (L->next==NULL);
}
//尾插法建立单链表
linklist List_TailInsert(linklist *L){//建立一个正向链表
    Initlist(L);
    int a;
    scanf("%d",&a);//输入一个数值
    LNode *r=*L;//设定两个指针指向头节点
    linklist s;
    while(a!=9999)//终止条件
    {
        s=(LNode *)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=a;//存放数据
        r->next=s;//连接两个节点
        r=s;//移动r指向新节点
        scanf("%d",&a);
    }
    r->next=NULL;//让链表末尾指向空
    return *L;//返回头指针
}
//头插法建立单链表
linklist List_headInsert(linklist *L){
    Initlist(L);
    int a;
    linklist s;
    scanf("%d",&a);//输入一个数值
    //可不可以没有这一句?
    (*L)->next=NULL;
    while(a!=9999)
    {
        s=(LNode*)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=a;//存放数据
        s->next=(*L)->next;
        (*L)->next=s;
        scanf("%d",&a);
    }
    return *L;
}
//链表逆置思路:从前往后取链表的数,取出来的数以头插法的形式插入另一个链表
linklist List_Invert(linklist L,linklist *L2){
    //linklist L2;
    linklist s,p=L->next;//提前声明是s,p的类型
    *L2=(LNode *)malloc(sizeof(LNode));
    (*L2)->next=NULL;
    while(p)
    {
        s=(LNode *)malloc(sizeof(LNode));//创建一个新的节点来存放数据
        s->data=p->data;//存放数据
        s->next=(*L2)->next;
        (*L2)->next=s;
        p=p->next;
    }
    return *L2;
}
int len(linklist *L){
    linklist p=(*L)->next;
    int length=0;
    while(p){
        length++;
        p=p->next;
    }
    return length;
}
//按位序后插入
bool Insert_order(linklist *L,int i,int e){
    if (i>len(L)||i<1)
        return false;
    linklist p=*L;
    //int j=0;
    int j=1;
    while(p&&j<i){//把握要找的节点和插入的地方的区别
        j++;
        p=p->next;
    }
    LNode *s;
    s=(LNode *)malloc(sizeof(LNode));
    s->data=e;
    s->next=p->next;
    p->next=s;
    return true;
}
//指定节点的后插和删除十分好处理,因为直接通过next就可以定位;前插还需要循环查找到指定节点,因此我实现一下指定节点的前驱查找,笑死不用了,他直接后插了这个节点,然后交换两个的值,厉害了
//getelem不写了,能删除就能找到
//bool Getelem(linklist L,)
bool delete_list(linklist *L,int i)
{
    if (i>len(L)||i<1)
        return false;
    linklist p=*L;
    int j=1;
    while(p&&j<i){//把握要找的节点和插入的地方的区别
        j++;
        p=p->next;
    }
    linklist x=p->next;//有点像temp操作,不能直接对本体操作
    p->next=x->next;//让p的next变成另一个,而不是p=p—>next->next
    free(x);
    return true;
}
//前插的思路就是1.照旧后插,然后交换两个数值;2.始终保持p比j慢一步(j=1开始)
bool printlist(linklist L){
    if (L->next==NULL)
        return false;
    LNode *p=L->next;
    while(p!=NULL)
    {
        printf("%d ",p->data);
        p=p->next;
    }
    return true;
}
int main()
{
    linklist L=List_headInsert(&L);
    printf("头插法的结果是");
    printlist(L);
    printf("\n");
    // linklist L=List_TailInsert(&L);
    // printf("尾插法的结果是");
    // printlist(L);
    linklist L2;
    List_Invert(L,&L2);
    printf("链表逆置的结果是");
    printlist(L2);
    printf("\n");
    //在第二个位置后/前插一个888
    printf("链表插入的结果是");
    Insert_order(&L,2, 888);
    printlist(L);
    printf("\n");

    //删除第三个位置的元素
    printf("链表删除的结果是");
    delete_list(&L,3);
    printlist(L);
    printf("\n");
    return 0;
}

后记

代码都是我一个一个实现,并且测试过的,当时还参考了一些大佬的文章,但是忘了加参考,现在也很难找了,就算了。本来没写说明,但是发现不到一周,就把该忘的都忘了,所以又回来写说明啦,应该写的很清楚了,祝我和各位数据结构自由,互联网精神万岁

——---------------------------------------有IDP的人像星星,没有的人像落叶-------------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值