🍔链表基础知识
1.概念:链表是由多个节点链接构成的,节点包含数据域和指针域,指针域上存放的指针指向下一个节点
2.链表的种类:按单向或双向、带头或不带头、循环或不循环分为多个种类
3.特点:无法直接找到结点,但可以快速插入、删除节点,尤其适用于头插,效率块于其它方式
4.应用:p2p网络,文件系统,作为其他数据结构的基础数据结构
👻本篇文章是关于带头单向链表的
🍟带头单链表
👻头可以理解为哨兵位。哨兵不用干其他活,放好哨就行,而哨兵位不存储有效数据,只提供指向下一个节点(即存放有效数据的第一个节点)的指针。
👻以一个学生信息表为例,用单链表存储学生信息
🍟定义部分
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct {int age;int height;double weight;
}Student;
typedef struct node {Student stu;struct node* next;
}Node;
🍟函数部分
🥤一、辅助操作函数
🍕1.结点的拷贝函数
//节点的拷贝函数
void CopyValue(Student* s1, Student* s2){s1->age = s2->age;s1->height = s2->height;s1->weight = s2->weight;
}
🍕2.输入学生信息函数
//输入学生信息
void InputValue(Student* stu) {if (!stu)return;printf("学生的年龄,身高,体重\n");scanf("%d%d%lf", &stu->age, &stu->height, &stu->weight);
}
🍕3.打印信息函数
//输出每个节点的信息
void DisplayNode(Student* stu) {printf("年龄:%-5d 身高:%-5d 体重:%-5lf\n", stu->age, stu->height, stu->weight);
}
🍕4.打印单链表
//打印单链表
void DisplayLink(Node* head) {//1.判断链表是否为空,为空直接返回if (!head)return;//2.打印每个节点信息Node* p = head->next;while (p != NULL) {DisplayNode(&p->stu);p = p->next;}
}
🥤二、创建节点,创建、销毁链表
🍕1.创建节点
//创建节点
Node* CreateNode(Student* stu){//1.申请一份空间Node* node = (Node*)malloc(sizeof(Node));//2.判断空间是否申请成功if (!node) {printf("内存不足\n");return NULL;}//3.初始化结节中的数据域,即学生信息CopyValue(&node->stu, stu);node->next = NULL;//4.返回创建的结点return node;
}
🍕2.创建链表
//创建链表
Node* CreateLink() {Student stu = { 0,0,0 };//该组数据不是有效数据//1.创建头结点Node* head = CreateNode(&stu);if (!head) exit(-1);//2.创建其他结点,连接到头结点构成链表int tag = 0;Node* pnew, * tail = head;printf("是否创建新结点,继续添加输入1,否则输入0\n");scanf("%d", &tag);while (tag != 0) {InputValue(&stu);pnew=CreateNode(&stu);tail->next = pnew;tail = pnew;printf("是否创建新结点,继续添加输入1,否则输入0\n");scanf("%d", &tag);}return head;
}
🍕3.销毁链表
//销毁链表
void FreeLink(Node* head) {//1.判断链表是否为空,为空则返回if (head == NULL)return;//如果非空,则逐个结点释放Node* p, * q;p = head;while (p->next != NULL) {q = p->next;p->next = q->next;free(q);}free(head);
}
🥤三、增删查改——增
🍕在指定节点后面插入节点
//在指定位置插入节点
void InsertNode(Node* head, Node* pnew,int i) {//1.判断链表是否为空,为空拒绝插入if (!head)return;//2.判断结点是否为空,为空拒绝插入if (!pnew)return;//3.遍历链表,插入指定位置Node* p = head;for (int n = 0;n < i;n++) {p = p->next;}pnew->next = p->next;p->next = pnew;
}
只能对当前指针所在节点处的后面的结点进行操作!!!
如果想在指定节点前面插入节点,只需要改变for循环中的初始值,让指针停留在指定节点的前置节点处,就可以插入到指定节点的前面了
🥤四、增删查改——删
🍕删除指定位置节点
//删除指定位置节点
void DeleteNode(Node* head, int i) {//1.判断链表是否为空,为空直接返回if (!head)return;//2.遍历链表找到要删除的位置Node *p=head,*q;for (int n = 1;n < i&&p->next!=NULL;n++) {p = p->next;//当p等于NULL了,说明把整条链表都遍历完了还没有找到}//3.如果找到了该位置,删除,找不到就返回if (!p)return;q = p->next;p->next = q->next;free(q);
}
只能对当前指针所在节点处的后面的结点进行操作!!!
n从1开始,指针停留位置就是被删除节点的前置节点,从而可以删除掉指定节点;如果n从0开始,指针停留位置是指定节点,此时就无法删除该位置了,此时的指针只能掌控住指向节点之后的节点。
🥤五、增删查改——查与改
🍕比较两节点内容
//比较两节点内容
int Compare(Student* s1, Student* s2) {if (s1 == NULL || !s2)return -1;if (s1->age == s2->age && s1->height == s2->height && s1->weight == s2->weight)return 1;return 0;
}
🍕根据指定信息查找结点
//根据指定信息查找节点
int CheckNode(Node* head, Student* stu) {//1.链表与查找的节点不为空if (!head || !stu)return 0;//2.遍历单链表,找到是否有相同的节点Node* p = head->next;int ret = 1;int m = 0;while (p!=NULL) {m = Compare(&p->stu, stu);if (m == 1)return ret;p = p->next;ret++;}return 0;
}
🍕改变指定位置的节点信息
//改变指定位置的节点信息
void ModifyValue(Node* head,Student*stu, int input) {//1.判断第一个节点是否存在if (!head || head->next == NULL)return;//2.遍历链表找到节点位置Node* p = head->next;for (int i = 1;i < input;i++) {p = p->next;}//3.改变信息p->stu.age = stu->age;p->stu.height = stu->height;p->stu.weight = stu->weight;
}
🍟测试带头单链表完整程序
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
typedef struct {int age;int height;double weight;
}Student;
typedef struct node {Student stu;struct node* next;
}Node;
//节点的拷贝函数
void CopyValue(Student* s1, Student* s2){s1->age = s2->age;s1->height = s2->height;s1->weight = s2->weight;
}
//输入学生信息
void InputValue(Student* stu) {if (!stu)return;printf("学生的年龄,身高,体重\n");scanf("%d%d%lf", &stu->age, &stu->height, &stu->weight);
}
//输出每个节点的信息
void DisplayNode(Student* stu) {printf("年龄:%-5d 身高:%-5d 体重:%-5lf\n", stu->age, stu->height, stu->weight);
}
//创建节点
Node* CreateNode(Student* stu){//1.申请一份空间Node* node = (Node*)malloc(sizeof(Node));//2.判断空间是否申请成功if (!node) {printf("内存不足\n");return NULL;}//3.初始化节点中的数据域,即学生信息CopyValue(&node->stu, stu);node->next = NULL;//4.返回创建的节点return node;
}//创建链表
Node* CreateLink() {Student stu = { 0,0,0 };//1.创建头节点Node* head = CreateNode(&stu);if (!head) exit(-1);//2.创建其他节点,连接到头节点构成链表int tag = 0;Node* pnew, * tail = head;printf("是否创建新结点,继续添加输入1,否则输入0\n");scanf("%d", &tag);while (tag != 0) {InputValue(&stu);pnew=CreateNode(&stu);tail->next = pnew;tail = pnew;printf("是否创建新结点,继续添加输入1,否则输入0\n");scanf("%d", &tag);}return head;
}
//销毁链表
void FreeLink(Node* head) {//1.判断链表是否为空,为空则返回if (head == NULL)return;//如果非空,则逐个节点释放Node* p, * q;p = head;while (p->next != NULL) {q = p->next;p->next = q->next;free(q);}free(head);
}//打印单链表
void DisplayLink(Node* head) {//1.判断链表是否为空,为空直接返回if (!head)return;//2.打印每个节点信息Node* p = head->next;while (p != NULL) {DisplayNode(&p->stu);p = p->next;}
}
//在指定位置插入节点
void InsertNode(Node* head, Node* pnew,int i) {//1.判断链表是否为空,为空拒绝插入if (!head)return;//2.判断结点是否为空,为空拒绝插入if (!pnew)return;//3.遍历链表,插入指定位置Node* p = head;for (int n = 0;n < i;n++) {p = p->next;}pnew->next = p->next;p->next = pnew;
}
//在指定位置删除结点
void DeleteNode(Node* head, int i) {//1.判断链表是否为空,为空直接返回if (!head)return;//2.遍历链表找到要删除的位置Node *p=head,*q;for (int n = 1;n < i&&p->next!=NULL;n++) {p = p->next;//当p等于NULL了,说明把整条链表都遍历完了还没有找到}//3.如果找到了该位置,删除,找不到就返回if (!p)return;q = p->next;p->next = q->next;free(q);
}
//比较两节点内容
int Compare(Student* s1, Student* s2) {if (s1 == NULL || !s2)return -1;if (s1->age == s2->age && s1->height == s2->height && s1->weight == s2->weight)return 1;return 0;
}
//根据指定信息查找节点
int CheckNode(Node* head, Student* stu) {//1.链表与查找的节点不为空if (!head || !stu)return 0;//2.遍历单链表,找到是否有相同的节点Node* p = head->next;int ret = 1;int m = 0;while (p!=NULL) {m = Compare(&p->stu, stu);if (m == 1)return ret;p = p->next;ret++;}return 0;
}//改变指定位置的节点信息
void ModifyValue(Node* head,Student*stu, int input) {//1.判断第一个节点是否存在if (!head || head->next == NULL)return;//2.遍历链表找到节点位置Node* p = head->next;for (int i = 1;i < input;i++) {p = p->next;}//3.改变信息p->stu.age = stu->age;p->stu.height = stu->height;p->stu.weight = stu->weight;
}int main() {//1.初步创建链表并插入初始信息,最终打印Node* head = CreateLink();DisplayLink(head);//2.插入新节点printf("请输入要插入的位置:\n");int input = 0;scanf("%d", &input);//得到位置Student stu;printf("请输入学生信息:\n");InputValue(&stu);//得到信息Node* pnew = CreateNode(&stu);//得到新的结点InsertNode(head, pnew, input);printf("以下是插入信息后的信息表\n");DisplayLink(head);//3.在指定位置删除结点printf("请输入要删除的位置\n");int input2 = 0;scanf("%d", &input2);DeleteNode(head, input2);printf("以下是删除信息后的信息表\n");DisplayLink(head);//4.根据指定信息查找结点int input3;do{if (head->next == NULL) {printf("表中已无信息\n");break;}printf("请输入要查找的学生信息\n");Student stu2;InputValue(&stu2);int ret = CheckNode(head, &stu2);if (ret != 0)printf("找到了,该学生编号为:%d\n", ret);elseprintf("无该学生信息\n");printf("是否继续查找,是输1,否输0:");input3 = 0;scanf("%d", &input3);} while (input3 == 1);//5.改变指定位置的节点信息printf("请输入要改变的结点位置:");int input4 = 0;scanf("%d", &input4);printf("请输入改变后的信息\n");Student stu3;InputValue(&stu3);ModifyValue(head,&stu3,input4);printf("以下是改变信息后的信息表\n");DisplayLink(head);//销毁链表FreeLink(head);
}
🌈测试结果
如有错误请指正哦,感谢❤️