数据结构《二》链表的实现

Posted AURORA_CODE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构《二》链表的实现相关的知识,希望对你有一定的参考价值。


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

一、单向无头链表的实现

1.功能实现

01 struct SListNode

typedef int SLTDateType;
typedef struct SListNode
{
	SLTDateType data;
	struct SListNode* next;
}SListNode;

02 动态申请新结点

SListNode* BuySListNode(SLTDateType x) 
{
	SListNode *node = (SListNode*)malloc(sizeof(SListNode));
	if (NULL == node)
	{
		printf("BUYSListNode Error!");
		return NULL;
	}
	node->data = x;
	node->next = NULL;
	return node;
}

03 无头单向链表的打印

void SListPrint(SListNode* plist)
{
	SListNode *current = plist;
	while (current!=NULL)
	{
		printf("%d --> ", current->data);
		current = current->next;
	}
	printf("NULL\\n");
}

04 无头单向链表的销毁

void SListDestory(SListNode** pplist)
{
	SListNode *node = NULL;
	if (NULL == *pplist)
	{
		return;
	}
	while (NULL!=(*pplist))
	{
		node = *pplist;
		(*pplist) = (*pplist)->next;
		free(node);
	}
}

05 无头单向链表的头插头删

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	assert(pplist != NULL);
	SListNode *node = BuySListNode(x);
	node->next = *pplist;
	*pplist=node;
}

void SListPopFront(SListNode** pplist)
{
	SListNode *current = NULL;
	assert(pplist != NULL);
	if (NULL==*pplist)
	{
		return;
	}
	else
	{
		current = *pplist;
		*pplist = (*pplist)->next;
		free(current);
	}
}

06 无头单向链表的尾插尾删

void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist != NULL);
	SListNode *current = *pplist;
	if (*pplist==NULL)
	{
		*pplist = BuySListNode(x);
	}
	else
	{
		while (current->next)
		{
			current = current->next;
		}
		current->next = BuySListNode(x);
	}
}

void SListPopBack(SListNode** pplist)
{
	assert(pplist != NULL);
	SListNode *current = *pplist;
	if (*pplist == NULL)
	{
		return;
	}
	else if (NULL==(*pplist)->next)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		while (current->next->next)
		{
			current = current->next;
		}
		free(current->next);
		current->next = NULL;
	}
}

07 无头单向链表的查找

SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode *current = plist;
	while (current)
	{
		if (x==current->data)
		{
			return current;
		}
		current = current->next;
	}
	return NULL;
}

08 无头单向链表的按位插入

// 单链表在pos位置之后插入x
void SListInsertAfter(SListNode* pos, SLTDateType x)
{

	SListNode *node = NULL;
	if (NULL == pos)
	{
		printf("POS ERROR\\n");
		return;
	}
	node = BuySListNode(x);
	node->next = pos->next;
	pos->next = node;
}

09 无头单向链表的按位删除

// 单链表在pos位置之后删除
void SListEraseAfter(SListNode* pos)
{
	SListNode *node = NULL;
	if (NULL == pos)
	{
		printf("POS ERROR\\n");
		return;
	}
	node = pos->next;
	pos->next = node->next;
	free(node);
}

2. 测试代码

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include"slist.h"
void test_Back()
{
	SListNode *head = NULL;
	printf("尾插测试\\n");
	SListPushBack(&head, 1);
	SListPrint(head);
	SListPushBack(&head, 2);
	SListPrint(head);
	SListPushBack(&head, 3);
	SListPrint(head);
	SListPushBack(&head, 4);
	SListPrint(head);
	SListPushBack(&head, 5);
	SListPrint(head);
	printf("尾删测试\\n");
	SListPopBack(&head);
	SListPrint(head);
	SListPopBack(&head);
	SListPrint(head);
	SListPopBack(&head);
	SListPrint(head);
	SListPopBack(&head);
	SListPrint(head);
	SListPopBack(&head);
	SListPrint(head);
	SListDestory(&head);
}
void test_Front()
{
	SListNode *head = NULL;
	printf("头插测试\\n");
	SListPushFront(&head, 1);
	SListPrint(head);
	SListPushFront(&head, 2);
	SListPrint(head);
	SListPushFront(&head, 3);
	SListPrint(head);
	SListPushFront(&head, 4);
	SListPrint(head);
	SListPushFront(&head, 5);
	SListPrint(head);
	printf("头删测试\\n");
	SListPopFront(&head);
	SListPrint(head);
	SListPopFront(&head);
	SListPrint(head);
	SListPopFront(&head);
	SListPrint(head);
	SListPopFront(&head);
	SListPrint(head);
	SListPopFront(&head);
	SListPrint(head);
	SListDestory(&head);
}
void test()
{
	SListNode *head = NULL;
	SListPushFront(&head, 1);
	SListPushFront(&head, 2);
	SListPushFront(&head, 3);
	SListPushFront(&head, 4);
	SListPushFront(&head, 5);
	SListPushFront(&head, 6);
	SListPushFront(&head, 7);
	SListPushFront(&head, 8);
	SListPushFront(&head, 9);
	SListPrint(head);
	printf("指定位置之后删除测试:\\n");
	SListEraseAfter(SListFind(head, 7));
	SListPrint(head);
	printf("指定位置之后插入测试:\\n");
	SListInsertAfter(SListFind(head, 7), 6);
	SListPrint(head);
	SListDestory(&head);
}
int main()
{
	//test_Back();
	//test_Front();
	test();
	return 0;
}

二、双向带头循环链表的实现

1. 功能实现

01 struct LinkNode

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType _data;
	struct ListNode* _next;
	struct ListNode* _prev;
}ListNode;

02 动态申请链表新结点

ListNode* ListCreate(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (NULL == node)
	{
		assert(0);
	}
	node->_data = x;
	node->_prev = NULL;
	node->_next = NULL;
	return node;
}

03 双向带头循环链表的初始化

ListNode* ListInit()
{
	ListNode* pHead = ListCreate(0);
	pHead->_next = pHead;
	pHead->_prev = pHead;
	return pHead;
}

04 双向带头循环链表的销毁

void ListDestory(ListNode* pHead)
{
	assert(pHead);
	while (pHead->_next != pHead)
	{
		ListErase(pHead->_next);
	}
	free(pHead);
	pHead = NULL;
}

05 双向带头循环链表的打印

void ListPrint(ListNode* pHead)
{
	assert(pHead);
	ListNode* pcurrent = pHead->_next;
	printf("head --> ");
	while (pHead != pcurrent)
	{
		printf("%d --> ", pcurrent->_data);
		pcurrent = pcurrent->_next;
	}
	printf("head\\n");
}

06 双向带头循环链表的查找

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* pcurrent = pHead->_next;
	while (pcurrent != pHead)
	{
		if (x == pcurrent->_data)
			return pcurrent;
		pcurrent = pcurrent->_next;
	}
	return NULL;
}

07 双向带头循环链表的按位插入

void ListInsert(ListNode* pos, LTDataType x)
{
	if (NULL == pos)
	{
		return;
	}
	ListNode* newnode = ListCreate(x);
	newnode->_prev = pos->_prev;
	newnode->_next = pos;
	pos->_prev->_next = newnode;
	pos->_prev = newnode;
}

08 双向带头循环链表的按位删除

void ListErase(ListNode* pos)
{
	if (NULL == pos)
	{
		return;
	}
	pos->_prev->_next = pos->_next;
	pos->_next->_prev = pos->_prev;
	free(pos);
	pos = NULL;
}

09 双向带头循环链表的头插头删

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListInsert(pHead->_next, x);
}
// 双向链表头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	ListErase(pHead->_next);
}

10 双向带头循环链表的尾插尾删

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListInsert(pHead, x);
}
// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	ListErase(pHead->_prev);
}

2. 测试代码

#include"linklist.h"
int main()
{
	ListNode *head = ListInit();
	//先头插5个元素1,2,3,4,5
	printf("先将1,2,3,4,5头插入链表得:\\n");
	ListPushFront(head, 1);
	ListPushFront(head, 2);
	ListPushFront(head, 3);
	ListPushFront(head, 4);
	ListPushFront(head, 5);
	ListPrint(head);
	printf("再将6,7,8尾插入链表得:\\n");
	//再尾插3个元素6,7,8
	ListPushBack(head, 6);
	ListPushBack(head, 7);
	ListPushBack(head, 8);
	ListPrint(head);
	//头删1个结点
	printf("头删一个节点得:\\n");
	ListPopFront(head);
	ListPrint(head);
	printf("尾删一个节点得:\\n");
	ListPopBack(head);
	ListPrint(head);
	printf("找出值等于2的节点并删除:\\n");
	ListErase(ListFind(head, 2));
	ListPrint(head);
	printf("在值等于1的元素前插入888:\\n");
	ListInsert(ListFind(head, 1), 888);
	ListPrint(head);
	ListDestory(head);
}

以上是我对一些比较常见的链表的实现。如有错误,希望大佬指正。
同时,经过对顺序表和链表的学习,我对链表和顺序表的异同以及优缺点做了总结,在此分享出来一同探讨。

角度顺序表链表
存储存储空间连续存储空间不一定连续
随机插入/删除搬移元素 O(n)不需要搬移元素,直接修改指针指向 O(1)
随机访问支持 O(1)不支持 O(n)
获取下一个元素的方式对下标 + +/- -next/prev
插入需要考虑容量,是否需要扩容不需要考虑容量
空间扩容时才需要再次申请空间频繁向堆申请小的内存块
应用场景元素高效存储+频繁访问任意位置频繁地插入删除
缓存利用率

以上是关于数据结构《二》链表的实现的主要内容,如果未能解决你的问题,请参考以下文章

单链表基本操作

《链表》之带头双向循环链表

❤️数据结构入门❤️(1 - 4)- 双向链表

数据结构双向链表的实现

数据结构与算法什么是链表?并用代码手动实现一个单向链表

数据结构c语言篇 《二》带头双向循环链表实现以及链表相关面试题(下)