C++11 数据结构2 线性表的链式存储,实现,测试

线性表的链式存储 --单链表

前面我们写的线性表的顺序存储(动态数组)的案例,最大的缺点是插入和删除时需要移动大量元素,这显然需要耗费时间,能不能想办法解决呢?链表。

链表为了表示每个数据元素与其直接后继元素之间的逻辑关系,每个元素除了存储本身的信息外,还需要存储指示其直接后继的信息。

线性表的链式存储结构中,每个节点中只包含一个指针域,这样的链表叫单链表。

一 单链表中任意一个节点的 数据模型

通过一组任意的存储单元来存储线性表中的数据元素。每给元素存放元素自身的信息外,还需要存放一个指向其后继的指针,其中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;
}

5.添加和删除代码图示

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值