C语言数据结构:链表的增删改查及动态创建

目录

一,链表与数组

① 定义区别

② 实现区别

二,链表遍历和计算链表中节点数量

① 链表遍历

② 计算节点数量

三,查找链表节点

四,增加节点到链表中

① 在节点后方插入

② 在节点前方插入

● 在头节点前方插入

● 在其他节点前方插入

五,删除链表中指定的节点

① 删除链表头节点

② 删除链表其它节点

六,修改链表中指定的节点

七,动态创建链表

① 头插法创建链表

② 尾插法创建链表


一,链表与数组

① 定义区别

数组:数组是在内存中连续存储的具有相同类型的一组数据的集合

      ● 数组中数的类型必须相同;

      ● 数组中数在内存中必须是连续存储的

链表:把它想象成自行车链条,由n个节点组成,可以对这些节点进行“增,删,改,查”等操作,即对数据的操作。

      ● 可以进行动态存储分配

      ● 可以在节点中定义多种数据类型

      ● 用NULL来表示空节点;

      ● 链表都有一个头指针,一般以head来表示,存放的是一个地址

      ● 链表中包含头结点其它节点两种,头结点是没有数据域的;

      ● 链表中每个节点都分为两个区域,一个数据域,一个是指针域;

② 实现区别

数组:

#include <stdio.h>

int main()
{
        int i;
        int arr[] = {1,2,3,4,5,6,7,8};
        int len = sizeof(arr)/sizeof(arr[0]);

        for(i=0;i<len;i++){
                printf("%d ",arr[i]);
        }
        putchar('\n');
        printf("number=%d\n",len);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 5 6 7 8 
number=8

链表:

#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

int main()
{
        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};

        t1.next = &t2; //t1节点的下一个节点为&t2,因为struct Test *next,所以要取地址
        t2.next = &t3; //t2节点的下一个节点为&t3
        t3.next = &t4; //t3节点的下一个节点为&t4

        printf("%d %d %d %d\n",t1.data,t1.next->data,t1.next->next->data,t1.next->next->next->data);
        //此处以结构体的方式打印,用来引入链表主题
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4

二,链表遍历和计算链表中节点数量

① 链表遍历

struct Test *p = head;              //定义链表头
        while(p != NULL){           //如果链表头不为空
                printf("%d ",p->data);  //就先打印链头的值
                p = p->next;        //一直链接下一个数的值,不断打印,直到节点值为NULL就停止
#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;      //定义链表头
        while(p != NULL){           //如果链表头不为空
                printf("%d ",p->data);  //就先打印链头的值
                p = p->next;        //一直链接下一个数的值,不断打印,直到节点值为NULL就停止
        }
        putchar('\n');
}
int main()
{
        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        printLink(&t1); //此处t1就是链头,要取链表头的地址
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4

② 计算节点数量

int cnt = 0;  //接收链表数量       
        struct Test *p = head;
        while(p != NULL){  //如果链头不为空,那么cnt++直到链尾出现NULL就停止
                cnt++;    
                p = p->next;
        }
        return cnt; //返回值取cnt
#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);   //遍历链表部分
                p = p->next;
        }
        putchar('\n');
}

int numLinkList(struct Test *head)
{
        int cnt = 0;  //接收链表数量       
        struct Test *p = head;
        while(p != NULL){  //如果链头不为空,那么cnt++直到链尾出现NULL就停止
                cnt++;    
                p = p->next;
        }
        return cnt; //返回值取cnt
}
int main()
{
        int ret;
        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        printLink(&t1); //遍历链表

        ret = numLinkList(&t1);  //t1为链头,该函数传参就传t1的地址
        printf("num = %d\n",ret);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
num = 4

三,查找链表节点

int searchLinklist(struct Test *head,int n)//定义n为要查找的节点
{
        struct Test *p = head;
        while(p != NULL){
                if(p->data == n){   //如果头节点就是我们要查找的节点
                        return 1;   //那么返回值为1,表示找到
                }
                p = p->next;  //否者就继续往下一个节点查找,直到找到或者节点为空就停止
        }
        return 0;        //一直没找到就返回0;
}
#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data); //遍历链表部分
                p = p->next;
        }
        putchar('\n');
}

int searchLinklist(struct Test *head,int n)
{
        struct Test *p = head;
        while(p != NULL){
                if(p->data == n){   //如果头节点就是我们要查找的节点
                        return 1;   //那么返回值为1,表示找到
                }
                p = p->next;  //否者就继续往下一个节点查找,直到找到或者节点为空就停止
        }
        return 0;        //一直没找到就返回0;
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        printLink(&t1);//遍历链表
        ret = searchLinklist(&t1,3); //查找3这个节点
        if(ret == 1){    //如果返回值为1
                printf("have 3\n"); //就查找到3这个节点
        }else{
                printf("no 3\n");  //否者就没有3这个节点
        }

        ret = searchLinklist(&t1,7); //查找7这个节点
        if(ret == 1){
                printf("have 7\n");
        }else{
                printf("no 7\n");
        }
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4   //遍历出来的链表节点
have 3    //查找到有3这个节点
no 7      //没有查找到7这个节点

四,增加节点到链表中

① 在节点后方插入

逻辑分析:

● 函数中需要定义新插入的节点new,和在哪个节点后插入n;

● 遍历链表,找到要插入的节点位置;while(p != NULL){p = p->next;}

● 如果链头就是要找的那个节点:p->data == n;

● 新节点的下一个=找的那个节点的下一个:new->next = p->next

● 找的那个节点的下一个节点就是新插入的节点:p->next = new; 

#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)   //遍历链表部分
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

void searchBehindLink(struct Test *head,struct Test *new,int n)
{                                    //new为新节点,n为要在哪个节点后插入节点
        struct Test *p = head;
        while(p != NULL){    //需要遍历步骤,找到要在那个节点后方插入节点      
                if(p->data == n){ //如果链头的值就是要找的那个节点
                        new->next = p->next; //新节点的下一个=找的那个节点的下一个
                        p->next = new; //找的那个节点的下一个就是新插入的节点
                }
                p = p->next;
        }
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test new = {100,NULL}; //定义要插入的节点,插入的数是100

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        printLink(&t1);//遍历
        searchBehindLink(&t1,&new,3);//在节点3后面插入新节点100
        printLink(&t1);//插入新节点后在遍历一次,看是否插入成功
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4         //原先的节点数
1 2 3 100 4     //插入成功,在3节点后方插入100

② 在节点前方插入

● 在头节点前方插入

如果在头结点插入新节点,那么头节点的地址就会发生改变,需要定义指针变量来接收新的头结点地址。

#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head) //遍历链表部分
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{
        struct Test *p = head;
        if(p->data == n){  //链表头的前方插入
                new->next = p; //直接让新节点new->next等于现在的头就行了
                return new; //返回一个new
        }
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test new = {102,NULL}; //插入102
        struct Test *head = NULL;

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        head = &t1;
        printLink(head);//遍历链表
        head = searchFrontLink(head,&new,1);//返回的是new,main函数中的头发生改变,重新用head来接收
        printLink(head);//添加完成,再次遍历链表,验证是否插入成功
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4      //遍历链表
102 1 2 3 4  //在头节点前方插入新节点102,插入成功

● 在其他节点前方插入

struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{
        struct Test *p = head;
        while(p->next != NULL){ //此时没有在链头的前方插入,所以要定义链头的下一个不为NULL
                if(p->next->data == n){   //链头的下一个节点就是我们要找的节点
                        new->next = p->next; //插入进来的新节点就是头节点的下一个
                        p->next = new; //头节点的下一个就是新节点
                        return p; //此时要返回到头
                } 
                p = p->next;

        }
        return p; //返回头节点
}
#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

struct Test *searchFrontLink(struct Test *head,struct Test *new,int n)
{
        struct Test *p = head;
        while(p->next != NULL){ //此时没有在链头的前方插入,所以要定义链头的下一个不为NULL
                if(p->next->data == n){   //链头的下一个节点就是我们要找的节点
                        new->next = p->next; //插入进来的新节点就是头节点的下一个
                        p->next = new; //头节点的下一个就是新节点
                        return p; //此时要返回到头
                } 
                p = p->next;

        }
        return p; //返回头节点
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test new = {102,NULL}; //插入新节点102
        struct Test *head = NULL;

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        head = &t1;
        printLink(head);
        searchFrontLink(head,&new,3); //插在节点3的前面
        printLink(head);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 2 102 3 4  //在节点3的前面插入新节点102

五,删除链表中指定的节点

删除节点后,内存中会存在残留的缓存数据,需要使用free()来释放地址空间,而free()一般是需要承接malloc()动态开辟了的地址空间。

① 删除链表头节点

逻辑分析:

● 删除的是头节点,所以不需要遍历;

● 确定头节点就是我们要删除的节点:p->data == n;

● 那么新的头节点就是原来头节点的下一个节点:head = head->next;

● 释放删除节点的内存空间:free();

● 删除链头,链头地址发生改变,需要返回新的链头

#include <stdio.h>
#include <stdlib.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

struct Test *deletLink(struct Test *head,int n)
{
        struct Test *p = head;
        if(p->data == n){  //如果头节点的值就是我们要删除的节点
                head = head->next; //那么新的头节点就是原来头节点的下一个节点
                free(p); //删除了节点就要释放删除节点的内存
                return head;//链头被删除,头发生了改变,就要返回新链头
        }
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test *head = NULL;
        struct Test *p = (struct Test *)malloc(sizeof(struct Test));//动态开辟链头地址空间
//      t1.next = &t2;
        p->data = 1;//头节点里的值为1
        p->next = &t2;//头节点的下一个节点为t2
        t2.next = &t3;
        t3.next = &t4;

//      head = &t1;
        head = p;
        printLink(head);
        head = deletLink(head,1);//头发生该表,需要新的链头来承接链表头
        printLink(head);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
2 3 4     //头结点1被删除

② 删除链表其它节点

逻辑分析:

● 遍历链表,遍历出要删除的其它节点;

● 因为不是删链头,所以循环遍历的条件为:p->next != NULL;

● 找到要删除的节点:p->next->data == n;

● 返回值为链头

#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

struct Test *deletLink(struct Test *head,int n)
{
        struct Test *p = head;
        while(p->next != NULL){  //不是删除头节点,所以定义头节点的下一个节点不为NULL
                if(p->next->data == n){  //如果头节点的下一个节点就是我们要删除的节点
                        p->next = p->next->next; //那么头节点的下一个就是头节点下一个的下一个
                        return head;
                }
                p = p->next;
        }
}
int main()
{
        int ret;

        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test *head = NULL;

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        head = &t1;
        printLink(head);
        head = deletLink(head,2);
        printLink(head);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 3 4    //节点2被删除

六,修改链表中指定的节点

int changeLink(struct Test *head,struct Test *new,int n)
{
        struct Test *p = head;
        while(p != NULL){   //遍历
                if(p->data == n){ //如果头结点的data就是我们=要改变的节点n
                        p->data = new->data;//那么找到的节点数据就等于要改的数据
                        return 1;//返回1,修改成功
                }
                p = p->next;//没有找到那个节点,就一直遍历到NULL节点停止
        }
}
#include <stdio.h>

struct Test{
        int data;
        struct Test *next;
};

void printLink(struct Test *head)
{
        struct Test *p = head;
        while(p != NULL){
                printf("%d ",p->data);
                p = p->next;
        }
        putchar('\n');
}

int changeLink(struct Test *head,struct Test *new,int n)
{
        struct Test *p = head;
        while(p != NULL){   //遍历
                if(p->data == n){ //如果头结点的data就是我们=要改变的节点n
                        p->data = new->data;//那么找到的节点数据就等于要改的数据
                        return 1;//返回1,修改成功
                }
                p = p->next;//没有找到那个节点,就一直遍历到NULL节点停止
        }
}
int main()
{
        struct Test t1 = {1,NULL};
        struct Test t2 = {2,NULL};
        struct Test t3 = {3,NULL};
        struct Test t4 = {4,NULL};
        struct Test *head = NULL;
        struct Test new = {9,NULL};//要改成的节点为9

        t1.next = &t2;
        t2.next = &t3;
        t3.next = &t4;

        head = &t1;
        printLink(head);
        changeLink(head,&new,3);//修改节点3为节点9
        printLink(head);
        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
1 2 3 4 
1 2 9 4  //将节点3改为节点9,如果修改头节点也没有问题 

七,动态创建链表

① 头插法创建链表

每一次创建的新节点插入在上一个头节点之前,再让新创建的节点为链头

#include<stdio.h>
#include<stdlib.h>

struct Test//声明结构体类型
{
        int data;//定义数据域
        struct Test *next;//定义指针域
};

void printLink(struct Test *head)//遍历链表
{
        struct Test *p = head;

        while(p != NULL){//如果链头不为空,
                printf("%d ",p->data);//输出p节点中data的值
                p = p->next;//p指向下一节点,会不断指向链表尾NULL时停止
        }
        putchar('\n');//换行
}

struct Test* insertFromHead(struct Test *head,struct Test* new)
{

        if(head == NULL){//判断头节点是否为空
                head = new;//如果为空直接把创建的新节点当作链表头
        }else{
                new->next = head;//当链表不为空时,新节点的下一个节点为原先的头节点,此时,新节点为头
                head = new;//新节点成为链表头
        }
        return head;//头节点被改变,返回新的头节点
}

struct Test* creatLink(struct Test* head,struct Test *new)//头插法动态创建链表
{
        while(1){
                new = (struct Test*)malloc(sizeof(struct Test));//不断为新节点开辟空间
                printf("input your ne node data:\n");
                scanf("%d",&(new->data));//输入新节点
                if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建并退出
                        printf("quit!\n");
                        free(new);//不断为新节点开辟内存空间,就要不断的释放空间
                        return head;//不断的头插,不断的返回新头
                }
                head = insertFromHead(head,new);//调用上面的判断函数
        }
        return head;//头被改变,返回新头
}

int main()
{
        struct Test *head = NULL;
        struct Test *new = NULL;
        head = creatLink(head,new);//head来承接不断被改变的新头
        printLink(head);//遍历链表

        return 0;
}
dhw@dhw-virtual-machine:~$ ./a.out
input your ne node data:
1
input your ne node data:
2
input your ne node data:
3
input your ne node data:
4
input your ne node data:
5
input your ne node data:
6
input your ne node data:
0     //输入0就退出程序
quit!
6 5 4 3 2 1  //从头插入,每一个新插入的节点都为链表头

② 尾插法创建链表

如果头节点为空,那么创建的第一个节点就当做链表头,后面每次创建的新节点都依次插在链表最后一个节点的后面

#include<stdio.h>
#include<stdlib.h>

struct Test
{
        int data;//定义数据域
        struct Test *next;//定义指针域
};

void printLink(struct Test *head)//遍历链表
{
        struct Test *p = head;
        while(p != NULL){//p现在是链表头,
                printf("%d ",p->data);//输出p节点中data的值
                p = p->next;//链头不断指向下一节点,直到链表尾NULL时停止
        }
        printf("\n");
}

struct Test* insertBehind(struct Test *head,struct Test *new)
{
        struct Test *p = head;

        if(p == NULL){//判断链表是否为空
                return head = new;//如果为空把创建的新节点当作链表头
        }
        while(p->next != NULL){//遍历到最后一个节点
                p = p->next;//使p指向下一节点
        }
        p->next = new;//把新节点插入最后一个节点的指针域(next)中
        return head;//把链表头传回去
}

struct Test* creatLink(struct Test *head)//尾插法动态创建链表
{
        struct Test *new;
        while(1){
                new = (struct Test *)malloc(sizeof(struct Test));//开辟空间
                new->next = NULL;
                printf("input your ne node data:\n");
                scanf("%d",&(new->data));//输入新节点
                if(new->data == 0){//判断每次输入的值是否为0,如果为0,停止创建并退出
                        printf("quit!\n");
                        return head;//返回到头
                }
                head = insertBehind(head,new);
        }
}

int main()
{
        struct Test *head = NULL;
        head = creatLink(head);
        printLink(head);

}
dhw@dhw-virtual-machine:~$ ./a.out
input your ne node data:
1
input your ne node data:
2
input your ne node data:
3
input your ne node data:
4
input your ne node data:
5
input your ne node data:
6
input your ne node data:
0      //输入0就退出程序
quit!
1 2 3 4 5 6  //按创建节点的顺序,新节点依次往后排列
  • 29
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
好的,我可以为您介绍一下 C 语言中动态链表增删改查操作。 首先,我们需要定义一个链表结构体: ```c typedef struct Node { int data; struct Node *next; } Node, *LinkedList; ``` 其中,`data` 表示链表节点中存储的数据,`next` 表示指向下一个节点的指针。 接下来,我们可以实现链表创建操作: ```c LinkedList createList() { LinkedList head = (LinkedList)malloc(sizeof(Node)); head->next = NULL; return head; } ``` 这个函数会返回一个链表头节点的指针,也就是一个空链表。 接下来,我们可以实现链表节点的插入操作: ```c void insertNode(LinkedList L, int data) { Node *newNode = (Node *)malloc(sizeof(Node)); newNode->data = data; newNode->next = L->next; L->next = newNode; } ``` 这个函数会在链表头节点后插入一个新的节点。 接下来,我们可以实现链表节点的删除操作: ```c void deleteNode(LinkedList L, int data) { Node *p = L; while (p->next != NULL && p->next->data != data) { p = p->next; } if (p->next == NULL) { printf("Not found!\n"); return; } Node *tmp = p->next; p->next = tmp->next; free(tmp); } ``` 这个函数会在链表中查找指定数据的节点,并删除它。 最后,我们可以实现链表节点的查找操作: ```c Node *findNode(LinkedList L, int data) { Node *p = L->next; while (p != NULL && p->data != data) { p = p->next; } return p; } ``` 这个函数会在链表中查找指定数据的节点,并返回它的指针。 以上就是 C 语言中动态链表增删改查操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

D.•

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值