用C语言实现链表(增删改查)
今天是第二次发记录,加油💪
定义节点类型
typedef struct Node {
ElemType data;
struct Node *p_next;
} linkList;
解释:起一个linkList的别名是为了方便后面直接用linkList来直接代替struct Node
初始化链表
//初始化头节点函数
void initLinkList(linkList *L) {
L = (linkList *) malloc(sizeof(linkList)); //为头节点分配空间
L->p_next = NULL; //将头节点指针置空
}
解释:也就是初始化头节点,给头节点分配一个内存然后将其指针域指向NULL
创造链表函数
void createLinkList(linkList *L) {
struct Node *p_now; //声明当前指针
struct Node *p_new; //声明新指针
int num;
p_now = L; //将当前指针从头节点开始
printf("请问你想要创建多少数据\n");
scanf("%d", &num);
for (int i = 0; i < num; ++i) {
printf("请输入第%d条数据", i + 1);
p_new = (struct Node *) malloc(sizeof(struct Node *));//给新指针分配内存
scanf("%d", &p_new->data); //设置新节点数据
p_new->p_next = NULL; //设置新节点指针
p_now->p_next = p_new; //将新建节点链接到当前节点的尾部
p_now = p_new; //改变当前节点
}
printf("创建成功\n");
}
解释:询问用户想要创建多少个节点对象,然后开始创建链表,p_now指向当前节点的一个指针(用来遍历) p_new代表新创建出来的节点,给p_new分配好内存后,先完善这个p_new节点(也就是先将这个节点的数据域和指针域完善),然后这里用的是前插法来插入节点的,最后改变p_now指针使得其一直指向的是当前的节点
输出链表函数
void printLinkList(linkList *L) {
struct Node *p_now;
int count = 0;
p_now = L->p_next; //因为头节点不放数据,所以直接将当前节点改变为头节点后的一个节点
while (p_now) {
printf("%d\n", p_now->data);
p_now = p_now->p_next;
count++;
}
printf("当前链表中共有%d个节点数据", count);
}
解释:先声明一个p_now作为“当前指针”,用count来记录这个链表中一共有多少个节点,然后用while循环进行遍历,如果存在这个当前节点就输出当前节点的数据,count++,然后不断的移动当前节点
插入元素函数
//pos代表第几个元素不是下标
void insertLinkList(linkList *L, int pos, struct Node *new_node) {
struct Node *p_now;
p_now = L;
for (int i = 0; i < pos - 1; ++i) {
p_now = p_now->p_next;
}
new_node->p_next = p_now->p_next;
p_now->p_next = new_node;
}
解释:先用p_now指向头节点或第一个存储数据的节点(如果是头节点想下面的i就从0开始,如果是第一个节点,下面的i就从1开始)。 一直将p_now移向要插入位置的前一个节点的位置,然后将要插入节点的指针域指向原本在这个位置的节点,然后将前一个节点的指针域里的后继指针指向这个新节点
查找元素位置函数
//查找元素位置
int findElem(linkList *L, ElemType e) {
struct Node *p_now;
p_now = L->p_next; //从第一个元素开始
int pos = 1;
while (p_now) {
if (p_now->data == e) {
return pos;
} else {
pos++;
p_now = p_now->p_next;
}
}
return -1;
}
解释:p_now指向第一个存储数据的元素,然后开始遍历,如果当前节点的值等于传入的e就输出当前pos值,如果不相等就让pos自增然后不断移动p_now指针来遍历
更新元素函数
void updateLinkList(linkList *L, int pos, ElemType e) {
struct Node *p_now;
p_now = L;
for (int i = 0; i < pos; ++i) {
p_now = p_now->p_next;
}
p_now->data = e;
}
解释:根据传入的位置进行遍历,遍历到要改变位置的那个节点后直接改变值即可
删除元素函数
void deleteLinkList(linkList *L, int pos) {
struct Node *p_now;
struct Node *p_temp; //用于记录被销毁的指针防止其变为野指针
p_now = L;
for (int i = 0; i < pos - 1; ++i) {
p_now = p_now->p_next;
}
p_temp = p_now->p_next; //保存被删除的那个节点
p_now->p_next = p_temp->p_next; //将前一个节点的指针指向被删除节点的后一个节点
free(p_temp); //释放内存
}
解释:p_now用来遍历,p_temp用来指向要被删除的那个节点(方便释放内存)防止这个被删除的节点变为野指针。
各个函数的测试用例
void testInsert(linkList *L) {
struct Node newNode;
newNode.data = 5;
newNode.p_next = NULL;
insertLinkList(L, 2, &newNode);
printLinkList(L);
}
void testFindElem(linkList *L) {
int pos;
pos = findElem(L, 3);
printf("\n这个元素的位置是%d", pos);
}
void testUpdate(linkList *L) {
updateLinkList(L, 3, 999);
printLinkList(L);
}
void testDelete(linkList *L) {
deleteLinkList(L, 3);
printLinkList(L);
}
将各个函数封装成一个测试函数,到时候直接在主函数中调用即可
tips:如果用的话(,因为里面我提前设了一些值,)在创造链表的时候至少创建三个节点。
总代码
#include <stdio.h>
#include <stdlib.h>
typedef int ElemType;
typedef struct Node {
ElemType data;
struct Node *p_next;
} linkList;
void initLinkList(linkList *L); //初始化头节点函数
void createLinkList(linkList *L); //创建头节点函数
void printLinkList(linkList *L); //输出链表
void insertLinkList(linkList *L, int pos, struct Node *new_node); //插入数据
int findElem(linkList *L, ElemType e); //查询数据返回元素位置
void updateLinkList(linkList *L, int pos, ElemType e); //修改数据
void deleteLinkList(linkList *L, int pos); //删除数据
void testInsert(linkList *L); //单元测试插入函数
void testFindElem(linkList *L); //测试查询元素函数
void testUpdate(linkList *L); //测试更新函数
void testDelete(linkList *L); //测试删除函数
int main() {
linkList L; //创建头节点
int pos;
initLinkList(&L);
createLinkList(&L);
printLinkList(&L);
// //测试插入函数
// testInsert(&L);
// //测试查询函数
// testFindElem(&L);
// //测试更新函数
// testUpdate(&L);
// printLinkList(&L);
// //测试删除链表
// testDelete(&L);
return 0;
}
//初始化头节点函数
void initLinkList(linkList *L) {
L = (linkList *) malloc(sizeof(linkList)); //为头节点分配空间
L->p_next = NULL; //将头节点指针置空
}
void createLinkList(linkList *L) {
struct Node *p_now; //声明当前指针
struct Node *p_new; //声明新指针
int num;
p_now = L; //将当前指针从头节点开始
printf("请问你想要创建多少数据\n");
scanf("%d", &num);
for (int i = 0; i < num; ++i) {
printf("请输入第%d条数据", i + 1);
p_new = (struct Node *) malloc(sizeof(struct Node *));//给新指针分配内存
scanf("%d", &p_new->data); //设置新节点数据
p_new->p_next = NULL; //设置新节点指针
p_now->p_next = p_new; //将新建节点链接到当前节点的尾部
p_now = p_new; //改变当前节点
}
printf("创建成功\n");
}
void printLinkList(linkList *L) {
struct Node *p_now;
int count = 0;
p_now = L->p_next; //因为头节点不放数据,所以直接将当前节点改变为头节点后的一个节点
while (p_now) {
printf("%d\n", p_now->data);
p_now = p_now->p_next;
count++;
}
printf("当前链表中共有%d个节点数据", count);
}
//pos代表第几个元素不是下标
void insertLinkList(linkList *L, int pos, struct Node *new_node) {
struct Node *p_now;
p_now = L;
for (int i = 0; i < pos - 1; ++i) {
p_now = p_now->p_next;
}
new_node->p_next = p_now->p_next;
p_now->p_next = new_node;
}
//查找元素位置
int findElem(linkList *L, ElemType e) {
struct Node *p_now;
p_now = L->p_next; //从第一个元素开始
int pos = 1;
while (p_now) {
if (p_now->data == e) {
return pos;
} else {
pos++;
p_now = p_now->p_next;
}
}
return -1;
}
void updateLinkList(linkList *L, int pos, ElemType e) {
struct Node *p_now;
p_now = L;
for (int i = 0; i < pos; ++i) {
p_now = p_now->p_next;
}
p_now->data = e;
}
void deleteLinkList(linkList *L, int pos) {
struct Node *p_now;
struct Node *p_temp; //用于记录被销毁的指针防止其变为野指针
p_now = L;
for (int i = 0; i < pos - 1; ++i) {
p_now = p_now->p_next;
}
p_temp = p_now->p_next; //保存被删除的那个节点
p_now->p_next = p_temp->p_next; //将前一个节点的指针指向被删除节点的后一个节点
free(p_temp); //释放内存
}
void testInsert(linkList *L) {
struct Node newNode;
newNode.data = 5;
newNode.p_next = NULL;
insertLinkList(L, 2, &newNode);
printLinkList(L);
}
void testFindElem(linkList *L) {
int pos;
pos = findElem(L, 3);
printf("\n这个元素的位置是%d", pos);
}
void testUpdate(linkList *L) {
updateLinkList(L, 3, 999);
printLinkList(L);
}
void testDelete(linkList *L) {
deleteLinkList(L, 3);
printLinkList(L);
}
谢谢查阅🙏