链表结构 及 实现无头单向非循环链表的多种增删查改操作代码

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/qq_44759710/article/details/101061606

链表概念

链表是一种物理存储结构上非连续、非顺序的存储结构,属于线性表的一种,其数据元素的逻辑顺序是通过链表中的指针接次序实现的

链表的分类

链表从结构上,可分为:

  • 单链表、双向链表
    在这里插入图片描述
  • 带头单链表、不带头单链表
    在这里插入图片描述
  • 循环单链表、非循环单链表
    在这里插入图片描述

综上:
我们可得 8 种链表的构造方法,接下来,我们将对单向无头非循环链表的构造方法及基本操作进行描述

首先要定义两个结构体,一个代表以 _head 为第一个结点的结构体,另一个结构体定义每个结点包含的元素:_next 即指向下一个元素的指针,_data 即该节点数据

typedef int DataType;
typedef struct SingleListNode
{
	DataType _data;
	struct SingleListNode* _next;
}SingleListNode;

typedef struct SingleList
{
	SingleListNode* _head;
}SingleList;

链表的增删查改操作

一 . 增删查改前期步骤

  • 建初始化函数
    给链表的第一个结点赋空即可
void SingleListInit(SingleList* sl)
{
	//next, data
	assert(sl);
	sl->_head = NULL;
}
  • 建创建结点的函数
    链表特点为插入一个数据,则开辟一个结点,开辟步骤为:
    传入结点数值 x ,先为新结点 newNode 开辟对应空间,使新结点数值为 x ,新结点的 next 指向空
SingleList* CreatNode(DataType x)
{
	SingleListNode* newNode = (SingleListNode*)malloc(sizeof(SingleListNode));
	newNode->_data = x;
	newNode->_next = NULL;
}
  • 建销毁函数
    使 cur 为链表的第一个结点,next 为 cur 的下一个结点,释放 cur ,再循环依次释放链表元素,再使 _head 指向空
void SingleListDestroy(SingleList* sl)
{
	assert(sl);
	SingleListNode* cur = sl->_head;
	while (cur)
	{
		SingleListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	sl->_head = NULL;
}
  • 输出顺序表
void SingleListPrint(SingleList* sl)
{
	assert(sl);
	SingleListNode* cur = sl->_head;
	while (cur)
	{
		printf("%d->", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}

二 . 增加数据操作

  1. 在尾部插入数据
    如果链表为空,则只需将插入元素赋给 _head 即可,其他情况时,应遍历找到最后一个结点,再最后一个结点后创建新结点
void SingleListPushBack(SingleList* sl, DataType x)
{
	assert(sl);
	//空链表
	if (sl->_head == NULL)
	{
		sl->_head = CreatNode(x);
	}
	//遍历链表
	else
	{
		SingleListNode* cur = sl->_head;
		while (cur->_next)
		{
			cur = cur->_next;
		}
		//找到最后一个结点
		cur->_next = CreatNode(x);
	}
}
  1. 在头部插入数据
    使 cur 为第一个元素直接在 cur 前创建新结点即可,使新结点的 _next 指向 cur
void SingleListPushFront(SingleList* sl,DataType x)
{
	assert(sl);
	SingleListNode* cur = sl->_head;
	SingleListNode* newNode = CreatNode(x);
	newNode->_next = cur;
}
  1. 在任意数据前实现插入操作
    传入插入元素 src 及数据 x 使得在 x 前插入元素 src ,首先新建 src 的结点,如果第一个结点值为 x ,则进行头插操作,其他情况先找到数值为 src的结点 cur ,再使 newNode 及 cur 的 next 更改指向
void SingleListInsertFront(SingleList* sl, DataType x, DataType src)
{
	assert(sl);
	SingleListNode * cur;
	SingleListNode * newNode = CreatNode(src);

	if (sl->_head->_data == x)
	{
		SingleListPushFront(sl, src);
		return;
	}
	for (cur = sl->_head; cur->_next; cur = cur->_next)
	{
		if (cur->_next->_data == x)
		{
			break;
		}
	}
	newNode->_next = cur->_next;
	cur->_next = newNode;
}
  1. 在任意位置后实现插入操作
    传入插入位置 pos,创建新结点,使 pos 的 next 指向新结点,新结点的 next 指向原先的 next
    另:若想在 pos 前插入数据,可以先实现上述步骤,再将 pos 与新结点位置交换
void SingleListInsertAfter(SingleListNode* pos, DataType x)
{
	assert(pos);
	SingleListNode* newNode = CreatNode(x);
	SingleListNode* next = pos->_next;
	pos->_next = newNode;
	newNode->_next = next;
}

三 . 删除数据操作

  1. 在尾部删除数据
    首先遍历出尾部的结点,释放尾部节点,更改 prev 的指向,若删除为第一个结点,则使 _head 为空即可
void SingleListPopBack(SingleList* sl)
{
	assert(sl);
	SingleListNode* cur = sl->_head;
	SingleListNode* prev = NULL;
	while (cur->_next)
	{
		prev = cur;
		cur = cur->_next;
	}
	free(cur);
	cur = NULL;
	if (prev == NULL)
	{
		//删除的是头节点
		sl->_head = NULL;
	}
	else
	{
		prev->_next = NULL; 
	}
}
  1. 在头部删除数据
    清空第一个元素,更改 _head,如果链表为空,则直接返回
void SingleListPopFront(SingleList* sl)
{
	assert(sl);
	SingleList *next, *cur;
	if (sl->_head == NULL)
		return;
	cur = sl->_head;
	next = sl->_head->_next;
	free(cur);
	cur = NULL;
	sl->_head = next;
}
  1. 在任意位置实现删除操作
    传入插入位置 pos,删除在后面的元素,先判断 pos 是否存在 _next,重新链接 pos 的 _next及新元素的 _next
void SingleListEraseAfter(SingleListNode* pos)
{
	assert(pos);
	SingleListNode* next = pos->_next;
	if (pos->_next == NULL)
		return;
	else
	{
		SingleListNode* next2 = next->_next;
		pos->_next = next2;
		free(next);
		next == NULL;
	}
}

四 . 查找数据操作

  1. 一般查找元素位置操作
    挨个查找元素,若找到,则返回结点,否则返回空
SingleListNode* SingleListFind(SingleList* sl, DataType x)
{
	assert(sl);
	SingleListNode * cur;
	for (cur = sl->_head; cur; cur = cur->_next)
	{
		if (cur->_data == x)
		{
			return cur;
		}
	}
	return NULL;
}

上述即为几种关于该种链表的基本增删查改操作的代码思想,关于单链表还涉及很多知识点和题目,十分重要,之后的博客会分享一些常见的关于单链表的进阶问题

猜你喜欢

转载自blog.csdn.net/qq_44759710/article/details/101061606