链表的概念:
链表是一个动态的数据结构,他和数组的区别在于:链表不需要确定数据结构的大小,他是需要存放数据再申请空间,而数组是先申请足够大的空间,再存放数据
但是链表中的所有模块的地址是不连续的(数组是连续的)。
往链表中存放新的节点的步骤:
① 先要确定有头节点,没有则创建。有了头节点后,才能操作整个链表
② 如果这张链表的数据存放有防重复机制的话,先输入防重复数据的值,确定不重复之后,再使用malloc创建新的节点newNode。
③ 完善malloc出来的模块中的数据域的数据
④ 将newNode链接到链表中(具体是哪一张链表取决于传入的头节点)
如何链接到链表中:两种方式
1: 头插法 将新的的节点添加到头节点的后面
2:尾插法 将新的节点添加到尾节点的后面
数据结构:是一门计算机专业的学科,
其主要研究对象为:如何管理批量同类型元素
主要从两个方面深入研究:
- 元素间的逻辑关系:
a. 同属一个集合 0 ~ 0
b. 元素间是线性关系(线性表) 1 对 1
c. 元素间是树形关系 1 对 n
d. 元素间是图形关系 m 对 n - 存储方式
- 顺序存储
批量同类型元素在一块连续的内存空间顺序摆放 - 链式存储
批量同类型元素不一块连续的内存空间中,
但每个元素都保存着另一个元素在内存空间的首地址
- 顺序存储
线性表:
-
顺序存储的线性表 ---- 顺序表(数组)
-
链式存储的线性表 ----- 链表
链表中的元素 ---- 节点 — Node
节点类型一定是结构体,该结构体中至少有一个成员用来保存其它节点的地址
这些用来保存其它节点地址的成员称为指针域(地址域)
其它成员统称为数据域,用来描述具体的事物
分类:
-
按指针域成员个数分为:
a. 单向链表
b. 双向链表 -
按首尾是否相接分为:
a. 循环链表
b. 非循环链表 -
按是否有头节点分为:
a. 有头节点链表
b. 无头节点链表
单向链表的使用案例
按照学生姓名查找学生,添加学生信息,修改学生的信息,删除学生,按成绩排序,加载,保存,
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//数据域
typedef struct Data{
char name[20];
int age;
int score;
}data_t;
//单向链表域
typedef struct Stu{
data_t data;
struct Stu* next;
}stu_t;
//创建一个头节点
stu_t* init(){
stu_t* head = calloc(1,sizeof(stu_t));
head->next = 0;
return head;
}
//按学生姓名查找
stu_t* findStu(stu_t* head){
char name[20] = {0};
printf("请输入要查找的学生姓名:");
scanf("%s",&name);
while(getchar()!=10);
stu_t* p = head->next;//第二个节点
while(p != NULL){
if(strcmp(name,p->data.name)==0){//注意头节点没有比较到
return p;//找到就返回这个节点的地址
}
p = p->next;
}
return NULL;//没找到就返回NULL
}
//修改分数
void updateStu(stu_t* head){
int score = 0;
stu_t* tar = findStu(head);//调用了函数帮忙查找(注意头没有找,因为没放数据)
printf("请输入新的分数:");
scanf("%d",&score);
while(getchar()!=10);
if(tar==NULL){return ;}
tar->data.score = score;
}
//用冒泡排序来排序学生成绩
void sortStu(stu_t* head){
stu_t* p = head->next;//第二个节点
stu_t* q = p;//第二个节点
data_t temp = {0};//数据域,作为临时交换数据
if(q == NULL){return;}
while(p->next != NULL){//i<len-1
q = head->next;//初始化
while(q->next != NULL){//j<len-1
if(q->data.score < q->next->data.score){
temp = q->data;
q->data = q->next->data;
q->next->data = temp;
}
q=q->next;
}
p=p->next;
}
}
//删除学生
void removeStu(stu_t* head){
char name[20] = {0};
printf("请输入要删除的学生姓名:");
scanf("%s",&name);
while(getchar()!=10);
stu_t* p = head;
stu_t* temp = 0;//用来指向要删除的学生
while(p->next != NULL){
if(strcmp(p->next->data.name,name)==0){
temp = p->next;
p->next = p->next->next;
free(temp);//释放节点就是删除的操作
}
p=p->next;
if(p==NULL){break;}
}
}
//释放整个链表
void freeStu(stu_t* head){
stu_t* p = head->next;
stu_t* temp = 0;
while(p!= NULL){
temp = p;
p = p->next;
free(temp);
}
free(head);
}
//添加学生信息
void insertStu(stu_t* head){
stu_t* newNode = malloc(sizeof(stu_t));
memset(newNode,0,sizeof(stu_t));
char name[20] = {0};
printf("请输入姓名:");
scanf("%s",name);
while(getchar()!=10);
int age = 20;
static int score = 85;
score++;
strcpy(newNode->data.name,name);
newNode->data.age = age;
newNode->data.score = score;
//头插法,插在头节点的下一个节点
/* newNode->next = head->next;
head->next = newNode;*/
//尾插法,插在尾节点的下一个位置
stu_t* p = head;
while(p->next != NULL){
p = p->next;
};
p->next = newNode;
newNode->next = NULL;
}
//打印学生信息
void printStu(stu_t* head){
stu_t* p = head->next;
while(p!=NULL){
printf("******\n");
printf("姓名:%s\n",p->data.name);
printf("成绩:%d\n",p->data.score);
p=p->next;
}
printf("******\n");
}
//保存到文件中
void saveStu(stu_t* head){
FILE* fp = fopen("./stuDB.txt","w");
stu_t* p = head->next;
while(p != NULL){
fwrite(&(p->data),sizeof(data_t),1,fp);
//fprintf(fp,"%s %d %d\n",p->data.name,p->data.age,p->data.score);
p = p->next;
}
fclose(fp);
}
//加载的意思是加载到原有链表的后面
void loadStu(stu_t* head){
int retval = 0;
data_t data = {0};
stu_t* p = head;
while(p->next!=NULL){
p = p->next;
}
FILE* fp = fopen("./stuDB.txt","r");//加载以只读的方式打开
if(fp == 0){return;}
while(1){
retval = fread(&data,sizeof(data_t),1,fp);
if(retval == 0){break;}
stu_t* newNode = malloc(sizeof(stu_t));
memset(newNode,0,sizeof(stu_t));
newNode->data = data;
p->next = newNode;
newNode->next = NULL;
p = p->next;
}
fclose(fp);
}
//单向链表至始至终头结点都没有存放数据
int main(){
stu_t* head = init();//创建头节点
insertStu(head);//添加学生信息
insertStu(head);//添加学生信息
insertStu(head);//添加学生信息
printStu(head);//打印学生信息
removeStu(head);//删除学生
sortStu(head);//排序学生成绩
loadStu(head);//加载
printStu(head);//打印学生信息
saveStu(head);//保存
freeStu(head);//释放链表
return 0;
}
单向链表的逆序排序
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
typedef struct Data{
char name[20];
int age;
int score;
}data_t;
typedef struct Stu{
data_t data;
struct Stu* next;
}stu_t;
void insertStu(stu_t* head){
stu_t* newNode = malloc(sizeof(stu_t));
memset(newNode,0,sizeof(stu_t));
char name[20] = {0};
printf("请输入姓名:");
scanf("%s",name);
while(getchar()!=10);
int age = 20;
static int score = 85;
score++;
strcpy(newNode->data.name,name);
newNode->data.age = age;
newNode->data.score = score;
//头插法
/* newNode->next = head->next;
head->next = newNode;*/
stu_t* p = head;
while(p->next != NULL){
p = p->next;
};
p->next = newNode;
newNode->next = NULL;
}
void printStu(stu_t* head){
stu_t* p = head->next;
while(p!=NULL){
printf("******\n");
printf("姓名:%s\n",p->data.name);
printf("成绩:%d\n",p->data.score);
p=p->next;
}
printf("******\n");
}
/*
封装函数实现功能:将链表逆序
*/
void exchange(stu_t* head){
stu_t* p = head;
stu_t* q = head;
stu_t* k = NULL;
while(1){
p = head;
while(p->next!=NULL){
k = p;
p = p->next;
}
p->next = q->next;
q->next = p;
k->next = NULL;
q = q->next;
if(k == q->next){break;}
}
}
int main(){
stu_t* head = malloc(sizeof(stu_t));
memset(head,0,sizeof(stu_t));
insertStu(head);
insertStu(head);
insertStu(head);
insertStu(head);
insertStu(head);
exchange(head);
printStu(head);
return 0;
}