注意插入和删除的时候的pos
代码语言:javascript
复制#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//节点结构体
struct LinkNode {
LinkNode* next;//指针域
};
//链表结构体
struct LList {
LinkNode pHeader;//头节点结构体
int size;//链表长度
};
//取个别名
typedef void* LinkList;
//链表的初始化:返回链表结构体
LinkList init_LinkList()
{
struct LList* mylist = (LList*)malloc(sizeof(LList));
if (mylist == NULL)
return NULL;
mylist->pHeader.next = NULL;
mylist->size = 0;
return mylist;
}
//插入
//第一个参数相当于void* list,这样用户就无法修改堆区结构体数据
//这边如果不用两个指针一前一后移动来插入,要注意curNode指针的移动位置,不要让其移到空位
void insert_LinkList(LinkList list,int pos,void* data)
{
if (list == NULL)
return;
struct LList* mylist = (LList*)list;
if (pos<0 || pos>mylist->size)
{
//尾插
pos = mylist->size;
}
//指向当前节点
LinkNode* curNode = &mylist->pHeader;
//将curNode移到要插入位置的前驱节点
for(int i = 0; i < pos; i )
{
curNode = curNode->next;
}
//找到位置后,进行插入操作
//将newNode指针指向用户传入的结构体首地址,获取前四个字节的数据
struct LinkNode* newNode = (LinkNode*)data;
newNode->next = NULL;
newNode->next = curNode->next;
curNode->next = newNode;
//更新链表长度
mylist->size ;
}
//遍历链表
void foreach_LinkList(LinkList list,void(*print)(void*))
{
if (list == NULL)
return;
if (print == NULL)
return;
LList* mylist = (LList*)list;
//指向当前节点
LinkNode* curNode = mylist->pHeader.next;
while (curNode)
{
//这里传入当前要打印的节点,传入的是一个指针指向在堆区开辟的结构体前四个字节,步长为4
//用户通过自己写打印函数,将步长变为要打印的结构体步长
print(curNode);
curNode = curNode->next;
}
}
//删除指定位置的节点----有效数据下标从0开始
void del_LinkList(LinkList mylist, int pos)
{
if (mylist == NULL)
return;
LList* list = (LList*)mylist;
if (pos<0 || pos>=list->size)
return;
//当前节点
LinkNode* curNode = &list->pHeader;
//将curNode移到要删除位置的前驱节点
for (int i = 0; i <pos; i )
{
curNode = curNode->next;
}
//记录待删除的节点
LinkNode* delNode = curNode->next;
curNode->next = delNode->next;
//free(delNode); 数据是用户取管理释放,因为不知道是开辟在栈区还是堆区
//delNode->next = NULL;
//更新链表长度
list->size--;
}
//按照值来删除
void del_LinkList(LinkList mylist,void* data,int(*compare)(void*,void*))
{
if (mylist == NULL)
return;
if (data == NULL)
return;
LList* list =(LList*)mylist;
//当前节点
LinkNode* curNode = &list->pHeader;
for (int i = 0; i < list->size; i )
{
curNode = curNode->next;
if (compare(curNode, data))
{
del_LinkList(list, i);
}
}
}
//清空链表
void clear_List(LinkList list)
{
if (list == NULL)
return;
LList* mylist = (LList*)list;
当前节点
//LinkNode* curNode = &mylist->pHeader;
//while (curNode)
//{
// curNode = curNode->next;
// //保存住下一个节点的位置
// LinkNode* nextNode = curNode->next;
// //free(curNode);数据是用户取管理释放,因为不知道是开辟在栈区还是堆区
// curNode = nextNode;
//}
mylist->pHeader.next = NULL;
mylist->size = 0;
}
//返回链表的长度
int size_List(LinkList list)
{
if (list == NULL)
return NULL;
LList* mylist = (LList*)list;
return mylist->size;
}
//销毁链表
void destory_List(LinkList list)
{
if (list == NULL)
return;
free(list);
list == NULL;
}
struct person
{
void* data;
char name[32];
int age;
};
void print(void* val)
{
person* p = (person*)val;
printf("姓名:%st年龄:%dn", p->name, p->age);
}
int compare(void* v1, void* v2)
{
person* p1 = (person*)v1;
person* p2 = (person*)v2;
if (p1->age == p2->age && strcmp(p1->name, p2->name)==0)
{
return 1;
}
return 0;
}
int main()
{
//用户只能在main函数里面拿到一个void*指针,该指针指向堆区开辟的链表结构体
//但用户无法知晓void*指向的堆区开辟内存里面存放的数据类型,也就无法通过强制类型转换对堆区的链表结构体数据进行修改
LinkList list = init_LinkList();
person p1 = { NULL,"大忽悠",19 };
person p2 = { NULL,"like",18 };
person p3 = { NULL,"小朋友",19 };
insert_LinkList(list, 0,&p1);
insert_LinkList(list, -1, &p2);
insert_LinkList(list, 10, &p3);
printf("打印链表结果如下:n");
foreach_LinkList(list, print);
printf("n按位置删除链表后:n");
del_LinkList(list,2);
foreach_LinkList(list, print);
printf("n按值删除链表后:n");
del_LinkList(list, &p1, compare);
foreach_LinkList(list, print);
printf("n链表的长度:%dn", size_List(list));
clear_List(list);
printf("清空链表结果如下:n");
foreach_LinkList(list, print);
printf("n链表的长度:%dn", size_List(list));
destory_List(list);
printf("n链表的长度:%dn", size_List(list));
return 0;
}