1.前言
单链表一般企业用到比较少,用的比较多是的企业链表,下图是企业链表的结构
单链表结构如图
个人理解:单链表存储的时候可能要指定数据类型,当然也可以用void*代表任意数据类型的数据,但是企业链表可以由用户指定数据类型,我们只需要创造链表,通过指针将链表串起来
2.代码组成
2.1 EnterpriseLinkList.h 头文件,定义企业链表有哪些功能
#pragma once
#include<iostream>
#pragma once
//链表小节点
typedef struct LINKNODE {
struct LINKNODE* next;
}LinkNode;
//链表节点
typedef struct LINKLIST {
LinkNode head;
int size;
}LinkList;
//遍历函数指针
typedef void(*PRINTNODE)(LinkNode*);
//比较函数指针
typedef int(*COMPARRE)(LinkNode*, LinkNode*);
//初始化链表
LinkList* Init_LinkList();
//插入
void Insert_LinkList(LinkList* list, int pos, LinkNode* data);
//删除
void Remove_LinkList(LinkList* list, int pos);
//查找
int Find_LinkList(LinkList* list, LinkNode* data, COMPARRE compare);
//返回链表的大小
int Size_LinkList(LinkList* list);
//打印
void Print_LinkList(LinkList* list, PRINTNODE print);
//释放链表内存
void FreeSpace_LinkList(LinkList* list);
注意 链表节点中用的是LinkNode而不是LinkNode*
2.2 EnterpriseLinkList.cpp 源文件
#include"EnterpriseLinkList.h"
//初始化链表
LinkList* Init_LinkList()
{
LinkList* list = new LinkList;
list->head.next = NULL;
list->size = 0;
return list;
}
//插入
void Insert_LinkList(LinkList* list, int pos, LinkNode* data)
{
if (list == NULL)return;
if (data == NULL)return;
if (pos<0 || pos>list->size) {
pos = list->size;
}
//查找插入位置
LinkNode* pCurrent = &(list->head);
for (int i = 0; i < pos; i++) {
pCurrent = pCurrent->next;
}
data->next = pCurrent->next;
pCurrent->next = data;
list->size++;
}
//删除
void Remove_LinkList(LinkList* list, int pos)
{
if (list == NULL)return;
if (pos<0 || pos>=list->size) return;
LinkNode* pCurrent = &(list->head);
for (int i = 0; i < pos; i++) {
pCurrent = pCurrent->next;
}
pCurrent->next = pCurrent->next->next;
list->size--;
}
//查找
int Find_LinkList(LinkList* list, LinkNode* data, COMPARRE compare)
{
if (list == NULL)return -1;
if (data == NULL)return -1;
LinkNode* pCurrent = list->head.next;
int index = 0;
int flag = -1;
while (pCurrent != NULL) {
if (compare(pCurrent, data) == 0) {
flag = index;
break;
}
pCurrent = pCurrent->next;
index++;
}
return flag;
}
//返回链表的大小
int Size_LinkList(LinkList* list)
{
return list->size;
}
//打印
void Print_LinkList(LinkList* list, PRINTNODE print)
{
if (list == NULL)return;
LinkNode* pCurrent = list->head.next;
while (pCurrent!=NULL)
{
print(pCurrent);
pCurrent = pCurrent->next;
}
}
//释放链表内存
void FreeSpace_LinkList(LinkList* list)
{
if (list == NULL)return;
delete list;
}
2.2.1 初始化链表LinkList* Init_LinkList()
在堆区人为开辟LinkList类型,将头节点的指针置空(NULL或者nullptr都可以),将list的大小置0
2.2.2 给位置插数值void Insert_LinkList(LinkList* list, int pos, LinkNode* data)
- 检查
list
和data
是否为空,如果为空(list空说明没有初始化成功,data空证明没有数据)则直接返回,检查pos
是否在合法范围内 - 遍历链表找到要插入位置的前一个节点。遍历的次数就是
pos
的值 - 将要插入节点的
next
指针指向当前位置节点的下一个节点 - 将当前位置节点的
next
指针指向要插入的节点,完成插入操作 - 增加链表的大小(
size
)
2.2.3 删除链表数值void Remove_LinkList(LinkList* list, int pos)
如同2.2.2,删除前要先检查。然后找到移除位置的前一个节点,将前一个节点的 next
指针指向要移除节点的下一个节点的下一个节点,这样就跳过了要移除的节点,最后减小链表的大小
2.2.4 根据数值查看位置int Find_LinkList(LinkList* list, LinkNode* data, COMPARRE compare)
此处注意函数的形参有一个COMPARRE compare,这个会在test.cpp给出函数具体内容。
index是链表的位置,也就是从0开始,flag目的是记录要查找值的位置最后返回
2.2.5 链表大小int Size_LinkList(LinkList* list)
2.2.6 打印链表的数值void Print_LinkList(LinkList* list, PRINTNODE print)
同2.2.4一样,注意形参PRINTNODE print,从头节点开始依次遍历直到链表最后
2.2.7 释放链表void FreeSpace_LinkList(LinkList* list)
人为开辟的堆都要释放掉
2.3 test.cpp 测试企业链表,包括main函数
#include"EnterpriseLinkList.h"
#include<string>
typedef struct PERSON {
LinkNode node;
char name[64];
int age;
}Person;
void MyPrint(LinkNode* data) {
auto p = (Person*)data;
std::cout << "Name:" << p->name << " age:" << p->age << std::endl;
}
int MyCompare(LinkNode* data1, LinkNode* data2) {
auto p1= (Person*)data1;
auto p2 = (Person*)data2;
if (strcmp(p1->name , p2->name)==0 && p1->age == p2->age) { return 0; }
return -1;
}
void test_EnterpriseLinkList()
{
LinkList* list = Init_LinkList();
Person p1, p2, p3, p4, p5;
strcpy_s(p1.name, sizeof(p1.name),"aaa");
strcpy_s(p2.name, sizeof(p2.name), "bbb");
strcpy_s(p3.name, sizeof(p3.name), "ccc");
strcpy_s(p4.name, sizeof(p4.name), "ddd");
strcpy_s(p5.name, sizeof(p5.name), "eee");
p1.age = 10;
p2.age = 20;
p3.age = 30;
p4.age = 40;
p5.age = 50;
Insert_LinkList(list, 0, (LinkNode*)&p1);
Insert_LinkList(list, 0, (LinkNode*)&p2);
Insert_LinkList(list, 0, (LinkNode*)&p3);
Insert_LinkList(list, 0, (LinkNode*)&p4);
Insert_LinkList(list, 0, (LinkNode*)&p5);
//打印
Print_LinkList(list, MyPrint);
//删除节点
Remove_LinkList(list, 2);
std::cout << "----------------------" << std::endl;
Print_LinkList(list, MyPrint);
//查找
Person findP;
strcpy_s(findP.name,sizeof(findP.name),"aaa");
findP.age = 10;
std::cout << "要查找的人的姓名为: " << findP.name << " 年纪为: " << findP.age << std::endl;
int pos = Find_LinkList(list, (LinkNode*)&findP, MyCompare);
std::cout << "位置为: " << pos << std::endl;;
FreeSpace_LinkList(list);
}
int main()
{
test_EnterpriseLinkList();
return 0;
}
注意因为PERSON结构体内姓名的定义是char类型,strcpy会报错,所以我用的是strcpy_s函数
auto是c++11的特性,可以不用auto
auto p = (Person*)data;可以替换成Person* p = (Person*)data