美文网首页
史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)

史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)

作者: 3561cc5dc1b0 | 来源:发表于2020-12-16 09:31 被阅读0次

    [TOC]

    1.准备工作

    首先包含头文件,定义链表结构体,产生随即链表的范围,定义全局头尾节点。

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX 10
    /*定义链表*/
    typedef struct Node 
    {
        int data;
        struct Node *next;  
    }Node;
    /*定义全局头尾节点*/
    Node *head = NULL;
    Node *end = NULL;
    

    2.创建链表

    /*根据传入的参数添加链表节点*/
    int CreatList(int a)
    {
        /*定义临时结构体并分配空间*/
        Node *temp = (Node *)malloc(sizeof(Node));
        if (temp ==NULL)
        {
            printf("malloc error!");
            return -1;
        }
        else
        {
            /*给数据类型赋值*/
            temp->data = a;
            temp->next = NULL;
            /*如果链表长度为0*/
            if (head == NULL)
            {
                head = temp; 
                end = temp;    
            }
            else
            {
                end->next = temp;
                end = temp;
            }
        }  
    }
    

    3.打印链表

    /*打印链表*/
    void PrintList(Node *temp)
    {
        if(temp == NULL)
        {
            printf("Empty List!\r\n");
        }
        while (temp)
        {
           printf("%d",temp->data);
           temp = temp->next;
           if(temp)
           printf("->");
        }
        printf("\r\n");
    }
    

    4.在元素后面插入元素

    向链表中增添元素,根据添加位置不同,可分为以下 3 种情况:
    1.插入到链表的头部(头节点之后),作为首元节点;
    2.插入到链表中间的某个位置;
    3.插入到链表的最末端,作为链表中最后一个数据元素;

    虽然新元素的插入位置不固定,但是链表插入元素的思想是固定的,只需做以下两步操作,即可将新元素插入到指定的位置:
    a.将新结点的 next 指针指向插入位置后的结点;
    b.将插入位置前结点的 next 指针指向插入结点;

    例如,我们在链表 {1,2,3,4} 的基础上分别实现在头部、中间部位、尾部插入新元素 5,其实现过程如图 所示:

    在这里插入图片描述
    /*根据传入的数,在其后面增加元素*/
    int InsertListEnd(int index,int a)
    {
        if (head == NULL)
        {
            printf("Empty List!\r\n");
            return 0;
        }
        if (FindList(index)->next == FindList(a))
            return 0;
        else
        {
          /*找到传入值的位置并保存*/
            Node *temp = FindList(index);
            /*分配空间存放新的传入的值*/
            Node *pt = (Node *)malloc(sizeof(Node));
            pt->data = a;
            /*是否是最后一个元素*/
            if (temp == end)
            {
                //尾巴的下一个指向新插入的节点
                end->next = temp;
                //新的尾巴
                end = temp;
            }
            else
            {
                // 先连后面 (先将要插入的节点指针指向原来找到节点的下一个)
                pt->next = temp->next;
                //后连前面
                temp->next = pt;
                printf("The list after insert %d is \r\n",a);
                PrintList(head);
            }
        }
    
    }
    

    5.在元素前面增加元素

    /*根据传入的数,在其前面增加元素*/
    int InsertListHead(int index,int a)
    {
        if (head == NULL)
        {
            printf("Empty List!\r\n");
            return 0;
        }
        /*要插入的位置就在原位*/
        if (FindList(index)->next == FindList(a))
            return 0;
        else
        {
           /*找到传入值的位置并保存*/
            Node *temp = FindList(index);
            /*分配空间存放新的传入的值*/
            Node *pt = (Node *)malloc(sizeof(Node));
            pt->data = a;
            /*是否是第一个元素*/
            if (temp == head)
            {
                //尾巴的下一个指向新插入的节点
                pt->next = head;
                //新的头
                head = pt;
            }
            else
            {
                /*寻找到要插入位置的前驱节点*/
                Node *pre = FindPreNode(temp);
                pre->next = pt;
                pt->next = temp;
                printf("The list after insert %d is \r\n",a);
                PrintList(head);
            }
        }
    
    }
    

    6.删除链表元素,要注意删除链表尾还是链表头

    从链表中删除指定数据元素时,实则就是将存有该数据元素的节点从链表中摘除,但作为一名合格的程序员,要对存储空间负责,对不再利用的存储空间要及时释放。因此,从链表中删除数据元素需要进行以下 2 步操作:

    1.将结点从链表中摘下来;
    2.手动释放掉结点,回收被结点占用的存储空间;

    其中,从链表上摘除某节点的实现非常简单,只需找到该节点的直接前驱节点 temp,执行一行程序:

    temp->next=temp->next->next;
    

    例如,从存有 {1,2,3,4} 的链表中删除元素 3,则此代码的执行效果如图 2 所示:


    在这里插入图片描述
    /*删除链表头*/
    void DeleteListHead()
    { //记住旧头
        Node *temp = head;
        //链表检测
        if (NULL == head)
        {
            printf("Empty list!\n");
            return;
        }
    
        head = head->next; //头的第二个节点变成新的头
        free(temp);
    }
    /*尾删除————删*/
    void DeleteListTail()
    {
        if (NULL == end)
        {
            printf("链表为空,无需删除\n");
            return;
        }
        //链表不为空
        //链表有一个节点
        if (head == end)
        {
            free(head);
            head = NULL;
            end = NULL;
        }
        else
        {
            //找到尾巴前一个节点
            Node *temp = head;
            while (temp->next != end)
            {
                temp = temp->next;
            }
            //找到了,删尾巴
            //释放尾巴
            free(end);
            //尾巴迁移
            end = temp;
            //尾巴指针为NULL
            end->next = NULL;
        }
    }
    /*删除链表任意元素*/
    void DeleteList(int a)
    {
       //链表判断 是不是没有东西
        if (NULL == head)
        {
            printf("Empty list!\n");
            return;
        }
        //链表有东西,找这个节点
         Node *temp = FindList(a);
        if (NULL == temp)
        {
            printf("%d not find\r\n",a);
            return;
        }
        //找到了,且只有一个节点
        if (head == end)
        {
            free(head);
            head = NULL;
            end = NULL;
            printf("The list after delete %d is empty!\r\n",a);
           
        }
        else if (head->next == end) //有两个节点
        {
            //看是删除头还是删除尾
            if (end == temp)
            {
                DeleteListTail();
                printf("The list after delete %d is \r\n",a);
                PrintList(head);
            }
            else if (temp == head)
            {
                DeleteListHead();
                printf("The list after delete %d is \r\n",a);
                PrintList(head);
            }
        }
        else //多个节点
        {
            //看是删除头还是删除尾
            if (end == temp)
                DeleteListTail();
            else if (temp == head)
                DeleteListHead();
            else //删除中间某个节点
            {   //找要删除temp前一个,遍历
                Node *pt = head;
                while (pt->next != temp)
                {
                    pt = pt->next;
                }
                //找到了
                //让前一个直接连接后一个 跳过指定的即可
                pt->next = temp->next;
                free(temp);
                printf("The list after delete %d is \r\n",a);
                PrintList(head);
            }
        }
        
    }
    

    7.根据传入的数值查询链表

    /*根据传入的数值,查询链表*/
    Node *FindList(int a)
    {
        Node *temp = head;
        if(head == NULL)
        {
            printf("Empty List!\r\n");
            return NULL;
        }
      
        else
        {
           while (temp)
           {
               if (temp->data == a)
               {
                    printf("%d find!\r\n",a);
                    return temp;
               }
               temp = temp->next;
           }
                printf("%d not find!\r\n",a);
                return 0;
        }
        
    }
    

    8.修改链表元素

    /*修改链表元素,element为要修改的元素,modify为修改后的值*/
    void ModifyList(Node *phead,int element,int modify)
    {
        Node *temp = phead;
        while((temp!= NULL))
        {
            
            if(temp->data == element)
            {
                temp->data  = modify;
                
            }   
            temp = temp->next;
        }
    }
    

    9.求链表长度

    /*求链表长度并返回*/
    int LengthList(Node *temp)
    {
        int length = 0;
        while (temp)
        {
            length++;
            temp = temp->next;
        }
        return length;
        
    }
    

    10.前驱,后继节点的查找

    Node *FindPreNode(Node *p)
    {
        Node *temp = head;
        /*寻找p的前驱节点*/
        if(p == head)
        {
            printf("%d is head node\r\n",p->data);
            return NULL;
        }
        else
        {
            while((temp->next != p) && (temp !=NULL))
            {
                
                temp = temp->next;
    
            }
            return temp;        
        }
    
    }
    Node *FindNextNode(Node *p)
    {
        Node *temp = head;
        /*寻找p的后继节点*/
        while(temp &&(temp != p))
        {
            temp = temp->next;
    
        }
        /*先不判断是否为尾节点,尾节点NULL也可以赋值*/
        temp = temp->next;
        return temp;
         
    }
    
    

    11.倒置链表

    /*方法一:倒置链表*/
    Node *InvertList(Node *phead)
    {
            if(phead == NULL || phead->next == NULL)
            {
                    return phead;
            }
            else
            {
                    Node *p = phead;
                    Node *q = NULL;
                    Node *r = NULL;
                    while(p != NULL)
                    {
                            /*保存下一个节点*/
                            q = p->next;
                            /*让该节点指向上一个节点*/
                            p->next = r;
                            /*上一个节点走到当前节点*/
                            r = p;
                            /*当前节点走到下一个节点*/
                            p = q;
                    }
                    head = r;
                    return head;
            }
    }
    /*方法二:倒置链表*/
     Node *ReverseList(Node *phead)
        {
            /*创建一个新链*/
            /*两个指针,一个指向新的链表,一个指向单个断开的节点元素。连接起来*/
            Node *ptmp = NULL;
            Node *tmp = NULL;
            /*处理链表为空*/
            if(NULL == phead)
            {
                    printf("link is empty\n");
                    return NULL;
            }else
            {
                    /*将旧链上的结点链到新链上*/
                    while(phead != NULL)
                    {
                            tmp = phead;
                            phead = phead->next;
                            /*连接到上一次存下来的连表上。第一次时,ptmp为空,整个链表赋值给tmp后只剩下第一个元素*/
                            tmp->next = ptmp;
                            /*新的链表赋值给ptmp*/
                            ptmp = tmp;
                    }
            }
            head = ptmp;
            return ptmp;
    }
    

    12.判断链表是否有环

    /*判断链表有环*/
    int Is_Circular(Node *phead)
    {
            if(phead == NULL || phead->next == NULL){
                    return 0;       
            }
            /*快慢指针,当二者相等时,一定有环*/
            Node *p1 = phead;
            Node *p2 = phead;
            while(p1 != NULL && p2 != NULL){
                    p2 = p2->next; 
                    if(p1 == p2)
                            return 1;
                    p2 = p2->next;
                    p1 = p1->next;
            }
            return 0;
    }
    

    测试函数

    int main ()
    {
        int i = 0;  
        /*设置获得随机数的种子(固定代码,没有这句,随机数是固定不变的)测试可以不加*/
        srand((int)time(0)); 
        for (i =5;i>0;i--)
        CreatList(rand()%MAX);
        // CreatList(i);
        printf("新创建的的链表为:");
        PrintList(head);
        InsertListHead(4,10);
        printf("在4前插入10后的链表为:");
        PrintList(head);
        InsertListEnd(4,10);
        printf("在4后插入10后的链表为:");
        PrintList(head);
        DeleteList(0);
        printf("删除0后的链表为:");
        PrintList(head);
        Node *p = FindList(7);
        Node *q = FindList(4);
        ModifyList(head,1,15);
        printf("修改1为15后的链表为:");
        PrintList(head);
        ReverseList(head);
        printf("反转后的链表为:");
        PrintList(head);
        printf("链表长度为:%d",LengthList(head));
        return 0;
    }
    

    测试截图

    在这里插入图片描述
    关于排序算法的讲解将在下节[单链表的5种排序算法]介绍。

    以上代码均为测试后的代码。如有错误和不妥的地方,欢迎指出。

    如遇到排版错乱的问题,可以通过以下链接访问我的CSDN。

    CSDN:CSDN搜索“嵌入式与Linux那些事

    我的GZH:嵌入式与Linux那些事,领取秋招笔试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,笔试题目,简历模版等)和2000G学习资料。

    相关文章

      网友评论

          本文标题:史上最全单链表的增删改查反转等操作汇总以及5种排序算法(C语言)

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