23.C语言 学生系统管理

一、实验目的

1.掌握链表的基本结构和关系。

2.掌握C指针和结构体的使用方法,掌握链表编程。

链表知识点讲解:

链表的遍历:

 有·两种链表,第一种是第一个节点数据部分为空。另一种是第一个节点就存放数据。

两种方式的编程是有点不同的:

        第一种方式,不管怎样对链表进行增加修改和删除,head的位置都不会变化,head的值只需要传入到函数中,不需要返回,不需要返回。

        第二种方式,如果数据插入到第一个节点,head存储的地址会发生变化。这种方式的链表在定义函数时,需要返回链表的head。

实现思路:

①用head指针永远指向链表头。

②用q指针一开始指向第一个有数据的节点,然后就可以访问到节点中的数据了。访问结束后,q移动到下一个节点(q=q->next;),直到q的值为NULL,结束对链表的访问。

/*    定义学生结构体的数据结构    */

typedef struct Student{

        char studentNo[20];

        char studentName[11];

}st;

/*   定义每条记录或节点的数据结构  */

typedef struct node{

        struct Student data;     //数据段

        struct node *next;         //指针域 

}Node,*Link;                        //Node为node类型的别名,Link为node类型的指针别名

Node st1;

Link p;(Link 本身就是一个指针类型的结构体变量)


链表的插入(建立):

在这个例子中,学生节点的插入是有顺序的(链表按照学号排序),也就是说,每次准备插入一个学生节点时,先要找到这个节点应该插入到那个节点的前面。有几种情况需要考虑:

1.当前链表是空的(head的指针域为空),状态如下(node为新节点(用malloc创立),准备插入的第一个节点):

此时,则直接用head->next = node;这条语句,就可以把node插入到链表中。 

if(head->next==NULL)
        head->next=node;

2.当前链表已经有节点的情况,状态如下图(node为新建立,准备插入的节点):

想要插入节点就需要两个一前一后的指针

 那么此时,就需要比较“node中studentNo”和“p的studentNo”,查看大小关系。如果“node中的studentNo”小于“p中的studentNo”,这把node插入到q,p之间。如果q,p永远保持一前一后的关系,这样才能保证在找到节点位置后,可以顺利的插入。                   q=head;
                                                                                                             p=head->next;
 

/*   增加学生记录   */

bool addNode(Link head)
{
    Link p,q;        //p,q两个节点一前一后
    Link node;       //node指针指向新创建的节点
    node=(Link)malloc(sizeof(Node));
    inputStudent(node);
    
    q=head;
    p=head->next;    //p指向head后面的第一个有效节点
    if(head->next==NULL)
        head->next=node;
    else{
        //循环访问链表中的所有节点
        while(p!=NULL)
        {
            if(node->data.studentNo < p->data.studentNo){
            //如果node节点的学号比p节点的学号小,则插在p的前面,完成插入后,提前退出子进程
                q->next = node;
                node->next=p;
                return true;
            }
            else{
                //如果node节点的学号比p节点的学号大,继续向后移动指针(依然保持q p一前一后)
                q=p;    
                p=p->next;    
            }
        }
        //如果没能提前退出循环,则说明之前没有插入,那么当前node节点的学号是最大值,此时插在链表的最后面
        q->next=node;
    }
    return true;
}

现步骤:


链表的删除:

 删除链表的思路:

首先,利用p指针扫描链表,根据学生的学号找到要删除的节点位置

如果找到,则用以下语句就可以删除p所指向的节点(free p)

                                                                q->next = p->next;

                                                                free(p);

这里编程的要点是:保证q在前,p在后的关系,这样才能保证在找到节点位置后,可以顺利的删除。

如果当前p的学号不等于要删除的学号,则p指针后移

要考虑如果找不到学生学号的情况。

测试数据时,应该实验可以找到学号的情形,和给定的学号找不到的情形,然后查看删除是否成功。

链表的修改,链表的查询:

这两个功能和删除链表的思路类似:

都是先找到要处理的节点,然后处理即可。

“修改”还需要考虑修改内容是否是学号,如果修改了学号,则应该编写程序保证不能破坏链表有序性的这个特点。


链表的清除:(养成好的习惯)

        当在菜单中选择了退出功能后,则结束程序。在结束程序之间,要考虑到一个问题:前面用malloc语句建立的节点,在main函数退出时,分配的存储空间是不会自动释放的,所以必须主动释放链表中所有节点占用的内存。(free语句释放所有malloc产生的节点

        定义一个clearLink函数,实现链表的遍历,并用free语句释放所有malloc产生的节点。


 

#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>

/* 定义学生结构体的数据结构 */
typedef struct Student{

        char studentNo[20];

        char studentName[11];

}st;


/* 定义每条记录或节点的数据结构 */

typedef struct node{

        struct Student data;     //数据段

        struct node *next;       //指针域 

}Node,*Link;                  //Node为node类型的别名,Link为node类型的指针别名

//定义提示菜单
void myMenu()
{
    printf("******************************菜    单*************************************\n");
    printf("        1  增加学生记录           2  删除学生记录                          \n");
    printf("        3  查找学生记录           4  修改学生记录                          \n");
    printf("        5  统计学生人数           6  显示学生记录                          \n");
    printf("        7  退出系统                                                        \n");
    printf("***************************************************************************\n");
}

void inputStudent(Link l)
{
    printf("请输入学生学号:");
    scanf("%s",l->data.studentNo);
    printf("请输入学生的姓名:");
    scanf("%s",l->data.studentName);

    //每个新创建的节点的next指针域都初始化为NULL
    l->next = NULL;
}

void inputStudentNo(char s[],char no[])
{
    printf("请输入要%s的学生学号:",s);
    scanf("%s",no);
}

void displayNode(Link head)
{
    //填写代码,根据传入的链表head头指针,扫描链表显示所有节点的信息

}

bool addNode(Link head)
{
    Link p,q;        //p,q两个节点一前一后
    Link node;       //node指针指向新创建的节点
    node=(Link)malloc(sizeof(Node));
    inputStudent(node);

    q=head;
    p=head->next;    //p指向head后面的第一个有效节点
    if(head->next==NULL)
        head->next=node;
    else{
        //循环访问链表中的所有节点
        while(p!=NULL)
        {
            if(node->data.studentNo < p->data.studentNo){
            //如果node节点的学号比p节点的学号小,则插在p的前面,完成插入后,提前退出子进程
                q->next = node;
                node->next=p;
                return true;
            }
            else{
                //如果node节点的学号比p节点的学号大,继续向后移动指针(依然保持q p一前一后)
                q=p;
                p=p->next;
            }
        }
        //如果没能提前退出循环,则说明之前没有插入,那么当前node节点的学号是最大值,此时插在链表的最后面
        q->next=node;
    }
    return true;
}

bool deleteNode(Link head,char no[])
{
    //按照给定的学号删除学生记录,如果删除成功返回true,如果没找到学号返回false
    //输入要处理的学号
    inputStudentNo("删除",no);

    return false;
}

bool queryNode(Link head,char no[])
{
    //按照给定的学号查询学生记录,如果查询成功返回true,如果没找到学号返回false

    //输入要处理的学号
    inputStudentNo("查询",no);

    return false;


}

bool modifyNode(Link head,char no[])
{
    //按照给定的学号找到学生记录节点,如果修改成功返回true,如果没找到学号返回false

    //输入要处理的学号
    inputStudentNo("修改",no);

    return false;

}

int countNode(Link head)
{
    //统计学生的人数,扫描链表统计节点个数,返回节点数
    //Link p;
    //int count = 0;
    //p = head->next;

    return false;
}

void clearLink(Link head)
{
    //Link q,p;
    //遍历链表,用free语句删除链表中用malloc建立起的所有节点

}

int main()
{
    int select;
    int count;
    Link head;      //定义链表
    char no[20];

    //建立head头结点,在这个程序中head指向头结点,头结点data部分没有内容,其后续节点才有真正的
    head = (Link)malloc(sizeof(Node));
    head->next = NULL;

    while(1)//死循环
    {
        myMenu();
        printf("\n请输入你的选择(1-7):");    //显示提示信息
        scanf("%d",&select);

        switch(select)
        {
            case 1:
                //增加学生记录
                if(addNode(head))
                    printf("成功插入一个学生记录。\n\n");
                break;

            case 2:
                //删除学生记录
                if(deleteNode(head,no))
                    printf("成功删除一个学生记录。\n\n");
                else
                    printf("没有找到要删除的学生节点。 \n\n");

            case 3:
                //查询学生记录
                if(queryNode(head,no))
                    printf("成功找到学生记录。\n\n");
                else
                    printf("没有找到学生记录。\n\n");
                break;

            case 4:
                //修改学生记录
                if(modifyNode(head,no))
                    printf("成功修改一个学生记录。\n\n");
                else
                    printf("没有找到要修改的学生节点。\n\n");
                break;

            case 5:
                //统计学生人数
                count = countNode(head);
                printf("学生人数为:%d\n\n",count);
                break;

            case 6:
                //显示学生记录
                displayNode(head);
                break;

            case 7:
                //退出前清除链表中的所有节点
                clearLink(head);
                return 0;
            default:
                printf("输入不正确,应该输入0~7之间的数。\n\n");
                break;
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值