线性表的链式存储 --单链表
前面我们写的线性表的顺序存储(动态数组)的案例,最大的缺点是插入和删除时需要移动大量元素,这显然需要耗费时间,能不能想办法解决呢?链表。
链表为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。
线性表的链式存储结构中,每个节点中只包含一个指针域,这样的链表叫单链表。
一 单链表中任意一个节点的 数据模型
通过一组任意的存储单元来存储线性表中的数据元素。每给元素存放元素自身的信息外,还需要存放一个指向其后继的指针,其中data为数据域,存放数据元素;next为指针域,存放后继指针,next的后继指针中的内容也是 和当前节点一样 的数据模型
LNode
typedef struct LNode{ //定义单链表结点类型
int data; //数据域
struct LNode *next; //指针域
}LNode;
其中 int data; 是数据域,当然这里只是用int 类型进行演示。实际代码中可以有很多的类型。链表的示意图如下
传统链表问题的提出
当我们 的 数据域 变化的时候怎么弄呢?
现在 数据域 只有一个 int data, 当我们还要添加 string name , char * othername的时候,怎么办?
也就是说:我们的数据域是 千变万化 的,因此 传统链表 不适用。
Linux内核链式链表
因此 Linux 的内核大神,想到了一个方法
让 数据域 和 指针域 分离, 让业务域包含一个 链式节点,,这个节点也只有一个指向 同样的链式节点。
让数据域 包裹 指针域
typedef struct LNode{ //定义单链表结点类型
struct LNode *next; //指针域
}LNode;
typedef struct Teacher{
int age;
string name;
char *othername;
LNode pnext;
}
企业级链式链表
上述Linux 链式链表的每次都要求 pnext 对于 整个struct 的偏移,
于是企业在开发过程中,就改了一版,让 链表节点在 业务节点的最开始
typedef struct LNode{ //定义单链表结点类型
struct LNode *next; //指针域
}LNode;
typedef struct Teacher{
LNode pnext;
int age;
string name;
char *othername;
}
二 各个链表的模型
三,代码实现
1. 底层设计思考
底层数据类型设计
结构体的第一个参数为 链表的节点:LinkListNode,第二个参数为链表的长度
TLinkList 是不必让上层知道的。
//这个线性表中存储一个数组,而且数组的每一项应该都是一个地址
typedef struct _taglinklist {
LinkListNode head;
int length;//该linklist 的大小
}TLinkList;
那么很显然,这个LinkListNode是 如下的:
这个LinkListNode 是需要让上层知道的。
typedef struct LinkListNode { //链表节点
struct LinkListNode *next;
}LinkListNode;
因为上层要组织数据,按照企业级的组织,一定是类型如下这样的
typedef struct Teacher {
LinkListNode linklistnode;
int age;
char name[128];
char *othername;
char **stuname; //一个老师下面有5个学生
}Teacher;
2. 底层开发 和 上层调用 公用的 002linklist.h
#ifndef __002LINKLIST_H__
#define __002LINKLIST_H__
typedef void LinkList; //链表的返回值,需要使用void *
typedef struct LinkListNode { //链表节点
struct LinkListNode *next;
}LinkListNode;
// 初始化,建立一个空的链表
//返回值不为NULL,表示创建成功。
//返回值为NULL,表示创建失败。
LinkList* LinkList_Create();
//销毁该线性表
//返回值为1,表示成功。
//返回值为-1,表示失败。
int LinkList_Destory(LinkList *list);
//清空seqlist
//返回值为1,表示成功。
//返回值为-1,表示失败。
int LinkList_Clear(LinkList *list);
// 返回线性表List存在的元素个数
//返回值 >=0 表示:该list存在的元素个数
//<0 表示error
int LinkList_Length(LinkList *list);
//从LinkList 中获取指定位置的数据
//参数pos:LinkList中的位置
//返回值:为存储在该位置的元素
//返回NULL 表示有问题
LinkListNode* LinkList_Get(LinkList *list, int pos);
//给LinkList中指定位置插入数据,
//参数LinkListnode为要插入的数据
//参数 pos 为要插入的位置
//成功返回1
//失败 返回<0
int LinkList_Insert(LinkList *list, LinkListNode *node, int pos);
//从LinkList 中删除指定位置的元素
//参数 pos
//返回值为 删除的元素
//返回NULL 表示出现了error
LinkListNode* LinkList_Delete(LinkList *list, int pos);
#endif
3.底层开发
#include "002linklist.h"
#include "stdio.h"
#include "stdlib.h"
#include <string.h>
//这个线性表中存储一个数组,而且数组的每一项应该都是一个地址
typedef struct _taglinklist {
LinkListNode head;
int length;//该linklist 的大小
}TLinkList;
// 初始化,建立一个空的链表
//返回值不为NULL,表示创建成功。
//返回值为NULL,表示创建失败。
LinkList* LinkList_Create() {
TLinkList * tempTLinkList = (TLinkList *)malloc(sizeof(TLinkList));
if(tempTLinkList==NULL) {
printf("LinkList_Create error list==NULL\n");
return NULL;
}
memset(tempTLinkList,0,sizeof(tempTLinkList));
tempTLinkList->head.next = NULL;
tempTLinkList->head.next = NULL;
tempTLinkList->length = 0;
return tempTLinkList;
}
//销毁该线性表
//返回值为1,表示成功。
//返回值为-1,表示失败。
int LinkList_Destory(LinkList *list) {
int ret = 1;
if (list == NULL) {
printf("LinkList_Destory error list==NULL\n");
ret = -1;
return ret;
}
TLinkList *templinklist = NULL;
templinklist = (TLinkList *)list;
if (templinklist != NULL) {
free(templinklist);
}
else {
printf("LinkList_Destory error templinklist==NULL\n");
}
return ret;
}
//清空seqlist
//返回值为1,表示成功。
//返回值为-1,表示失败。
int LinkList_Clear(LinkList *list) {
int ret = 1;
if (list == NULL) {
printf("LinkList_Clear error list==NULL\n");
ret = -1;
return ret;
}
TLinkList *templinklist = NULL;
templinklist = (TLinkList *)list;
templinklist->length = 0;
templinklist->head.next = NULL;
return ret;
}
// 返回线性表List存在的元素个数
//返回值 >=0 表示:该list存在的元素个数
//<0 表示error
int LinkList_Length(LinkList *list) {
int ret = 1;
if (list == NULL) {
printf("LinkList_Length error list==NULL\n");
ret = -1;
return ret;
}
TLinkList *tempseqlist = NULL;
tempseqlist = (TLinkList *)list;
return tempseqlist->length;
}
//从seqlist 中获取指定位置的数据
//参数pos:seqlist中的位置
//返回值:为存储在该位置的元素
//返回NULL 表示有问题
LinkListNode* LinkList_Get(LinkList *list, int pos) {
LinkListNode *retSeqListNode = NULL;
if (list == NULL) {
printf("LinkList_Get error list==NULL\n");
return retSeqListNode;
}
if (pos < 0) {
printf("LinkList_Get error pos<0 pos = %d\n", pos);
return retSeqListNode;
}
TLinkList *tempseqlist = NULL;
tempseqlist = (TLinkList *)list;
if (pos > tempseqlist->length - 1) {
printf("LinkList_Get error pos > (tempseqlist->length - 1) pos = %d,tempseqlist->length = %d\n",
pos, tempseqlist->length);
return retSeqListNode;
}
LinkListNode *curSeqListNode = &(tempseqlist->head);//让curSeqListNode 指向 链表的头部
for (int i = 0; i < pos && (curSeqListNode->next!=NULL);++i) {
curSeqListNode = curSeqListNode->next;
}
return curSeqListNode->next;
}
//给LinkList中指定位置插入数据,
//参数LinkListnode为要插入的数据
//参数 pos 为要插入的位置
//成功返回1
//失败 返回<0
int LinkList_Insert(LinkList *list, LinkListNode *node, int pos) {
int ret = 1;
LinkListNode *retSeqListNode = NULL;
if (list == NULL) {
ret = -1;
printf("LinkList_Insert error list==NULL ret = %d\n", ret);
return ret;
}
if (node == NULL) {
ret = -2;
printf("LinkList_Insert error node==NULL ret = %d\n", ret);
return ret;
}
if (pos < 0) {
ret = -3;
printf("LinkList_Insert error pos<0 pos = %d ret =%d \n", pos, ret);
return ret;
}
TLinkList *tempseqlist = NULL;
tempseqlist = (TLinkList *)list;
//work around ,假设length这时候为20,当pos>20,将pos变成20
//pos的值是从0开始,小于tempseqlist->capacity
if (pos > (tempseqlist->length) ) {
printf("LinkList_Insert wrokaround (pos > (tempseqlist->length ) pos = %d,tempseqlist->length = %d\n",
pos, tempseqlist->length);
pos = tempseqlist->length;
}
//正式插入数据,
int i = 0;
LinkListNode *curSeqListNode = &(tempseqlist->head);//让curSeqListNode 指向 链表的头部
for (int i = 0; i < pos; ++i) {
curSeqListNode = curSeqListNode->next;
}
node->next = curSeqListNode->next;
curSeqListNode->next = node;
tempseqlist->length++;
return ret;
}
//从LinkList 中删除指定位置的元素
//参数 pos
//返回值为 删除的元素
//返回NULL 表示出现了error
LinkListNode* LinkList_Delete(LinkList *list, int pos) {
int ret = 1;
LinkListNode *retSeqListNode = NULL;
if (list == NULL) {
ret = -1;
printf("LinkList_Delete error list==NULL ret = %d\n", ret);
return retSeqListNode;
}
if (pos < 0) {
ret = -2;
printf("LinkList_Delete error pos<0 pos = %d ret = %d\n", pos, ret);
return retSeqListNode;
}
TLinkList *tempseqlist = NULL;
tempseqlist = (TLinkList *)list;
if (pos > tempseqlist->length - 1) {
ret = -3;
printf("TLinkList_Delete error because (pos > tempseqlist->length-1) pos = %d tempseqlist->length = %d ret =%d \n",
pos, tempseqlist->length, ret);
return retSeqListNode;
}
//辅助指针变量curSeqListNode,指向的位置是链表的头部
LinkListNode *curSeqListNode = &(tempseqlist->head);//让curSeqListNode 指向 链表的头部
int i = 0;
for (i = 0; i < pos; ++i) {
curSeqListNode = curSeqListNode->next;
}
retSeqListNode = curSeqListNode->next; //先将要删除的节点缓存出来
curSeqListNode->next = retSeqListNode->next;// 删除的节点的next中保存着 “要删除元素的下一个元素”,让curseqlistnode->next 指向
tempseqlist->length--;
return retSeqListNode;
}
4.上层调用
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "iostream"
using namespace std;
extern "C" {
#include "002linklist.h"
}
typedef struct Teacher {
LinkListNode linklistnode;
int age;
char name[128];
char *othername;
char **stuname; //一个老师下面有5个学生
}Teacher;
int main() {
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);//程序退出时检测内存泄漏并显示到“输出”窗口
int ret = 0;
LinkList* linklist = NULL;
// 初始化,建立一个空的线性表
linklist = LinkList_Create();
if (linklist == NULL) {
ret = -1;
printf("LinkList_Create() func error ret =%d\n", ret);
return ret;
}
ret = LinkList_Length(linklist);
if (ret < 0) {
ret = -2;
printf("LinkList_Length(linklist) func error ret =%d \n", ret);
return ret;
}
printf("LinkList_Length(linklist) ret =%d \n", ret);
//给seqlist中指定位置插入数据,
//参数seqlistnode为要插入的数据
//参数 pos 为要插入的位置
//如果线性表中还有空间,但是指定的pos位置是大于 现在的length
//例如 线性表length为20,但是pos的值是50
//我们这里做work around,就会将数据插入到21的位置
//成功返回1
//失败 返回<=0
Teacher tea1;
tea1.age = 111;
strcpy(tea1.name, (const char*)"zhangsan");
tea1.othername = (char *)malloc(sizeof(char) * 128);
memset(tea1.othername, 0, sizeof(char) * 128);
strcpy(tea1.othername, (const char*)"zhangsanothername");
tea1.stuname = (char **)malloc(sizeof(char *) * 5);
memset(tea1.stuname, 0, sizeof(char *) * 5);
for (size_t i = 0; i < 5; i++)
{
tea1.stuname[i] = (char *)malloc(sizeof(char) * 128);//每个学生名字也有128个字符
memset(tea1.stuname[i], 0, sizeof(char) * 128);
sprintf(tea1.stuname[i], "zhangsanstuname%d", i + 1);
}
Teacher tea2;
tea2.age = 222;
strcpy(tea2.name, (const char*)"lisi");
tea2.othername = (char *)malloc(sizeof(char) * 128);
memset(tea2.othername, 0, sizeof(char) * 128);
strcpy(tea2.othername, (const char*)"lisiothername");
tea2.stuname = (char **)malloc(sizeof(char *) * 5);
memset(tea2.stuname, 0, sizeof(char *) * 5);
for (size_t i = 0; i < 5; i++)
{
tea2.stuname[i] = (char *)malloc(sizeof(char) * 128);//每个学生名字也有128个字符
memset(tea2.stuname[i], 0, sizeof(char) * 128);
sprintf(tea2.stuname[i], "lisistuname%d", i + 1);
}
Teacher tea3;
tea3.age = 333;
strcpy(tea3.name, (const char*)"wangwu");
tea3.othername = (char *)malloc(sizeof(char) * 128);
memset(tea3.othername, 0, sizeof(char) * 128);
strcpy(tea3.othername, (const char*)"wagnwuothername");
tea3.stuname = (char **)malloc(sizeof(char *) * 5);
memset(tea3.stuname, 0, sizeof(char *) * 5);
for (size_t i = 0; i < 5; i++)
{
tea3.stuname[i] = (char *)malloc(sizeof(char) * 128);//每个学生名字也有128个字符
memset(tea3.stuname[i], 0, sizeof(char) * 128);
sprintf(tea3.stuname[i], "wangwustuname%d", i + 1);
}
ret = LinkList_Insert(linklist, (LinkListNode*)&tea1, 0);
if (ret < 0) {
printf("LinkList_Insert(seqlist, &tea1, 0) func error ret =%d \n", ret);
return ret;
}
ret = LinkList_Insert(linklist, (LinkListNode*)&tea2, 0);
if (ret < 0) {
printf("LinkList_Insert(seqlist, &tea1, 0) func error ret =%d \n", ret);
return ret;
}
ret = LinkList_Insert(linklist, (LinkListNode*)&tea3, 0);
if (ret < 0) {
printf("LinkList_Insert(seqlist, &tea1, 0) func error ret =%d \n", ret);
return ret;
}
// 返回线性表List存在的元素个数
//返回值 >=0 表示:该list存在的元素个数
//<0 表示error
int seqlistlength = LinkList_Length(linklist);
if (seqlistlength < 0) {
ret = seqlistlength;
printf("LinkList_Length(seqlist) func error ret =%d \n", ret);
return ret;
}
printf("linklistlength = %d\n", seqlistlength);
//从seqlist 中获取指定位置的数据
//参数pos:seqlist中的位置
//返回值:为存储在该位置的元素
//返回NULL 表示有问题
for (int i = 0; i < LinkList_Length(linklist); i++)
{
Teacher* temptea = (Teacher *)LinkList_Get(linklist, i);
if (temptea == NULL) {
printf("can not get find teacher from pos = %d\n", i);
}
printf("temptea->age = %d,temptea->name = %s,temptea->othername=%s\n",
temptea->age,
temptea->name,
temptea->othername);
for (size_t j = 0; j < 5; j++)
{
printf("temptea->stuname[%d] = %s, ",
j, temptea->stuname[j]);
}
printf("\n");
}
//从seqlist 中删除指定位置的元素
//参数 pos
//返回值为 删除的元素
//返回NULL 表示出现了error
while (LinkList_Length(linklist) > 0) {
Teacher* deltea = (Teacher *)LinkList_Delete(linklist, 0);
if (deltea == NULL) {
printf("delete teacher from 0 error \n");
break;
}
printf("deltea->age = %d,deltea->name = %s,deltea->othername=%s\n",
deltea->age,
deltea->name,
deltea->othername);
if (deltea->stuname != NULL) {
for (size_t i = 0; i < 5; i++)
{
if (deltea->stuname[i] != NULL) {
printf("deltea->stuname[%d] = %s, ",
i, deltea->stuname[i]);
}
}
cout << endl;
}
if (deltea->stuname != NULL) {
for (size_t i = 0; i < 5; i++)
{
free(deltea->stuname[i]);
deltea->stuname[i] = NULL;
}
free(deltea->stuname);
deltea->stuname = NULL;
}
free(deltea->othername);
deltea->othername = NULL;
printf("\n");
}
//清空seqlist
//返回值为1,表示成功。
//返回值为-1,表示失败。
ret = LinkList_Clear(linklist);
if (ret < 0) {
printf("LinkList_Clear(seqlist) func error ret =%d \n", ret);
return ret;
}
//销毁该线性表
//返回值为1,表示成功。
//返回值为-1,表示失败。
ret = LinkList_Destory(linklist);
if (ret < 0) {
printf("LinkList_Destory(seqlist) func error ret =%d \n", ret);
return ret;
}
return 0;
}