实验一:链表的基本操作

预备知识

你们的每一次浏览都是我坚持写下去的动力

结构体

C语言的数据类型包括基本数据类型、构造型据类型指针类型和无类型;基本数据类型有整型、实型、字符型、枚举;构造数据类型包括数组、结构体、公用体;结构体可以有基本数据类型与指针类型组合而来,主要用来表示链表、栈、树等较为复杂的数据对象。

  • 基本格式

    以学生信息为例

    struct student
    {
      char sex;               //数据域
      int age;
      struct student *next;   //指针域
    };                       //结构体要以分号结束
    

    链表的一个结点就表示一个结构体对象,包括数据域可指针域;指针next指向下一个节点的地址;

  • typedef运算符

    简单理解就是typedef可以为已有的数据类型起一个新名字

    //给int起个新名字叫INTER
    typedef int INTER;    
    int e=10;    INTER e=10;  //等价
    

    同理,也可以为结构体起一个新名字,以方便代码书写,并且可以使代码简洁易读

    typedef struct student
    {
      char sex;             
      int age;
      struct student *next; 
    }STUDENT;
    

全局变量

简单讲,在函数体内定义的叫做局部变量,包括主函数,在函数体外定义的即为全局变量;全局变量的特点有一下几点

  • 存储在C语言内存结构中的静态存储区
  • 所有函数都可以访问并改变其值
  • 可以在主函数内定义局部变量通过向自定义函数传参的方式代替全局变量
int e=10;     //全局变量
int main()
{
  int m=20;   //局部变量
  printf("%d  %d",e,m);
}
// 10  20

指针

  • 指向结构体的指针

    typedef struct student
    {
      char sex;              
      int age;
      struct student *next;
    }STUDENT;
    STUDENT *p;  //指针p指向结构体STUDENT
    
  • 指向结构体变量的指针

    typedef struct student
    {
      char sex;              
      int age;
    }STUDENT;
    STUDENT stu1,stu2,stu3;   //定义三个对象,分别代表学生1、2、3
    STUDENT *p;               //此时p未初始化,指向一个随机值,因此需要对指针初始化 
    p=&stu1;                  //把学生1的地址赋给p
    

    由于链表所用的结构体设置了指针域,由指针域指向下一个结点,实际上并不需要指向结构体变量的指针

代码解析

  • 创建链表

    #include<stdio.h>
    #include<stdlib.h>
    
    typedef struct LNode
    {
        char date;
        struct LNode *next;
    }LNode;
    
    LNode *head; //定义链表头指针  
    int n;       //n用来存放链表结点个数
    

    上述代码中,头指针与结点个数设置为全局变量,因为链表不是随机存储结构,必须要通过头指针来访问其他结点,在对链表进行插入、删除等操作时,各功能函数都需要获取头指针;

  • 插入操作

    //插入元素e到表尾
    void Insert_end()
    {
        char e;
        LNode *p,*p_new = NULL;
        p=head;
        printf("请输入要插入的元素:\n");
        scanf("%c",&e);
        for(int i=0;i<n;i++)    //通过for循环,是p指向最后一个结点
        {
            p=p->next;
        }
        p_new=(LNode *)malloc(sizeof(LNode)); //为新添的节点分配空间
        if(p_new==NULL)
        {
            printf("内存分配失败!\n");
            exit(0);
        }
        p_new=p;       
        p_new->date=e;
        p_new->next=NULL;
        n++;
    }
    
    //插入元素e到表头
    void Insert_head()
    {
        
        char e;
        printf("请输入元素:\n");
        getchar();  //吃掉缓冲区的回车键,一定要加这一句,不然很惨的
        scanf("%c",&e);
        LNode *p,*p_new;
        p=head;
        p_new=(LNode *)malloc(sizeof(LNode));
        if(p_new==NULL)
        {
            printf("内存分配失败!\n");
            exit(0);
        }
        p_new->next=p;
        p_new->date=e;
        p=p_new;
        head=p;     //使head重新指向头指针
        n++;
    }
    
  • 对于元素的插入,可以插入到表头,表尾,或是某个元素之后,如果把每种情况都写成一个函数太过繁 琐,因此下面的完整代码中,把三种情况整合到一个函数中

完整代码

程序运行界面比较简单,我觉得重在算法的理解

#include<stdio.h>
#include<stdlib.h>
typedef struct LNode
{
    char date;
    struct LNode *next;
}LNode;

LNode *head;
int n;

//创建链表并输入元素
void create()
{
    char e;             //用来暂时存放你从键盘输入的元素
    LNode *p,*p_new;    //p指向当前最后一个结点,p_new指向新插入的结点
    head=(LNode *)malloc(sizeof(LNode));  //为头指针指向的头节点开辟空间
    if(head==NULL)
    {
        printf("内存分配失败!\n");
        exit(0);
    }
    p=head;         //将头结点的地址赋给p,用p代替head,因为链表为空时,头结点也是最后一个结点
    printf("请输入元素!\n");
    for(int i = 0;;i++)  
    {
        p_new=(LNode *)malloc(sizeof(LNode));
        if(p_new==NULL)
        {
            printf("内存分配失败!\n");
            exit(0);
        }
        scanf("%c",&e);
        n++;          //更新链表元素个数
        p->next=p_new;//将新结点的地址赋给最后一个结点的指针域
        p->date=e;    //将从键盘输入的元素e存放到上个结点的数据域
        p_new->next=NULL; 
        p=p_new;     //使p重新指向最后一个结点
        if(getchar()=='\n')
        {
            break;
        }   
    }
  free(p);  //上面代码执行完毕后,实际上最后一个结点是空的,因为数据都是存放在倒数第二个结点的数据域(p->date=e),因此应该释放最后一个结点占用的内存空间
}
//显示链表中所有元素
void Output()
{
    LNode *p;
    p=head;
    printf("链表中的元素为:\n");
    for(int i=0;i<n;i++)
    {
        printf("%c ",p->date);
        p=p->next;
    }
    printf("\n");
}
//插入元素e到首、尾、第i个元素后面
void Insert_i()
{
    int t;      //t用来存放插入的位置信息
    char e;
    LNode *p,*p_new;
    p=head;
    p_new=(LNode *)malloc(sizeof(LNode));
    if(p_new==NULL)
    {
        printf("内存分配失败!\n");
        exit(0);
    }
    printf("请输入位置i和元素e:\n");
    scanf("%d",&t);
    getchar();    
    scanf("%c",&e);
    //插入到表头
    if(t==0)
    {
        p_new->next=p;
        p_new->date=e;
        p=p_new;
        head=p;
        n++;
    }
    //插入到表尾
    else if(t==n)
    {
        for(int i=0;i<n;i++)
        {
            p=p->next;
        }
        p_new=p;
        p_new->date=e;
        p_new->next=NULL;
        n++;
        
    }
  //插入到某个元素的后面
    else
    {
        for(int i=0;i<t-1;i++)
        {
            p=p->next;
        }
        p_new->next=p->next;
        p->next=p_new;
        p_new->date=e;
        
        n++;
    }
}

//删除指定元素
void delete()
{
    char e;
    int n;
    LNode *p;
    p=head;
    printf("请输入需要删除的元素的位置: ");
    scanf("%d",&n);
    if(n==1)
    {
        head=p->next;
    }
    else{
       for(int i=0;i<n-2;i++)
       {
           p=p->next;
       }
       e=p->next->date;
       p->next=p->next->next;
       n--;
    }
  free(p);
}

//菜单
void menu()
{
    
    printf("\t\t\t* 1-输入\n\t\t\t* 2-插入\n\t\t\t* 3-删除\n\t\t\t* 4-输出\n\t\t\t* 0-退出\n");
    
}

int main()
{
    head=NULL;
    int choose;
    menu();
   while(1)
   {
       printf("%c亲,请开始你的操作:",0);
       scanf("%d",&choose);
       getchar();     //吃掉你输入后按下的回车键
       if(choose==0)
           exit(0);
       else
       {
           switch (choose) {
               case 1:
                   create();
                   break;
               case 2:
                   Insert_i();
                   break;
               case 3:
                   delete();
                   break;
               case 4:
                   Output();
                   break;
               default:
                   break;
           }
       }
       
   }
    return 0;
}

联系作者 QQ:749866529

posted @ 2019-10-03 19:51  翁德彪  阅读(896)  评论(0编辑  收藏  举报