C语言数据结构链表的实现

C语言数据结构单链表的实现。

定义

//- - - - - 单链表的存储结构- - - - -
typedef struct  LNode
{
   ElemType  data;                              //结点的数据域
   struct LNode  *next;                         //结点的指针域
}LNode,*LinkList;                               //LinkList为指向结构体LNode的指针类型

在后续的代码块中,为了方便读,则LNode代表一个节点,Linklist代表一个表。

为了操作方便,则在第一个节点处之前附设一个结点,称之为头结点。

下面对首元结点、头结点、头指针三个容易混淆的概念加以说明。
(1)首元结点是指链表中存储第一个数据元素的结点。
(2)头结点是在首元结点之前附设的一个结点,其指针域指向首元结点。头结点的数据域可以不存储任何信息,也可存储与数据元素类型相同的其他附加信息。
3)头指针是指向链表中第一个结点的指针。若链表设有头结点,则头指针所指结点为线性表的头结点;若链表不设头结点,则头指针所指结点为该线性表的首元结点

初始化

【算法步骤】
① 生成新结点作为头结点,用头指针L指向头结点。
② 头结点的指针域置空。

ElemType chushihua(Lnode **L){//初始化链表,放头节点。如果没有双指针,则是一个复制品。
    *L=(Lnode *)malloc(sizeof(Lnode));
    if(*L==NULL){//为了代码的健壮性
        printf("内存分配失败,程序退出");
        exit(0);
    }
    (*L)->next=NULL;//头节点
    printf("内存分配成功\n");
    return L;
}

在数据结构书里面定义的是伪代码,也可以理解为C++的代码。C语言里面是不允许出现引用的。
** 为什么需要引用**
如果不引用的话,就是一个复制的L,属于假的L

创建链表

头插法创建。

算法步骤
① 创建一个只有头结点的空链表。
② 根据待创建链表包括的元素个数n,循环n次执行以下操作:
·生成一个新结点*p;·
输入元素值赋给新结点p的数据域;·将新结点p插入到头结点之后。
在这里插入图片描述
头插法是一种第一个输入的数据,但是却是在表尾。

ElemType TouCha(){//头插建立单链表。头插法包含初始化。但是输入的数据,是最后一个数据位于单链表的第一个。
  LinkList L=(Lnode *)malloc(sizeof(Lnode));//头元节点
   L->next=NULL;//头节点指向制空
    int i;
    printf("请输入数据,输入WIN键+Z并回车结束输入\n");
    while (scanf("%d",&i)!=EOF)
    {
      Lnode *p=(Lnode *)malloc(sizeof(Lnode));
       p->data=i;
       p->next=L->next;//新建节点指针域指向和头节点指向同一位置。
       L->next=p;//首节点的下一个指针指向新建节点
    }
    return L;//返回链表。
}

尾插

【算法步骤】
① 创建一个只有头结点的空链表。
② 尾指针r初始化,指向头结点。
③ 根据创建链表包括的元素个数n,循环n次执行以下操作:
生成一个新结点p;
·输入元素值赋给新结点
p的数据域;·
将新结点p插入到尾结点r之后;
·尾指针r指向新的尾结点*p。

ElemType Weicha(){
    LinkList L=(Lnode *)malloc(sizeof(Lnode));
    L->next=NULL;
    Lnode *r;
    r=L;//r始终指向最后的节点。最开始指向头节点
    int i;
    printf("请输入数据,输入WIN键+Z并回车结束输入\n");//
    while (scanf("%d",&i)!=EOF)//
    {
        Lnode *p=(Lnode *)malloc(sizeof(Lnode));//申请新的节点空间。
        p->data=i;
        r->next=p;
        r=p;
    }
    r->next=NULL;//终端节点。
 return L;
}

取值

【算法步骤】
① 用指针p指向首元结点,用j做计数器初值赋为1。
② 从首元结点开始依次顺着链域next向下访问,只要指向当前结点的指针p不为空(NULL),并且没有到达序号为i的结点,则循环执行以下操作:·p指向下一个结点;·计数器j相应加1。
③ 退出循环时,如果指针p为空,或者计数器j大于i,说明指定的序号i值不合法(i大于表长n或i小于等于0),取值失败返回空;否则取值成功,此时j=i时,p所指的结点就是要找的第i个结点,用参数e保存当前结点的数据域,返回空。

ElemType GetElem(LinkList L,int i,ElemType *e){
    Lnode * p=L->next;//P指向首元节点
    int j=1;//计数器
    while (p!=NULL&&j<i)
    {
        p=p->next;
        ++j;
    }
    if (!p||j>i)
    {
        printf("错误");
        return ;
    }
    e=p->data;//取第i个节点的数据域。
    printf("第%d位的数据是%d\n",i,e);
    return ;
}

查找

【算法步骤】
① 用指针p指向首元结点。
② 从首元结点开始依次顺着链域next向下查找,只要指向当前结点的指针p不为空,并且p所指结点的数据域不等于给定值e,则循环执行以下操作:p指向下一个结点。
③ 返回p。若查找成功,p此时即为结点的地址值,若查找失败,p的值即为NULL。

Lnode * LocateElem(LinkList l,ElemType  e){
    Lnode *p=l->next;//指向首元节点。
     int i=1;
    while (p && p->data!=e)
    {
        p=p->next;
        i++;
    }
    return i;// 返回位序,return p;//返回e的节点地址p;
}

插入

【算法步骤】
① 查找结点i-1并由指针p指向该结点。
② 生成一个新结点s。
③ 将新结点
s的数据域置为e。
④ 将新结点s的指针域指向结点ai。
⑤ 将结点
p的指针域指向新结点*s。

ElemType Charu(LinkList *L,int i,ElemType e){
    Lnode *s,*p;
    int j=0;
    p=L;
while (p!=NULL && j<i-1)       
   {
    p=p->next;
    j++;
   }
   s=(Lnode *)malloc(sizeof(Lnode));
   s->data=e;
    s->next=p->next;
    p->next=s;
    return L; 

}

删除

算法步骤】删除单链表的第i个结点ai的具体过程如图2.14所示,图中的对应的4个步骤说明如下。[插图]图2.14 删除单链表第i个结点的过程① 查找结点ai−1并由指针p指向该结点。② 临时保存待删除结点ai的地址在q中,以备释放。③ 将结点*p的指针域指向ai的直接后继结点。④ 释放结点ai的空间。

ElemType ListDelete(LinkList *L,int i ){
    //删除第I个位置的元素
    Lnode *p=L,*s;//引用指针s零时保存删除节点的的地址。
    int j=0;
    while ((p->next!=NULL)&& j<(i-1))//与插入的位置不同。插入时合法位置有n+1个,删除时合法地址只有n个。会出现空指针的情况
    {
        p=p->next;
        ++j;
    }
    if (!(p->next)|| j>i-1)//i>n或者i<1时,位置不合理。
    {
        printf("错误,删除元素位置不和理\n");
        return ;
    }
    s=p->next;//临时保存被删节点准备释放。
    p->next=s->next;//改变删除节点的前面一个节点的指向。
    free(s);//
    printf("删除成功");
    return L;
}

完整代码实列

#include<stdio.h>
#include<stdlib.h>
typedef int ElemType;
typedef struct Lnode
{
   ElemType data;//存放数据
   struct Lnode *next;//指向下一个节点
}Lnode,*LinkList;
ElemType chushihua(Lnode **L){//初始化链表,放头节点。如果没有双指针,则是一个复制品。
    *L=(Lnode *)malloc(sizeof(Lnode));
    if(*L==NULL){//为了代码的健壮性
        printf("内存分配失败,程序退出");
        exit(0);
    }
    (*L)->next=NULL;//头节点
    printf("内存分配成功\n");
    return L;
}
//创建,头插法
ElemType TouCha(){//头插建立单链表。头插法包含初始化。但是输入的数据,是最后一个数据位于单链表的第一个。
  LinkList L=(Lnode *)malloc(sizeof(Lnode));//头元节点
   L->next=NULL;//头节点指向制空
    int i;
    printf("请输入数据,输入WIN键+Z并回车结束输入\n");
    while (scanf("%d",&i)!=EOF)
    {
      Lnode *p=(Lnode *)malloc(sizeof(Lnode));
       p->data=i;
       p->next=L->next;//新建节点指针域指向和头节点指向同一位置。
       L->next=p;//首节点的下一个指针指向新建节点
    }
    return L;//返回链表。
}
//创建尾插法
ElemType Weicha(){
    LinkList L=(Lnode *)malloc(sizeof(Lnode));
    L->next=NULL;
    Lnode *r;
    r=L;//r始终指向最后的节点。最开始指向头节点
    int i;
    printf("请输入数据,输入WIN键+Z并回车结束输入\n");//
    while (scanf("%d",&i)!=EOF)//
    {
        Lnode *p=(Lnode *)malloc(sizeof(Lnode));//申请新的节点空间。
        p->data=i;
        r->next=p;
        r=p;
    }
    r->next=NULL;//终端节点。
 return L;
}
//插入
ElemType Charu(LinkList *L,int i,ElemType e){
    Lnode *s,*p;
    int j=0;
    p=L;
while (p!=NULL && j<i-1)       
   {
    p=p->next;
    j++;
   }
   s=(Lnode *)malloc(sizeof(Lnode));
   s->data=e;
    s->next=p->next;
    p->next=s;
    return L; 

}
//单链表取值
ElemType GetElem(LinkList L,int i,ElemType *e){
    Lnode * p=L->next;//P指向首元节点
    int j=1;//计数器
    while (p!=NULL&&j<i)
    {
        p=p->next;
        ++j;
    }
    if (!p||j>i)
    {
        printf("错误");
        return ;
    }
    e=p->data;//取第i个节点的数据域。
    printf("第%d位的数据是%d\n",i,e);
    return ;
}
//查找
Lnode * LOcateElem(LinkList l,ElemType  e){
    Lnode *p=l->next;//指向首元节点。
     int i=1;
    while (p && p->data!=e)
    {
        p=p->next;
        i++;
    }
    return i;// 返回位序,return p;//返回e的节点地址p;
}
//删除
ElemType ListDelete(LinkList *L,int i ){
    //删除第I个位置的元素
    Lnode *p=L,*s;//引用指针s零时保存删除节点的的地址。
    int j=0;
    while ((p->next!=NULL)&& j<(i-1))//与插入的位置不同。插入时合法位置有n+1个,删除时合法地址只有n个。会出现空指针的情况
    {
        p=p->next;
        ++j;
    }
    if (!(p->next)|| j>i-1)//i>n或者i<1时,位置不合理。
    {
        printf("错误,删除元素位置不和理\n");
        return ;
    }
    s=p->next;//临时保存被删节点准备释放。
    p->next=s->next;//改变删除节点的前面一个节点的指向。
    free(s);//
    printf("删除成功");
    return L;
}

int main()
{
    LinkList l;
   /*初始化该函数
   L=chushihua(&L);*/
    int i,n;
   l=Weicha(l);
   printList(l);
   while (printf("请选择你需要操作:\n1:插入\n2:取值:\n3:按数据查找\n4:按位置删除\n5:打印表\n"),scanf("%d",&n)!=EOF)
   {
    switch (n)
    {
    case 1:
    printf("输入要插入的位置和数据\n");
    printf("位置;");
    scanf("%d",&i);
    printf("数据:");
    scanf("%d",&n);
    l=Charu(l,i,n);
    printList(l);
    break;
    case 2:
    printf("输入要取值的位置和数据\n");
    printf("位置;");
    scanf("%d",&i);
    int e;
    GetElem(l,i,e);
     break;
     case 3:
    printf("输入要查找的数据\n");
    printf("数据;");
    scanf("%d",&i);
    printf("%d", LOcateElem(l,i));
     break;
     case 4:
    printf("输入要删除的位置\n");
    printf("位置;");
    scanf("%d",&i);
    ListDelete(l,i);
     break;
     case 5:
     printList(l);
     break;
    default:
    printf("没有该选项,请重新输入\n");
        break;
    }  
   }
    system("pause");
    return 0;
}
void printList(LinkList L){
    Lnode *p=L->next;
    int i=1;
    while(p){
             printf("第%d个元素的值为:%d\n",i++,p->data);
             p=p->next;
    }
} 
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dagny--July 25

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值