c语言二十二:链表

一 链表的基本概念

1.1 链表的概念

链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构

1.2 静态数组,动态数组(malloc),链表的区别

  1. 静态数组int arr[10]:静态数组必须确定大小,不能实现动态申请,释放;
  2. 动态数组使用malloc动态申请的内存空间:不能实现局部申请,释放。不能快捷插入或删除数据,要涉及到大量的数据移动;

1.3 链表的特点

链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两个部分:

  1. 存储数据元素的数据域;
  2. 存储下一个节点地址的指针域

1.4 链表的构成

链表由一个个节点构成,每个节点一般采用结构体的形式组织。

typedef struct student
{
	int num;
	float score;
	struct student *next;
}STU;
链表节点分为两个域:
	数据域:存放各种实际数据,如:num,score
	指针域:存放下一个节点的首地址,如:next

在这里插入图片描述

1.5 链表节点的结构体实现代码

typedef struct stu
{
	//数据域
	int num;
	char name[32];
	float score;
	//指针域
	struct stu *next;  //保存下一个节点的首地址
}STU;

1.6 静态链表

[root@ansible9 ~]# cat test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct stu{ 
	//数据域
	int num;
	char name[32];
	float score;

	//指针域 保存下一个节点的首地址
	struct stu *next;
} STU;
	
int main(int arg, char *argv[])
{
	//生成链表节点并赋数据域的值
	STU data1 = {100, "andy", 61.1};
	STU data2 = {200, "tony", 91.1};
	STU data3 = {300, "lindy", 61.1};
	STU data4 = {400, "emliy", 67.1};
	STU data5 = {500, "test", 100};
	//链表头
	STU *head = &data1;
	data1.next = &data2;
	data2.next = &data3;
	data3.next = &data4;
	data4.next = &data5;
	data5.next = NULL;
	//链表的遍历
	STU *pb = head;
	while(pb != NULL)
	{
		printf("%d %s %f\n", pb->num, pb->name, pb->score);
		pb=pb->next;
	}
}
[root@ansible9 ~]# 
[root@ansible9 ~]# ./a.out 
100 andy 61.099998
200 tony 91.099998
300 lindy 61.099998
400 emliy 67.099998
500 test 100.000000

1.7 带头链表和不带头链表

在这里插入图片描述

  1. 不带头链表是直接用一个指针指向链表的第一个节点;
  2. 带头链表是链表的第一个节点是链表头,他的数据域没有数据,只有指针域指向第二个节点;

二 动态链表的操作

在这里插入图片描述
在这里插入图片描述

2.1 链表插入节点:插入到第一个节点的前面

//定义节点结构体
typedef struct stu{
	int num;
	char name[32];
	int score;
	struct stu *next;
} STU;

STU* insert(STU* head, STU tmp)
{
//1. 为新节点申请内存堆区空间
STU* new_node=(STU*)calloc(1,(int)sizeof(STU));
if(NULL == new_node)
{
	printf("申请内存空间失败");
	return NULL;
}
//2. 将通过形参tmp传来的节点的数据域的值赋给新节点
*new_node=tmp;
new_node->next=NULL;
//3. 将新节点插入到链表
STU* p = head;
//3.1 链表为空的情况
if(p == NULL)
{
	return new_node;
}
//3.2 链表不为空的情况
else
{
	new_node->next=p;
	return new_node;
}
} 

2.2 链表插入节点:插入到链表尾部

//定义节点结构体
typedef struct stu{
	int num;
	char name[32];
	int score;
	struct stu *next;
} STU;

STU* insert(STU* head, STU tmp)
{
//1. 为新节点申请内存堆区空间
STU* new_node=(STU*)calloc(1,(int)sizeof(STU));
if(NULL == new_node)
{
	printf("申请内存空间失败");
	return NULL;
}
//2. 将通过形参tmp传来的节点的数据域的值赋给新节点
*new_node=tmp;
new_node->next=NULL;
//3. 将新节点插入到链表
STU* p = head;
//3.1 链表为空的情况
if(p == NULL)
{
	return new_node;
}
//3.2 链表不为空的情况
else
{
	while(p->next != NULL)
	{
		p=p->next;
	}
	p->next=new_node;
	return head;
}
} 

2.3 链表插入节点:有序插入(以按照节点的score的值从小到大的顺序来插入节点)

在这里插入图片描述

//定义节点结构体
typedef struct stu{
	int num;
	char name[32];
	int score;
	struct stu *next;
} STU;

STU* insert(STU* head, STU tmp)
{
//1. 为新节点申请内存堆区空间
STU* new_node=(STU*)calloc(1,(int)sizeof(STU));
if(NULL == new_node)
{
	printf("申请内存空间失败");
	return NULL;
}
//2. 将通过形参tmp传来的节点的数据域的值赋给新节点
*new_node=tmp;
new_node->next=NULL;
//3. 将新节点插入到链表
STU* p = head;
//3.1 链表为空的情况
if(p == NULL)
{
	return new_node;
}
//3.2 链表不为空的情况
else
{
//3.2.1 寻找插入点
STU* p1=head;//指向插入点之后的节点
STU* p2=head;//指向插入点之前的节点
while(p1->score < new_node->score && p1->next != NULL)  //这个循环是在寻找插入点。退出的条件1(p1->score < new_node->score)为假时对应这插入点为链表首或插入点为链表中间两种可能;退出条件2(p1->next != NULL)为假时对应这插入点为链表尾的情况。
{
	p2 = p1;
	p1 = p1->next;
}
//3.2.2 判断插入点(链表头、链表尾、链表中间)
if(p1->score >= new_node->score)  //a,b链表头插入或链表中部插入
{
	if(p1==head)  //a 链表头部插入
	{
		new_node->next=head;
		head=new_node;
		return head;
	}
	else  //b. 链表中部插入
	{
		p2->next=new_node;
		new_node->next=p1;
		return head;
	}
}
else  //c 链表尾部插入
{
	p1->next=new_node;
	return head;
}
}
} 

2.4 查找节点(以查找节点的name为例)

STU* search(STU* head,char* name)
{
        STU* p = head;
        //1.先判断链表里有无节点
        if(head == NULL)//链表不存在
        {
                printf("链表为空\n");
                return NULL;
        }
        else  //链表不为空
        {
                //遍历节点寻找目标节点
                while(strcmp(p->name,name)!=0 && p->next != NULL)
                {
                        p=p->next;
                }
                if(p->next == NULL && strcmp(p->name,name)!=0) //已经遍历到最后一个节点还没找到
                {
                        printf("没找到name为%s的节点!\n", name);
                        return NULL;
                }
                else //找打目标节点了
                {
                        return p;
                }
        }
}

2.5 删除节点(以查找节点的name为例)

STU* delete(STU* head,char* name)
{
        STU* p = head;
        //先判断链表里有无节点
        if(head == NULL)
        {
                printf("链表为空\n");
                return NULL;
        }
        else  //链表不为空
        {
                //1.寻找删除点
                STU* p1=head;
                STU* p2=head;
                while(strcmp(p1->name,name)!=0 && p1->next != NULL)
                {
                        p2=p1;
                        p1=p1->next;
                }
                //2.1 没找到删除点
                if(strcmp(p1->name,name)!=0 && p1->next == NULL)
                {
                        printf("链表中没有要删除的节点\n");
                        return head;
                }
                //2.2.1 找到了要删除的节点,要删除的节点位置在链表头
                else if(p1 == head)
                {
                        printf("找到了要删除的节点:num %d name %s score %d\n",p1->num,p1->name,p1->score);
                        head=head->next;
                        free(p1);
                        return head;
                }
                //2.2.2 找到了要删除的节点,要删除的节点位置在链表尾
                else if(p1->next == NULL)
                {
                        printf("找到了要删除的节点:num %d name %s score %d\n",p1->num,p1->name,p1->score);
                        free(p1);
                        p2->next=NULL;
                        return head;
                }
                //2.2.3 找到了要删除的节点,要删除的节点位置在链表中间
                else
                {
                        printf("找到了要删除的节点:num %d name %s score %d\n",p1->num,p1->name,p1->score);
                        p2->next = p1->next;
                        free(p1);
                        return head;
                }
        }

}

2.6 释放链表

STU* free_link(STU* head)
{
        STU* p = head;
        while(p != NULL)
        {
                head=p->next;
                free(p);
                p=head;
        }
        return head;
}

2.7 链表的逆序

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

2.8 链表的排序

2.8.1 选择法排序(以数组实现)

将数组3 5 2 4 1用选择法从小到大排序

数组有5个元素,	n=5;
所以排序需要4轮,即第0轮,1轮,2轮,3轮;
所以外层循环为:for(i=0,i<n-1;i++);
每一轮都假设最小值min在第一位

在这里插入图片描述

在这里插入图片描述在这里插入图片描述在这里插入图片描述

2.8.2 选择法排序(以链表实现)

2.9 结构体指针成员的深、浅拷贝

2.9.1 前提

指针变量作为结构体的成员

2.9.2 浅拷贝-两个结构体变量中的指针成员指向同一块堆区空间

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
        int num;
        char* name;
} STU;
int main(int arg, char *argv[])
{
        char *name = (char *)calloc(32,(int)sizeof(char));
        strcpy(name,"i love you");
        STU s1 = {100,name};
        STU s2 = {200,name};
        printf("s1的num %d name %s\n",s1.num,s1.name);
        printf("s2的num %d name %s\n",s2.num,s2.name);
}

2.9.3 深拷贝-两个结构体变量中的指针成员指向各自的堆区空间

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
        int num;
        char* name;
} STU;
int main(int arg, char *argv[])
{
        STU s1;
        STU s2;
        s1.name = (char *)calloc(32,(int)sizeof(char));
        s2.name = (char *)calloc(32,(int)sizeof(char));
        s1.num = 100;
        s2.num = 200;
        strcpy(s1.name,"i love you");
        strcpy(s2.name,"you love me");
        printf("s1的num %d name %s\n",s1.num,s1.name);
        printf("s2的num %d name %s\n",s2.num,s2.name);
}

三 其他链表的概念及应用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值