特殊单项链表学习

1.特殊链表

针对于特殊的用户数据,在该链表中,节点只有指针域,没有数据域;用户数据预留4个节点的空间给节点使用就行,节点负责连接各个用户数据,不负责维护用户数据.

在这里插入图片描述

2.代码

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 节点结构体
struct LinkNode
{
	struct LinkNode* next; // 只有指针域,数据域由用户自己维护
};

// 链表结构体
struct LList
{
	struct LinkNode pHead;  // 链表头节点
	int m_size;  // 链表长度
};

typedef void* LinkList;

// 链表初始化
LinkList init_LinkList()
{
	// 为链表申请堆空间
	struct LList* myList = (struct LList*)malloc(sizeof(struct LList));
	if (myList == NULL)
	{
		printf("链表申请堆空间失败!\n");
		return NULL;
	}
	myList->m_size = 0;
	myList->pHead.next = NULL;

	return myList;
}

// 在链表中插入节点,用户会把数据地址传进来,所以用void *data1去接就行
void insert_LinkList(LinkList list1, int pos, void *data1)
{
	if (list1 == NULL)
	{
		return;
	}
	if (data1 == NULL)
	{
		return;
	}
	struct LList* myList = (struct LList*)list1;

	if (pos<0 || pos>myList->m_size)
	{
		// 插入位置无效就默认尾插
		pos = myList->m_size;
	}

	// 用户的数据包括一个 struct LinkNode节点和真正的数据
	// 需要把前四个字节地址取出来
	struct LinkNode* userNode = data1; // 类似于int a=0x11223344;  char *p = (char*)&a;
										// p取出了a的第一个字节空间地址

	// 找到待插入位置的前驱节点
	struct LinkNode* pCurrent = &myList->pHead;
	for (int i = 0; i < myList->m_size; i++)
	{
		pCurrent = pCurrent->next;
	}

	//从for循环退出之后pCurrent指向待插入位置的前驱节点
	// 更改链表节点的指针指向
	userNode->next = pCurrent->next;
	pCurrent->next = userNode;

	// 更新链表长度
	myList->m_size++;
}

// 遍历链表
void foreach_LinkList(LinkList list1, void (*userPrint)(void*))
{
	if (NULL == list1)
	{
		return;
	}

	// 把list1转换为真实链表
	struct LList* myList = (struct LList*)list1;

	// 定义一个临时节点指向链表用户数据首地址 
	struct LinkNode* pCurrent = myList->pHead.next;
	for (int i = 0; i < myList->m_size; i++)
	{
		// 节点是没有数据域的,所以让用户自己打印数据
		// pCurrent就是用户数据的首地址
		userPrint(pCurrent);
		pCurrent = pCurrent->next;
	}
}

// 按位置删除链表节点
void removeByPos_LinkList(LinkList list, int pos)
{
	if (list == NULL)
	{
		return;
	}

	// 底层程序猿知道用真实链表的成员属性
	struct LList* myList = (struct LList*)list;
	if (pos<0 || pos>myList->m_size - 1)
	{
		return;
	}

	// 1.先找到被删除位置的前驱节点
	struct LinkNode* pCurrent = &myList->pHead;
	for (int i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}
	// 跳出for循环的时候pCurrent指向被删除节点的前驱节点

	// 2.删除节点
	struct LinkNode* delNode = pCurrent->next;  // 记录被删节点
	pCurrent->next = delNode->next;		// 更改节点指针指向

	//free(delNode);  // 底层程序猿只维护节点指针域,数据是属于用户的,底层程序猿没有开辟数据域管理用户数据,
					 // 所以数据应该由用户解决,用户数据在堆区,则用户自己释放,在栈区,则系统释放
	//delNode = NULL;

	//3. 更新链表长度
	myList->m_size--;
}

// 销毁链表
void destory_LinkList(LinkList list1)
{
	if (NULL == list1)
	{
		return;
	}

	free(list1);  // 有多少个malloc,就对应多少个free
	list1 = NULL;
}
// 以下是用户代码

struct Person
{
	//struct LinkNode pNode; //容易被用户知道底层代码

	int *a;   // 用户只需要预留4字节给底层程序猿就行,所以int a,double *a等都可以
	char name[64];
	int age;
};

void personPrint(void* data)
{
	struct Person* data1 = data;
	printf("人物姓名:%8s, 年龄:%d\n", data1->name, data1->age);
}

void test01()
{
	// 链表初始化,用户只能调用底层程序猿提供的接口
	LinkList* list1 = init_LinkList();
	// list1->    // 用户没办法访问真实链表的属性

	struct Person p1 = { NULL, "百里守约", 6 };
	struct Person p2 = { NULL, "东皇太一", 22 };
	struct Person p3 = { NULL,  "炸弹猫", 18 };
	struct Person p4 = { NULL, "孙悟空", 500 };
	struct Person p5 = { NULL,  "凯皇", 33 };
	struct Person p6 = { NULL,  "裁判", 40 };

	insert_LinkList(list1, 0, &p1);
	insert_LinkList(list1, 0, &p2);
	insert_LinkList(list1, 1, &p3);
	insert_LinkList(list1, -1, &p4);
	insert_LinkList(list1, 1, &p5);
	insert_LinkList(list1, -1, &p6);

	foreach_LinkList(list1, personPrint);
}


int main() {

	test01();

	system("pause");
	return EXIT_SUCCESS;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值