数据结构-3.单向链表的实现

节点结构体设计

struct LinkNode
{
	// 数据域
	void* data;
	// 指针域
	struct LinkNode * next;
};
  • data:一个 void* 类型的指针,指向节点存储的数据。使用 void* 是为了链表能够存储不同类型的数据。
  • next:一个指向下一个 LinkNode 结构体的指针,形成链表的链接。

链表结构体设计

struct LList
{
	//头节点
	struct LinkNode pHeader;
	//链表长度
	int m_size;
};
  • pHeader:链表的头节点。虽然 pHeader 本身也是 LinkNode 类型,但它可以作为链表的起始节点,其 next 指针指向第一个实际的数据节点。
  • m_size:一个整数,表示链表中节点的数量。

初始化链表

LinkList init_LinkList()
{
	struct LList* myList = malloc(sizeof(struct LList));
	
	if (myList == NULL)
	{
		return NULL;
	}

	myList->pHeader.data = NULL;
	myList->pHeader.next = NULL;
	myList->m_size = 0;

	return myList;
}
  • 使用 malloc 分配 struct LList 的内存。
  • 初始化头节点的 data 指针为 NULLnext 指针也为 NULL
  • 设置链表长度 m_size0

插入链表

void insert_LinkList(LinkList list, int pos, void* data)
{
	if (list == NULL) 
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	// 将list还原成struct LList数据类型
	struct LList * myList = list;

	if (pos <0 || pos > myList->m_size)
	{
		//位置无效 强制尾插
		pos = myList->m_size;
	}
	//找到插入节点的前驱
	struct LinkNode * pCurrent = &myList->pHeader;
	for (int i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}
	//创建新节点
	struct LinkNode* newNode = malloc(sizeof(struct LinkNode));
	newNode->data = data;
	newNode->next = NULL;

	//建立节点关系

	newNode->next = pCurrent->next;
	pCurrent->next = newNode;

	//更新链表长度
	myList->m_size++;
}
  • 检查 listdata 是否为空,若为空则返回。
  • 如果位置 pos 无效(负数或超出链表当前大小),将位置设置为链表末尾。
  • 通过遍历找到插入位置的前驱节点 pCurrent
  • 创建新节点并插入链表中。
  • 更新链表长度 m_size

遍历链表

void foreach_linkList(LinkList list,void(*myForeach)(void *))
{
	if (list == NULL)
	{
		return;
	}

	struct LList* mylist = list;

	struct LinkNode* pCurrent = mylist->pHeader.next;
	for (int i = 0; i < mylist->m_size; i++)
	{
		myForeach(pCurrent->data);
		pCurrent = pCurrent->next;
	}
}
  • 检查 list 是否为空,若为空则返回。
  • 使用 pCurrent 遍历链表,从头节点的下一个节点开始。
  • 对每个节点的数据调用 myForeach,然后移动到下一个节点。

按位置删除链表数据

void removeByPos_LinkList(LinkList list, int pos)
{
	if (list == NULL)
	{
		return;
	}

	struct LList* mylist = list;



	if (pos <0 || pos > mylist->m_size-1)
	{
		return;
	}

	// 找到要删除节点的前驱节点
	struct LinkNode* pCurrent = &mylist->pHeader;
	for (int i = 0; i < pos; i++)
	{
		pCurrent = pCurrent->next;
	}

	// 记录待删除的节点
	struct LinkNode* pDel = pCurrent->next;

	//重新建立节点关系
	pCurrent->next = pDel->next;

	//释放删除节点
	free(pDel);
	pDel = NULL;

	// 更新链表长度
	mylist->m_size--;
}
  • 检查链表是否为空,如果为空,直接返回。
  • 检查pos是否有效(是否在范围0到m_size-1之间),如果无效,直接返回。
  • 遍历链表,找到位置为pos的节点的前驱节点。
  • 记录待删除的节点,并重新建立前驱节点和待删除节点之后的节点之间的链接。
  • 释放待删除节点的内存,将其指针置为NULL
  • 更新链表长度

按照值删除链表数据

int myComparePerson(void* data1, void* data2)
{
	struct Person* p1 = data1;
	struct Person* p2 = data2;

	return strcmp(p1->name,p2->name) == 0 && p1->age == p2->age;
}

void removeByValue_LinkList(LinkList List, void* data, int(*myComparePerson)(void * , void *))
{
	if (List == NULL)
	{
		return;
	}
	if (data == NULL)
	{
		return;
	}
	
	struct LList* mylist = List;
	//创建两个辅助指针
	struct LinkNode* pPrev = &mylist->pHeader;
	struct LinkNode* pCurrent = pPrev->next;
	for (int i = 0; i < mylist->m_size; i++)
	{
		//将两个指针的比较交给用户
		if (myComparePerson(pCurrent->data, data))
		{
			pPrev->next = pCurrent->next;
			free(pCurrent);
			pCurrent = NULL;

			mylist->m_size--;
			break;
		}

		//辅助指针后移
		pPrev = pCurrent;
		pCurrent = pCurrent->next;

	}
}
  • 检查链表和数据是否为空,如果为空,直接返回。
  • 初始化两个指针,pPrev指向当前节点的前一个节点,pCurrent指向当前节点。
  • 遍历链表,通过用户提供的比较函数匹配节点数据。
  • 如果找到匹配节点,调整前驱节点的next指针,绕过当前节点。
  • 释放匹配的节点内存,更新链表长度,结束循环。

清空链表


void clear_LinkList(LinkList list)
{
	if (list == NULL)
	{
		return;
	}
	struct LList* mylist = list;

	struct LinkNode* pCurrent = mylist->pHeader.next;

	for (int i = 0; i < mylist->m_size; i++)
	{
		struct LinkNode* pNext = pCurrent->next;

		free(pCurrent);

		pCurrent = pNext;

	}

	mylist->pHeader.next == NULL;
	mylist->m_size = 0;
}
  • 检查链表是否为空,如果为空,直接返回。
  • 遍历链表,从头节点开始,逐个删除所有节点。
  • 每次删除节点后,更新指针指向下一个节点,直到删除完所有节点。
  • 清空链表后,将链表头节点的next指针置为NULL,并将链表长度设为0。

返回链表长度

int size_LinkList(LinkList list)
{
	if (list == NULL)
	{
		return -1;
	}
	struct LList* mylist = list;
	return mylist->m_size;
}
  • 检查链表是否为空,如果为空,返回-1。
  • 如果链表非空,返回链表的m_size成员,该成员保存了链表的节点数量。

销毁链表

void destroy_linkList(LinkList list)
{
	if (list == NULL)
	{
		return;
	}
	//清空链表
	clear_LinkList(list);

	free(list);

	list = NULL;

}
  • 首先调用clear_LinkList函数,删除链表中的所有节点。
  • 释放链表本身的内存。
  • 将链表指针置为NULL,防止悬挂指针的使用。
posted @ 2024-08-05 15:26  ffff5  阅读(18)  评论(0编辑  收藏  举报