前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >线性表之链表---初始化,插入,删除

线性表之链表---初始化,插入,删除

作者头像
大忽悠爱学习
发布2021-03-04 10:39:35
4370
发布2021-03-04 10:39:35
举报
文章被收录于专栏:c++与qt学习

与之前链表不同,这里的链表可以适合不同的数据类型存储

插入也有两种写法

写法1:注意curNode指针的移动位置,不要让他在等于curNode=NULL后还要插入数据

写法2:

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//节点结构体
struct LinkNode {
	void* data;//万能指针接收用户输入的不同类型数据
	LinkNode* next;//指针域
};
//链表结构体
struct LList {
	LinkNode pHeader;//头节点结构体
	int size;//链表长度
};
//取个别名
typedef  void* LinkList;
//链表的初始化
LinkList init_LinkList()
{
	LList *llist=(LList*)malloc(sizeof(LList));
	if (llist == NULL)
	{
		return NULL;
	}
	//头节点初始化
	llist->pHeader.next = NULL;
	llist->pHeader.data = NULL;
	llist->size = 0;
	return llist;//返回的是链表结构体,void*指针可以接收任何类型指针的地址
}
//插入           第一个参数相当于void* list,这样用户就无法修改堆区结构体数据

//这边如果不用两个指针一前一后移动来插入,要注意curNode指针的移动位置,不要让其移到空位
void insert_LinkList(LinkList list,int pos,void* data)
{
	if (list == NULL)
		return;
	if (data == NULL)
		return;
	//这里要把用户传进的void*数据变为原来的LList数据类型
	LList* mylist =(LList*)list;
	if (pos<0 || pos>mylist->size)
	{
		//强制尾插
		pos = mylist->size;
	}
	LinkNode* curNode = &mylist->pHeader;//这里的头节点是结构体不是指针
	for (int i = 0; i < pos; i++)
	{
		//先把当前节点指针移到该插入的节点
		curNode = curNode->next;
	}
	//curNode是要插入节点的前驱
	LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	newNode->next = curNode->next;
	curNode->next = newNode;


	//更新链表长度
	mylist->size++;
}
//遍历链表
void foreach_LinkList(LinkList list,void(*myprint)(void*))
{
	if (list == NULL)
	{
		return;
	}
	if (myprint == NULL)
	{
		return;
	}
	LList *mylist = (LList*)list;
	LinkNode* curNode =mylist->pHeader.next;
	while (curNode)
	{
		//如何打印交给用户决定
		myprint(curNode->data);
		curNode = curNode->next;
	}
}
void print(void* val)
{
	int* num = (int*)val;
	printf("%d\n", *num);
}
int main()
{
	//用户只能在main函数里面拿到一个void*指针,该指针指向堆区开辟的链表结构体
	//但用户无法知晓void*指向的堆区开辟内存里面存放的数据类型,也就无法通过强制类型转换对堆区的链表结构体数据进行修改
	LinkList list = init_LinkList();
	int a = 5;
	int b = 10;
	int c = 20;
	insert_LinkList(list, 0,&a);
	insert_LinkList(list, 0, &b);
	insert_LinkList(list, 0, &c);
	foreach_LinkList(list, print);
	return 0;
}

删除

两种方法

方法2:两个节点前后移动

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
//节点结构体
struct LinkNode {
	void* data;//万能指针接收用户输入的不同类型数据
	LinkNode* next;//指针域
};
//链表结构体
struct LList {
	LinkNode pHeader;//头节点结构体
	int size;//链表长度
};
//取个别名
typedef  void* LinkList;
//链表的初始化
LinkList init_LinkList()
{
	LList *llist=(LList*)malloc(sizeof(LList));
	if (llist == NULL)
	{
		return NULL;
	}
	//头节点初始化
	llist->pHeader.next = NULL;
	llist->pHeader.data = NULL;
	llist->size = 0;
	return llist;//返回的是链表结构体,void*指针可以接收任何类型指针的地址
}
//插入           第一个参数相当于void* list,这样用户就无法修改堆区结构体数据

//这边如果不用两个指针一前一后移动来插入,要注意curNode指针的移动位置,不要让其移到空位
void insert_LinkList(LinkList list,int pos,void* data)
{
	if (list == NULL)
		return;
	if (data == NULL)
		return;
	//这里要把用户传进的void*数据变为原来的LList数据类型
	LList* mylist =(LList*)list;
	if (pos<0 || pos>mylist->size)
	{
		//强制尾插
		pos = mylist->size;
	}
	LinkNode* curNode = &mylist->pHeader;//这里的头节点是结构体不是指针
	for (int i = 0; i < pos; i++)
	{
		//先把当前节点指针移到该插入的节点
		curNode = curNode->next;
	}
	//curNode是要插入节点的前驱
	LinkNode* newNode = (LinkNode*)malloc(sizeof(LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	newNode->next = curNode->next;
	curNode->next = newNode;


	//更新链表长度
	mylist->size++;
}
//遍历链表
void foreach_LinkList(LinkList list,void(*myprint)(void*))
{
	if (list == NULL)
	{
		return;
	}
	if (myprint == NULL)
	{
		return;
	}
	LList *mylist = (LList*)list;
	LinkNode* curNode =mylist->pHeader.next;
	while (curNode)
	{
		//如何打印交给用户决定
		myprint(curNode->data);
		curNode = curNode->next;
	}
}
//删除指定位置的节点
void del_LinkList(LinkList mylist, int pos)
{
	if (mylist == NULL)
	{
		return;
	}
	LList *list = (LList*)mylist;
	if (pos<0 || pos>list->size)
	{
		return;
	}
	//找到插入节点的前驱节点
	LinkNode* curNode = &list->pHeader;
	//先把当前节点移到要删除节点的前面(前驱)
	for (int i = 0; i < pos; i++)
	{
		curNode = curNode->next;
	}
	//记录要删除的节点
	LinkNode* delNode = curNode->next;
	//重新建立关系
	curNode->next = delNode->next;
	//释放删除的节点
	free(delNode);
	delNode = NULL;
	//更新链表的长度
	list->size--;
}
//按照值来删除
void del_LinkList(LinkList mylist,void* data,int(*compare)(void*,void*))
{
	if (mylist == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	LList* list = (LList*)mylist;
	LinkNode* prveNode = &list->pHeader;
	LinkNode* curNode = list->pHeader.next;
	//找到data所在的节点
	while (curNode)
	{
		//找到要删除的节点
		if (compare(curNode->data,data))
		{
			printf("找到要删除的元素\n");
			break;
		}
		//辅助指针往后移动
		prveNode = curNode;
		curNode = curNode->next;
	}
	//如果不为空,表示找到了要删除的节点
	if (curNode!=NULL)
	{
		prveNode->next = curNode->next;
		free(curNode);
		curNode = NULL;
		list->size--;
	}
	printf("未找到该元素\n");

}
void print(void* val)
{
	int* num = (int*)val;
	printf("%d ", *num);
}
int compare(void* v1, void* v2)
{
	int* num1 = (int*)v1;
	int* num2 = (int*)v2;
	if (v1 == v2)
	{
		return 1;
	}
	return 0;
}
int main()
{
	//用户只能在main函数里面拿到一个void*指针,该指针指向堆区开辟的链表结构体
	//但用户无法知晓void*指向的堆区开辟内存里面存放的数据类型,也就无法通过强制类型转换对堆区的链表结构体数据进行修改
	LinkList list = init_LinkList();
	int a = 5;
	int b = 10;
	int c = 20;
	int d = 30;
	insert_LinkList(list, 0,&a);
	insert_LinkList(list, 0, &b);
	insert_LinkList(list, 0, &c);
	printf("打印链表结果如下:\n");
	foreach_LinkList(list, print);
	printf("\n按值删除链表后:\n");
	del_LinkList(list, &a, compare);
	foreach_LinkList(list, print);
	printf("\n按位置删除链表后:\n");
	del_LinkList(list, 0);
	foreach_LinkList(list, print);
	return 0;
}
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/02/28 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 插入也有两种写法
  • 删除
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档