一、链表
1.1 数据结构
数据结构是指相互之间存在联系的元素的集合方式,主要有两种:顺序存储结构和链式存储结构。
①顺序存储结构:元素在内存中按照顺序连续存放的存储方式,例:数组、结构体
②链式存储结构:元素在内存中不连续存放的存储方式,上一个元素存储另一个元素的地址,各个元素通过地址相互联系在一起。例:链表
1.2 链表
(1)概述:链表是由一种特殊的结构体组成的,其中包含的结构体个数有若干个,在内存中是不连续存放的,能够更好的利用内存空间。
(2)组成:链表由表头、若干个节点和表尾组成,每个节点内部空间又分为数据域和指针域。
①表头:只有指针域,没有数据域
②节点:数据域存放数据信息,指针域通常存放下一个节点的地址,也可以存放上一个节点地址。
③表尾:有数据域,指针域指向为空(NULL)
二、内部动态分配
2.1 malloc函数
函数原型:void *malloc(size_t size);
函数作用:向存储器申请一块内存空间(手动开辟,从堆区开辟)
函数形参:申请的空间大小,单位:字节。
函数返回值: 成功:返回开辟好的空间首地址。
失败:返回NULL
2.2 calloc函数
函数原型 :void *calloc(size_t nmemb, size_t size);
函数作用 :向存储器申请多块内存空间
函数形参 :申请几块空间,每块多大,单位:字节
函数返回值:成功:返回开辟的第一块空间的首地址(申请的多块空间是连续的);
失败:返回NULL
例:p = calloc (3,4) 向存储器申请3块,每块大小为4字节的空间,然后把第一块的空间地址赋值到指针p
2.3 realloc函数
函数原型 :void *realloc(void *ptr, size_t size);
函数作用 :修改已申请的存储空间大小
函数形参 :要修改的空间首地址,改成多大
函数返回值:修改成功,返回新空间的地址
失败,返回NULL
例:q = realloc (p,30) 向存储器申请修改指定空间的大小为30个字节,然后把新空间地址赋值给指针q
注:如果原空间有数据,新空间比原空间小时会发生数据丢失。
2.4 free函数
函数原型 :void free(void *ptr);
函数作用 :释放向存储器申请的空间
函数形参 :要释放的空间首地址
函数返回值:无。
注:free函数只能释放最近分配的空间以及内存动态分配的空间,函数是从stdlib.h头文件中调用。在程序中,使用malloc函数和calloc函数申请的空间,在程序运行的过程中,程序不会主动释放空间,所在每次存储空间使用完毕后,一定要手动释放(使用free函数释放)。
#include <stdio.h>
#include <stdlib.h>
struct student
{
char name[20];
int id;
float score;
struct student *next;
};
int main()
{
struct student *head =NULL;
head = (struct student *)malloc(sizeof(struct student));
scanf("%s%d%f",head->name,&head->id,&head->score);
printf("%s,%d,%.2f\n",head->name,head->id,head->score);
free(head); //释放 head
head =NULL;
printf("%s,%d,%.2f\n",head->name,head->id,head->score);
}
三、链表应用
#include <stdio.h>
#include <stdlib.h>
struct student
{
char name[20]; //
int id; // 数据域
float score; //
struct student *next; //指针域
};
/*******创建节点******/
struct student *Creat_Jiedian()
{
struct student *p =NULL;
p = (struct student *)malloc(sizeof(struct student)); //必须动态开辟节点空间
scanf("%s%d%f",p->name,&p->id,&p->score);
p->next = NULL; //节点指针域指向空
return p;
}
/**********创建链表**********
函数参数:
num:链表节点数
返回值:创建完成后的链表表头
****************************/
struct student *Creat_Lianbiao(int num)
{
int i;
struct student *head =NULL;
struct student *p1 =NULL,*p2 =NULL;
for(i=0;i<num;i++)
{
p1 = Creat_Jiedian();
if(i==0)
{
head = p1;
}
else
{
p2->next = p1;
}
p2 = p1;
}
p1->next = NULL; //表尾指针域指向NULL
return head;
}
/*********打印函数********
函数参数:
head:打印链表的表头
*************************/
void Printf(struct student *head)
{
while(head != NULL)
{
printf("%s %d %.2f\n",head->name,head->id,head->score);
head = head->next;
}
}
/*********查询函数**********
函数参数:
head:查询链表表头
id:查询节点id
返回值:查询节点的首地址
***************************/
struct student *Find_Jiedian(struct student *head,int id)
{
if (head == NULL)
{
return NULL; //链表为空
}
while(head != NULL)
{
if(head -> id == id)
{
return head;
}
head = head ->next;
}
return NULL;
}
/**********删除节点函数**********
函数参数:
head:链表表头
id:删除节点id
返回值:删除节点后的链表表头
********************************/
struct student *Delete_Jiedian(struct student *head,int id)
{
struct student *temp =head, *p = NULL;
if (temp == NULL)
{
return NULL;
}
while(temp->id != id && temp->next != NULL)
{
p = temp;
temp = temp->next;
}
if(head == temp) //删除节点为表头
{
head = temp->next;
free (temp);
temp = NULL;
}
else if(temp->next == NULL)
{
if(temp->id == id)
{
p->next = NULL;
free (temp);
temp = NULL;
}
else
{
printf("查无此人\n");
}
}
else
{
p->next = temp->next;
free (temp);
temp = NULL;
}
return head;
}
/******************************
函数功能:插入函数
函数参数:
head:链表表头
new_jiedian:要插入的节点
函数返回值:插入后的表头
******************************/
struct student * Add_Jiedian(struct student *head,struct student *new_jiedian)
{
struct student *temp = head;
struct student *p = NULL;
if(head == NULL)
{
head = new_jiedian;
new_jiedian->next = NULL;
return head;
}
while(((temp->id) < (new_jiedian->id)) && (temp->next != NULL))
{
p = temp;
temp = p->next;
}
if(temp->id > new_jiedian->id)
{
if(temp == head)
{
head = new_jiedian;
new_jiedian->next = temp;
}
else
{
p->next = new_jiedian;
new_jiedian->next = temp;
}
}
else if(temp->next == NULL)
{
temp->next = new_jiedian;
new_jiedian->next = NULL;
}
return head;
}
/*********链表排序**********
函数参数:
head:需要排序链表的表头
返回值:排序完成后的链表表头
***************************/
struct student *Paixu(struct student *head)
{
struct student *new_head = NULL;
struct student *new_tail = NULL;
struct student *p = NULL;
struct student *min = NULL;
struct student *p_min = NULL;
if(head == NULL)
{
return NULL;
}
while(head != NULL)
{
min = head;
for(p = min ; p->next != NULL ; p = p->next)
{
if((min->id) > ((p->next)->id))
{
p_min = p;
min = p_min->next;
}
}
//把最小节点从原链表剥离
if(head == min)
{
head = min->next;
}
else
{
p_min->next = min->next;
}
//放到新链表中
if(new_head == NULL)
{
new_head = min;
new_tail = min;
}
else
{
new_tail->next = min;
new_tail = min;
}
if(new_head != NULL)
{
min->next = NULL; //表尾指向NULL
}
}
return new_head;
}
int main()
{
int a,b;
struct student *head = NULL,*p = NULL;
while(1)
{
printf("*************************\n");
printf("****** 0:创建链表 ******\n");
printf("****** 1:删除节点 ******\n");
printf("****** 2:插入节点 ******\n");
printf("****** 3:查询节点 ******\n");
printf("*************************\n");
printf("输入数字0-3:");
scanf("%d",&b);
switch(b)
{
case 0: printf("请输入节点个数:");
scanf("%d",&a);
head = Creat_Lianbiao(a);
head = Paixu(head);
Printf(head);
break;
case 1: printf("请输入删除id:");
scanf("%d",&a);
head = Delete_Jiedian(head,a);
Printf(head);
break;
case 2: printf("请输入插入节点信息:");
struct student *new_jiedian =NULL;
new_jiedian = Creat_Jiedian();
head = Add_Jiedian(head,new_jiedian);
Printf(head);
break;
case 3: printf("请输入查询id:");
scanf("%d",&a);
p = Find_Jiedian(head,a);
printf("%s %d %.2f\n",p->name,p->id,p->score);
break;
}
}
}