[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那些事”

欢送欢送关注我的公众号:嵌入式与Linux那些事,支付秋招口试面试大礼包(华为小米等大厂面经,嵌入式知识点总结,口试题目,简历模版等)和2000G学习材料。