链式存储结构:
不连续的存储空间(链表)
链表是由一系列的节点组成的每个节点包含两个域:指针域、数据域
节点的next指针指向空NULL则为最后一个节点。
插入新节点
找到插入的上一个节点,让其指针指向插入节点
再让插入节点指针指向下一个节点
删除新节点
找到删除的上一个节点,让其指针指向删除元素的下一个节点
回收删除元素的内存。
单向链表:
节点之间只有单向连接关系,可以递归查找下一个节点
数据层面相关的操作需要通过回调函数接口,用户自定义实现,但是在链表的节点层面包含有用户数据层,没有做到完全数据解耦合。
头文件的实现
//
// Created by zmj on 2024/1/16. 单向链表 传统链表
//
#ifndef TEST_LINKLIST_H
#define TEST_LINKLIST_H
#include "stdlib.h"
#include "stdio.h"
//链表节点
typedef struct LINKNODE{
void* data;//void* 可以指向任何类型数据
struct LINKNODE* next;
}LinkNode;
//链表
typedef struct LINKLIST{
LinkNode *head;
int size;
}LinkList;
typedef void(*PRINTLINKNODE)(void*);
//链表操作函数
//初始化
LinkList * Init_LinkList(){
LinkList *list = (LinkList*)malloc(sizeof(LinkList));
list->size=0;
//头节点是不保存数据,避免头结点判断,简化逻辑
list->head=(LinkNode*) malloc(sizeof(LinkNode));
list->head->data = NULL;
list->head->next = NULL;
return list;
};
//指定位置插入
void Insert_LinkList(LinkList* list,int pos,void* data){
if(list==NULL){
return;
}
if(data==NULL){
return;
}
if(pos<0||pos>list->size){
pos = list->size;
}
//创建新的节点
LinkNode *newnode = (LinkNode*)malloc(sizeof(LinkNode));
newnode->data = data;
newnode->next = NULL;
//找到插入的上一个节点
//先定义一个辅助节点,让其指向链表头。
LinkNode *pFrontNode = list->head;
for(int i=0;i<pos;i++){
pFrontNode = pFrontNode->next;
}
//插入新节点
newnode->next = pFrontNode->next;
pFrontNode->next = newnode;
list->size++;
};
//删除指定位置元素
void Remove_LinkList(LinkList* list,int pos){
if(list==NULL){
return;
}
if(pos<0||pos>list->size){
return;
}
//查找删除节点的前一个节点
LinkNode *pFrontNode = list->head;
for(int i=0;i<pos;i++){
pFrontNode = pFrontNode->next;
}
//辅助删除的节点指向下一个节点
LinkNode *pDel = pFrontNode->next;
pFrontNode->next = pDel->next;
free(pDel);
list->size--;
};
//获得链表的长度
int Size_LinkList(LinkList* list){
return list->size;
};
//查找
int Find_LinkList(LinkList* list,void* data){
if(list==NULL){
return -1;
}
if(data==NULL){
return -1;
}
LinkNode *pCurrent = list->head->next;
int i=0;
while (pCurrent!=NULL){
if(pCurrent->data==data){
break;
}
i++;
pCurrent = pCurrent->next;
}
};
//返回链表的第一个节点
void *Front_LinkList(LinkList* list){
return list->head->next->data;
};
//打印链表
void Print_LinkList(LinkList* list,PRINTLINKNODE print){
if(list==NULL){
return;
}
LinkNode *pCurrent = list->head->next;
while (pCurrent!=NULL){
print(pCurrent->data);
pCurrent = pCurrent->next;
}
};
//释放链表的内存
void FreeSpace_LinkList(LinkList* list){
if(list==NULL){
return;
}
LinkNode *pCurrent =list->head;
while (pCurrent!=NULL){
LinkNode *pNext = pCurrent->next;
free(pCurrent);
pCurrent = pNext;
}
list->size=0;
free(list);
};
#endif //TEST_LINKLIST_H
测试程序:
typedef struct PERSON{
char name[64];
int age;
int score;
}Person;
//链表用户自定义打印回调函数
void MyPrint(void* data){
Person *p = (Person*)data;
printf(" name: %s, age: %d, score: %d\n",p->name,p->age,p->score);
}
int main(){
//测试单向链表
LinkList *list =Init_LinkList();
Person p1 = {"aaa",22,90};
Person p2 = {"bbb",18,100};
Person p3 = {"ccc",16,86};
Person p4 = {"ddd",20,105};
Person p5 = {"eee",24,77};
Insert_LinkList(list,0,&p1);
Insert_LinkList(list,0,&p2);
Insert_LinkList(list,0,&p3);
Insert_LinkList(list,0,&p4);
Insert_LinkList(list,0,&p5);
Print_LinkList(list,MyPrint);
Remove_LinkList(list,3);
printf("------------------------\n");
Print_LinkList(list,MyPrint);
FreeSpace_LinkList(list);
return 0;
}
优点:
没有大量的数据操作,均是指针操作,可以无限扩容
缺点:
只能单向迭代遍历,不能快捷访问,插入等操作仍然不够简洁
数据定义在node里面,初始化释放均比较需要进行两次内存操作