单链表

作者: b6aed1af4328 | 来源:发表于2016-10-24 16:16 被阅读26次
    #include<stdio.h>//本代码意在锻炼对链表这一数据类型的管理。用readDataFromFile函数和saveData函数读取和
    #include<stdlib.h>//保存链表文件(注意:这里仅保存了链表的数据域),createList完成链表头节点的创建,inser
    #include<string.h>//tdata函数和deletedata函数完成节点的插入和删除(插入有头插,尾插,任意插入,这里要
                        //注意任意插入时p->next=temp->next,temp->next=p完成插入,删除有头删,尾删和任意位
    typedef struct Student{     //置删除,任意位置删除时用temp->next=p->next完成删除。有意思的是,插入与
    char name[20];              //删除,均只与前一个节点有关),用finddata函数和changedata函数完成链表的
    int num;                    //的查找和修改,基本思想是for循环遍历查找。sort函数完成链表的排序(包括
                                //冒泡,选择和脱链,这里最重要的是脱链,其中的i->data.num>temp->next->dat
                                //a.num秀我一脸,成功将temp和i的上一个节点对上,值得学习)
                                //本文要点1:回车残留 因要输入姓名字符串,要时刻注意回车残留。注意使用
                                //getchar().
                                //要点2:for遍历循环,开始时看要不要参与运算比较选择head还是head->next,
                                //结束时,temp!=NULL遍历完整个链表,temp->next!=NULL到倒数第二个节点,以
                                //此类推。
                                //要点3:结构体指针要么指向一个结构体整体,要么malloc指向堆区。千万千万注意
                                //指针的指向。
                                //本文使用了宏定义,typedef 和枚举,个人感觉可以将函数指针用上去,更简洁
                                //本文充分体现了DRY原则,能重复的都尽量用函数封装了。
    
    
    
    }Student;//将数据域与地址域分开,便于运算。且为下文的文件保存读取准备了便利条件。
    
    typedef struct LINK{
    Student data;
    struct LINK *next;
    }LINK,*pLINK;
    typedef enum IsRepeat//枚举   增强文件的可读性  在checkrepeatdata函数和getData函数中运用。
    {
        YES,
        NO
    }IsRepeat;
    
    /*
    enum season{
    spring 1;
    summer 3;
    autumn;//不写的话,依次递增为4
    winter;
    }
    
    */
    Student getData(pLINK head);//节点信息输入函数声明
    
    
    int getNumber();//节点数输入函数声明
    int getListLength(pLINK head)//链表长度函数,使用for循环遍历得到,有2种 head到temp->next!=NULL和head
    {                           //->next到temp!=NULL,个人认为后一种更易理解,都是数据节点。头节点没数据,
        int j=0;                    //不算到链表长度里的。而保存到文件中的是数据域,头节点自然也没份。
        pLINK temp;
        for(temp=head;temp->next!=NULL;temp=temp->next)
        {
            j++;
        }
        return j;
    }
    
    IsRepeat checkRepeatData(pLINK head,Student stu)//查重函数 for循环遍历,if条件判断,若能跑完整个循环
    {                                               //自然是没有重复的。
        pLINK temp;
        for(temp=head->next;temp!=NULL;temp=temp->next)
        {
            if(temp->data.num==stu.num)
            {
                return YES;
            }
        }
        if(temp==NULL)
        {
            return NO;
        }
        return NO;
    }
    
    
    
    #define filename\//宏定义 一般便于理解和很多字符要打,为便于编写时使用。
    "/home/lifulin/5"
    void myScanf(char *str,int size)//字符串输入函数,很典型   size内时遇\n止,并改\n为\0;size-1之外(跑完
    {                               //整个for循环)后,直接让str[i]='\0'(size-1的用意体现出来了),因为只能
        int i=0;                    //放这么多,之后的缓存都吸收释放掉。
        for(i=0;i<size-1;i++)
        {
            str[i]=getchar();
            if(str[i]=='\n')
            {
                break;
            }
        }   
            //说明在中途碰到了\n
            if(str[i]=='\n')//注意插入时的回车残余,要用getchar()吸收。
            {
                str[i]='\0';
            }
            else
            {
                str[i]='\0';
                while(getchar()!='\n');//while循环用getchar()吸收之后的输入缓存。
            }
        
    }
    
    //创建链表函数封装
    pLINK createList(pLINK head)
    {
        if(head==NULL)//防内存泄露。
        {
        head=(pLINK)malloc(sizeof(LINK));//if循环里可重定义,不好找出bug。
            head->next=NULL;              //代码块(花括号)作用域不一样。
        }
        printf("链表创建成功\n");
        return head;
    }
    
    //链表打印函数封装
    void printData(pLINK head)
    {
        if(head==NULL)//意思是有头节点,没有其他数据节点顶多不打印,不会报错。
        {               //连头节点都没有,直接写temp=head->next,直接报错。
        printf("无信息打印\n");
        return;
        }
        pLINK temp=head->next;
        printf("head->");
        for(temp=head->next;temp!=NULL;temp=temp->next)//标准的for循环遍历
        printf("[%s,%d]->",temp->data.name,temp->data.num);
        printf("NULL\n");
    }
    
    //链表头插函数封装
    
    void headInsertData(pLINK head)
    {
    /*  if(head==NULL)//insertData函数中已考虑
        {
            printf("链表没有创建\n");
            return;
        }*/
        pLINK p=(pLINK)malloc(sizeof(LINK));
        p->next=head->next;//将p指向的节点和原head的后一个节点相连。
        head->next=p;//将head指向的节点与p指向的节点相连。这样2边都连上,头插成功。
        p->data=getData(head);//输入数据。
        printf("数据头插成功\n");
    
    }
    
    //节点信息输入函数封装
    
    Student getData(pLINK head)
    {
        printf("请输入学生信息[name num]:\n");
        Student stu;
        myScanf(stu.name,20);
        scanf("%d",&stu.num);
        getchar();
        int errorCount=0;
        while(checkRepeatData(head,stu)==YES)//枚举
        {
            errorCount++;
            if(errorCount==3)
            {
                printf("输入错误次数过多\n");
                exit(0);//整数即可,整个程序退出,强制退出。(错误码)
            }
            printf("该学号学生已存在\n");
            printf("请重新输入数据[姓名 学号]:\n");
            myScanf(stu.name,20);
            scanf("%d",&stu.num);
            getchar();
        }
        return stu;
    
    }
    
    void tailInsertData(pLINK head)//尾插函数封装
    {
    /*  if(head==NULL)
        {
            printf("链表没有创建\n");
            return;
        }*/
        //找尾指针:最后一个数据节点的地址
        pLINK temp;
        for(temp=head;temp->next!=NULL;temp=temp->next)
        {
        }//这个循环跳出的条件:temp->next==NULL
        pLINK p=(pLINK)malloc(sizeof(LINK));
        p->data=getData(head);//输入数据 很巧妙的一步。
        temp->next=p;//这一步就完成了尾插的8成工作。
        p->next=NULL;
        printf("数据尾插成功\n");
    }
    
    void headDeleteData(pLINK head)
    {
    /*  if(head==NULL)//在deleteData函数中已考虑
        {
            printf("链表没有创建\n");
            return;
        }*/
        if(head->next==NULL)
        {
            printf("没有数据可删除\n");
            return;
        }
        pLINK temp=head->next;
        head->next=temp->next;//将head指向的节点的next指针直接指向head->next->next的节点,绕过了原head后的
        free(temp);             //的节点,完成头删。
        printf("头删成功\n");
    }
    
    void tailDeleteData(pLINK head)
    {
    /*  if(head==NULL)
        {
            printf("链表没有创建\n");
            return;
        }*/
        if(head->next==NULL)
        {
            printf("没有数据可删除\n");
            return;
        }
        pLINK temp=NULL;
        pLINK temp1=NULL;
        temp=head;
        temp1=temp->next;
        while(temp1->next!=NULL)//等待temp1->next==NULL;
        {
            temp=temp->next;
            temp1=temp->next;
        }
        temp->next=NULL;
        free(temp1);
        temp1=NULL;//空间虽然释放了,但指针仍可指向该地址,故改NULL。
        printf("尾删成功\n");
    }
    
    void randomDeleteData(pLINK head)
    {
     /*if(head==NULL)//在deleteData函数中已考虑。
     {
        printf("链表没有创建\n");
        return;
     }*/
     if(head->next==NULL)
     {
        printf("没有数据可删除\n");
        return;
     }
     int j=0;
     pLINK p=head;
     for(p=head;p->next!=NULL;p=p->next)
     {
        j++;
     }
     int select;
     int i=0;
     select=getNumber();
     if(select>j||select<=0)
     {
        printf("超出查询范围\n");
        return;
     }
     pLINK temp=head;
     pLINK temp1=temp->next;
     for(i=1;i<select;i++)
     { 
        temp=temp->next;
        temp1=temp1->next;
     }
     temp->next=temp1->next;
     free(temp1);
     printf("删除成功\n");
    }
    
    int getNumber()
    {
        int select;
        printf("输入要处理的节点的节点数\n");
        scanf("%d",&select);
        getchar();//在任意位置插入节点时与myScanf连用,为了消除回车残余而如此使
        return select;                      //  用。
    }
    
    void randomInsertData(pLINK head)
    {
    /*  if(head==NULL)
        {
            printf("链表没有创建\n");
            return;
        }*/
    /*  if(head->next==NULL)
        {
            printf("没有数据可删除\n");
            return;
        }*/
        int j=0;
         pLINK p1=head;
         for(p1=head;p1->next!=NULL;p1=p1->next)
            {
                j++;
             }
         int select=0;
         int i=0;
         select=getNumber();
         printf("%d %d\n",j,select);
         if(select>j+1||select<0)
         {
            printf("超出插入范围\n");
            return;
         }
    
    /*  int select;
        select=getNumber();
        int i;
    */  pLINK temp=head;
        for(i=0;i<select-1;i++)
        {
            temp=temp->next;
        }
        pLINK p=(pLINK)malloc(sizeof(LINK));
        p->next=temp->next;
        temp->next=p;
        p->data=getData(head);
    }
    
    void insertData(pLINK head)
    {
        if(head==NULL)
        {
            printf("链表没有创建\n");
            return;
    
        }
        int select;
        while(1)
        {
            printf("==============\n");
            printf("1.头插节点\n");
            printf("2.尾插节点\n");
            printf("3.任意位置插入节点\n");
            printf("4.返回上一层\n");
            printf("==============\n");
            scanf("%d",&select);
            getchar();//清除缓存\n。//其余的不需要吸收回车残余,因为不是字符串
            switch (select)           //输入,会智能识别数字输入。数字输入遇
            {                           //空格和回车即停止。
            case 1:
                headInsertData(head);
                break;
            case 2:
                tailInsertData(head);
                break;
            case 3:
                randomInsertData(head);
                break;
            case 4:
                return;
            default:
                printf("输入错误,请重新输入\n");
                break;
            }   
        }
    }
    
    void deleteData(pLINK head)
    {
        if(head==NULL)
        {
            printf("链表没有创建\n");
            return;
        }
        int select;
        while(1)
        {
            printf("==========\n");
            printf("1.头删节点\n");
            printf("2.尾删节点\n");
            printf("3.任意位置删除节点\n");
            printf("4.返回上一层\n");
            printf("==========\n");
            scanf("%d",&select);
            getchar();
            switch (select)
            {
            case 1:
                headDeleteData(head);
                break;
            case 2:
                tailDeleteData(head);
                break;
            case 3:
                randomDeleteData(head);
                break;
            case 4:
                return;
            default:
                printf("输入错误,请重新输入\n");
                break;
            }   
        }
    }
    
    
    
    void searchData(pLINK head)
    {
        if(head==NULL||head->next==NULL)
        {
            printf("无信息可查询\n");
            return;
        }
        int num;
        printf("请输入要查询的学号\n");
        scanf("%d",&num);
        pLINK temp;
        for(temp=head->next;temp!=NULL;temp=temp->next)
        {
            if(temp->data.num==num)
            {
                printf("[%s %d]\n",temp->data.name,temp->data.num);
                return;
            }
    
        }
        if(temp==NULL)
        {
            printf("查无此人\n");
            return;
        }
    }
    
    void changeData(pLINK head)
    {
        if(head==NULL||head->next==NULL)
        {
            printf("无信息可修改\n");
            return;
        }
        char name[20];
        printf("请输入姓名\n");
        myScanf(name,20);
        
        pLINK temp;
        for(temp=head->next;temp!=NULL;temp=temp->next)
        {
            if(strcmp(temp->data.name,name)==0)
            {
                printf("name=%s,number=%d\n",temp->data.name,temp->data.num);
                printf("请输入修改的学生信息\n");
                myScanf(temp->data.name,20);
                scanf("%d",&temp->data.num);
                printf("修改后的学生信息\n");
                printf("name=%s,number=%d\n",temp->data.name,temp->data.num);
                printf("修改成功\n");
                break;
            }
    
        }
        if(temp==NULL)
        {
            printf("查无此人\n");
            return;
        }
    }
    
    void saveData(pLINK head)//头节点因为没有数据,所以是读不进去的。
    {
        if(head==NULL||head->next==NULL)
        {
            printf("无信息可保存\n");
            return;
        }
        FILE *fp=fopen("/home/lifulin/5","w");
        if(fp==NULL)
        {
            printf("文件没打开\n");
            return;
        }
    
        pLINK temp;
        for(temp=head->next;temp!=NULL;temp=temp->next)
        {
            fwrite(&temp->data,sizeof(Student),1,fp);
        }
        fclose(fp);
        printf("保存成功\n");
    
    
    
        
    /*  int j=0;
        rewind(fp);
        pLINK temp=head;
        for(temp=head;temp->next!=NULL;temp=temp->next)
        {
            j++;
        }
        printf("j=%d\n",j);
        fwrite(head,sizeof(LINK),j,fp);
        fclose(fp);
        printf("文件保存成功\n");*/
    }
    
    pLINK readDataFromFile()
    {
    /*  pLINK head=(pLINK)malloc(sizeof(LINK));
        head->next=NULL;
        FILE *fp=fopen(filename,"r");
        if(fp==NULL)
        {
            printf("open failed\n");
            return NULL;
        }//fread返回你成功读取的数据个数。
        Student stu;
        while(fread(&stu,sizeof(stu),1,fp)==1)
        {
            pLINK p=(pLINK)malloc(sizeof(LINK));
            p->data=stu;
            //尾插,找到最后一个节点
            pLINK temp;
            for(temp=head;temp->next!=NULL;temp=temp->next);
            temp->next=p;
            p->next=NULL;
        }
        fclose(fp);
        return head;
    */
    
    
    
        FILE *fp=fopen("/home/lifulin/5","r");
        if(fp==NULL)
        {
            printf("文件没打开\n");
            return;
        }
        rewind(fp);
        pLINK temp=(pLINK)malloc(sizeof(LINK));
        printf("%d\n",temp->data.num);
        fread(&temp->data,sizeof(Student),1,fp);
        if(temp==NULL)
        {
            printf("没有文件读出\n");
            return;
        } 
        pLINK head=(pLINK)malloc(sizeof(LINK));
        head->next=temp;
        pLINK temp1=temp;
        temp=(pLINK)malloc(sizeof(LINK));
        while(fread(temp,sizeof(Student),1,fp)==1)
        {
        //  printf("%d\n",temp->data.num);
            temp1->next=temp;
            temp1=temp;
            temp=(pLINK)malloc(sizeof(LINK));
        }
        printf("读取成功\n");
        return head;
    }
    
    void blueSort1(pLINK head)
    {
    /*  pLINK i=head;
        for(i=head;
    */
    
        int i=0;
        int length=getListLength(head);
        for(i=0;i<length-1;i++)
        {
            pLINK j;
            for(j=head->next;j->next!=NULL;j=j->next)//这里较原来的冒泡排序多算了几次。
            {
                if(j->data.num>j->next->data.num)
                {
                    Student stu=j->data;
                    j->data=j->next->data;
                    j->next->data=stu;
                }
            }
        }
        printf("排序成功\n");
    }
    
    void blueSort2(pLINK head)
    {
        pLINK p1;
        for(p1=head->next;p1->next!=NULL;p1=p1->next)//不是p1!=NULL,因为p2=p1->next
        {
            pLINK p2;
            for(p2=p1->next;p2!=NULL;p2=p2->next)
            {
                if(p1->data.num>p2->data.num)
                {
                    Student temp=p1->data;
                    p1->data=p2->data;
                    p2->data=temp;
                }
            }
        }
        printf("排序成功\n");
    }
    
    void selectSort(pLINK head)
    {
      pLINK i;
      for(i=head->next;i->next!=NULL;i=i->next)
      {
        pLINK j;
        pLINK min=i;
        for(j=i->next;j!=NULL;j=j->next)
        {
            if(min->data.num>j->data.num)
            {
                min=j;
            }
        }
        if(min!=i)
        {
            Student temp=min->data;
            min->data=i->data;
            i->data=temp;
        }
      }
      printf("排序成功\n");
    }
    
    
    pLINK offListSort(pLINK head)
    {
        pLINK first=(pLINK)malloc(sizeof(LINK));
        first->next=NULL;
        pLINK tail=first;
        while(head->next!=NULL)
        {
            pLINK temp;
            pLINK minf=head;
            pLINK min=head->next;
            for(temp=head->next;temp->next!=NULL;temp=temp->next)
            {
                if(min->data.num>temp->next->data.num)//神来之笔!而且与前面temp=head->next呼应。
                {
                        minf=temp;
                        min=temp->next;
                }
            }
            minf->next=min->next;
    
            
        /*  for(temp=head->next;temp!=NULL;temp=temp->next)//头删
            {
                if(min->data.num>temp->data.num)
                {
                    Student stu=min->data;
                    min->data=temp->data;
                    temp->data=stu;
                }
            }
            head->next=min->next;
    */
    
            /*
            pLINK temp1;
            for(temp1=first;temp1->next!=NULL;temp1=temp1->next);
            temp1->next=min;
            min->next=NULL;*/
    
    
            tail->next=min;
            min->next=NULL;
            tail=tail->next;
        }
        free(head);
        head=NULL;
    
    /*  printf("排序成功\n");
        pLINK temp2;
        for(temp2=first->next;temp2!=NULL;temp2=temp2->next)
        {
            printf("name=%s,num=%d\n",temp2->data.name,temp2->data.num);
        }*/
    
        return first;
    }
    
    
    
    pLINK sort(pLINK head)
    {
        if(head==NULL||head->next==NULL)
        {
            printf("无数据可排序\n");
            return;
        }
        int select;
        while(1)
        {
            printf("===========\n");
            printf("1.冒泡1\n");
            printf("2.冒泡2\n");
            printf("3.选择\n");
            printf("4.脱链\n");
            printf("5.返回上一层\n");
            printf("===========\n");
            scanf("%d",&select);
            getchar();
            switch (select)
            {
                case 1:
                    blueSort1(head);
                    break;
                case 2:
                    blueSort2(head);
                    break;
                case 3:
                    selectSort(head);
                    break;
                case 4:
                    head=offListSort(head);
                    break;
                case 5:
                    return head;
    
                default:
                    break;
            }
        }
        return head;
    }
    
    
    
    int main()
    {
    //创建链表 (创建头节点)
        pLINK head=NULL;
    //  head=(pLINK)malloc(sizeof(LINK));
    //  head->next=NULL;
        int select;
        while(1)
        {
            printf("===========\n\n");
            printf("1.创建链表\n");
        //  printf("2.头插节点\n");
    //      printf("3.尾插节点\n");
    //      printf("4.头删节点\n");
    //      printf("5.尾删节点\n");
    //      printf("6.删除节点\n");
            printf("2.插入节点\n");
            printf("3.删除节点\n");
            printf("4.打印数据\n");
            printf("5.查找学号\n");
            printf("6.修改数据\n");
            printf("7.退出程序\n");
            printf("8.保存文件\n");
            printf("9.读取文件\n");
            printf("10.数据排序\n");
            printf("===========\n\n");
            scanf("%d",&select);
            getchar();
            switch (select)
            {
                case 1:
                    head=createList(head);
                    break;
                case 2:
                    insertData(head);
                    break;
                case 3:
                    deleteData(head);
                    break;
                case 4:
                    printData(head);
                    break;
                case 5:
                    searchData(head);
                    break;
                case 6:
                    changeData(head);
                    break;
                case 7:
                    return 0;
                case 8:
                    saveData(head);
                    break;
                case 9:
                    head=readDataFromFile();
                    break;
                case 10:
                    head=sort(head);
                    break;
            /*  case 5:
                    tailDeleteData(head);
                    break;
                case 6:
                    randomDeleteData(head);
                    break;
                case 7:
                    printData(head);
                    break;*/
                default:
                    printf("输入错误,请重试\n");
                    break;
            }
    
        } 
    //  head=createList(head);
    //  headInsertData(head);
        //1.头插  数据节点插在头节点的后面
    /*  pLINK p=(pLINK)malloc(sizeof(LINK));//主函数里可以不用malloc,这里主要
        p->next=head->next;                 //为之后的函数调用做准备。
        head->next=p;
    
        printf("请输入学生信息[name num]:\n");
        Student stu;
        scanf("%s%d",stu.name,&stu.num);//这里%s遇空格和回车就止,故空格和回车
        p->data=stu;//神来之笔!可以的。       //都可以。
    */
        //2.打印数据
    //  printData(head);
    //  pLINK temp=head;
    //  for(temp=head->next;temp!=NULL;temp=temp->next)
    //  {
    //      printf("name=%s,num=%d\n",temp->data.name,temp->data.num);
        
    //  }
        
    
        return 0;
    }
    
    

    相关文章

      网友评论

          本文标题:单链表

          本文链接:https://www.haomeiwen.com/subject/qwyluttx.html