数据结构 -- 链表详解

一、链表的概念及结构

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

链表的结构可以形象的看作是一辆火车,每块空间独立存在并且通过指针链接在一起。

在数据结构中:

注意:1、从上图可以看出,链式结构在逻辑上是连续的,但在物理上不一定连续。

           2、现实的节点一般都是从堆上申请出来的。

           3、在堆上申请的空间是按一定策略来分配的,两次申请的空间可能连续也可能不连续。

二、链表的分类

实际上链表的结构非常多样,以下情况组合起来就有八种链表结构

1、单向或者双向

2、带头(哨兵位)或者不带头

3、循环或者非循环

虽然链表中存在这么多结构,但是在实际中最常用的就只有两种结构:

1、无头单向非循环链表:结构简单,一般不会单独用来存储数据,实际中更多的是作为其他数据结构的子结构,如哈希桶、图的邻接表等。

2、带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表,这个结构虽然结构较为复杂,但是使用代码实现反而因为结构的原因更加简单了,这一点在后续代码实现过程中会体现出来。

三、链表的实现

在代码实现过程中,我们依然为两个链表分别建立三个文件

无头单向非循环链表:SList.h(函数声明)、SList.c(函数定义)、4.c(测试函数功能)

带头双向循环链表:List.h(函数声明)、List.c(函数定义)、5.c(测试函数功能)

3.1 无头单向非循环链表

1)创建结构体

首先我们需要创建一个结构体,其中一个存放数据,另一个指向下一个空间。

typedef int SLTDateType;

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

2)尾插/头插

尾插需要先判断链表是否为空,为空则直接建立一个节点就行,不为空则需要遍历到最后一个节点后插入

// 单链表的尾插
void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	assert(*pplist);

	SListNode* newnode = BuySListNode(x);
	
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

头插的实现较为简单,只需要在链表头插入即可

// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x)
{
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

测试结果

3)尾删/头删

尾删和尾插的思路刚好相反,注意需要判空

// 单链表的尾删
void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}
// 单链表的头删
void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* del = *pplist;
		*pplist = (*pplist)->next;
		free(del);
	}
}

测试结果

4)查找

// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x)
{
	SListNode* cur = plist;
	while (cur)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

3.2 带头双向循环链表

1)创建结构体

这里我们依然创建一个结构体,其中一个存放数据,另外两个分别指向前一片空间和后一片空间

typedef int LTDataType;

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

2)创建头结点(哨兵位)

创建哨兵位时只有这一个元素,该元素的前后指针都指向自己,已到达循环的目的

// 创建头结点(哨兵位)
ListNode* ListCreate()
{
	ListNode* pHead = BuyNode(-1);
	pHead->_next = pHead;
	pHead->_prev = pHead;
	return pHead;
}

3)尾插/头插

// 双链表的尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* tail = pHead->_prev;
	ListNode* newnode = BuyNode(x);

	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = pHead;
	pHead->_prev = newnode;
}
// 双链表的头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* newnode = BuyNode(x);
	ListNode* first = pHead->_next;

	pHead->_next = newnode;
	newnode->_prev = pHead;
	newnode->_next = first;
	first->_prev = newnode;
}

测试结果

4)尾删/头删

// 双链表的尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);

	ListNode* tail = pHead->_prev;
	ListNode* tailprev = tail->_prev;
	free(tail);
	tailprev->_next = pHead;
	pHead->_prev = tailprev;
}
// 双链表的头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead);

	ListNode* first = pHead->_next;
	ListNode* second = first->_next;

	pHead->_next = second;
	second->_prev = pHead;
	free(first);
}

测试结果

5)查找

// 双链表的查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		if (cur->_data == x)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

6)在指定位置前插入

// 在pos位置的前面插入
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* prev = pos->_prev;
	ListNode* newnode = BuyNode(x);

	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = pos;
	pos->_prev = newnode;
}

7)删除指定位置的节点

// 删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* posprev = pos->_prev;
	ListNode* posnext = pos->_next;

	posprev->_next = posnext;
	posnext->_prev = posprev;
	free(pos);
}

四、源码

4.1 无头单向非循环链表

SList.h(函数声明)

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



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

// 动态申请一个节点
SListNode* BuySListNode(SLTDateType x);
// 单链表打印
void SListPrint(SListNode* plist);
// 单链表尾插
void SListPushBack(SListNode** pplist, SLTDateType x);
// 单链表的头插
void SListPushFront(SListNode** pplist, SLTDateType x);
// 单链表的尾删
void SListPopBack(SListNode** pplist);
// 单链表头删
void SListPopFront(SListNode** pplist);
// 单链表查找
SListNode* SListFind(SListNode* plist, SLTDateType x);

SList.c(函数定义)

#include"SList.h"

SListNode* BuySListNode(SLTDateType x)
{
	SListNode* newnode = (SListNode*)malloc(sizeof(SListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;

	return newnode;
}


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


void SListPushBack(SListNode** pplist, SLTDateType x)
{
	assert(pplist);
	assert(*pplist);

	SListNode* newnode = BuySListNode(x);
	
	if (*pplist == NULL)
	{
		*pplist = newnode;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newnode;
	}
}

void SListPushFront(SListNode** pplist, SLTDateType x)
{
	assert(pplist);

	SListNode* newnode = BuySListNode(x);
	newnode->next = *pplist;
	*pplist = newnode;
}

void SListPopBack(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* tail = *pplist;
		while (tail->next->next)
		{
			tail = tail->next;
		}

		free(tail->next);
		tail->next = NULL;
	}
}

void SListPopFront(SListNode** pplist)
{
	assert(pplist);
	assert(*pplist);

	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		SListNode* del = *pplist;
		*pplist = (*pplist)->next;
		free(del);
	}
}

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

4.c(测试函数功能)

#include"SList.h"


void TestSList1()
{
	SListNode* plist = NULL;
	SListPushFront(&plist, 1);
	SListPushFront(&plist, 2);
	SListPushFront(&plist, 3);
	SListPushFront(&plist, 4);

    SListPrint(plist);

	SListPushBack(&plist, 5);
	SListPushBack(&plist, 6);
	SListPushBack(&plist, 7);
	SListPushBack(&plist, 8);

	SListPrint(plist);

	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	SListPopBack(&plist);
	SListPrint(plist);

	/*SListPopBack(&plist);
	SListPrint(plist);*/

	/*SListPopFront(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);

	SListPopFront(&plist);
	SListPrint(plist);*/

}



int main()
{
	TestSList1();

	return 0;
}

4.2 带头双向循环链表

List.h(函数声明)

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>


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

// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos);

List.c(函数定义)

#include"List.h"


ListNode* BuyNode(LTDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return NULL;
	}

	newnode->_data = x;
	newnode->_next = NULL;
	newnode->_prev = NULL;
}

ListNode* ListCreate()
{
	ListNode* pHead = BuyNode(-1);
	pHead->_next = pHead;
	pHead->_prev = pHead;
	return pHead;
}

void ListPrint(ListNode* pHead)
{
	assert(pHead);

	printf("sentinel<==>");
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		printf("%d<==>", cur->_data);
		cur = cur->_next;
	}
	printf("\n");
}

bool LTEmpty(ListNode* pHead)
{
	assert(pHead);

	return pHead->_next == pHead;
}

void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* tail = pHead->_prev;
	ListNode* newnode = BuyNode(x);

	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = pHead;
	pHead->_prev = newnode;
}

void ListPopBack(ListNode* pHead)
{
	assert(pHead);

	ListNode* tail = pHead->_prev;
	ListNode* tailprev = tail->_prev;
	free(tail);
	tailprev->_next = pHead;
	pHead->_prev = tailprev;
}

void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* newnode = BuyNode(x);
	ListNode* first = pHead->_next;

	pHead->_next = newnode;
	newnode->_prev = pHead;
	newnode->_next = first;
	first->_prev = newnode;
}

void ListPopFront(ListNode* pHead)
{
	assert(pHead);

	ListNode* first = pHead->_next;
	ListNode* second = first->_next;

	pHead->_next = second;
	second->_prev = pHead;
	free(first);
}

ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		if (cur->_data == x)
		{
			return cur;
		}
		cur = cur->_next;
	}
	return NULL;
}

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);

	ListNode* prev = pos->_prev;
	ListNode* newnode = BuyNode(x);

	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = pos;
	pos->_prev = newnode;
}

void ListErase(ListNode* pos)
{
	assert(pos);

	ListNode* posprev = pos->_prev;
	ListNode* posnext = pos->_next;

	posprev->_next = posnext;
	posnext->_prev = posprev;
	free(pos);
}

void ListDestory(ListNode* pHead)
{
	assert(pHead);

	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		ListNode* next = cur->_next;
		free(cur);
		cur = next;
	}
	free(pHead);
}

5.c(测试函数功能)

#include"List.h"

TestList()
{
	ListNode* plist = ListCreate();
	ListPushBack(plist, 1);
	ListPushBack(plist, 2);
	ListPushBack(plist, 3);
	ListPushBack(plist, 4);
	ListPrint(plist);

	ListPushFront(plist, 5);
	ListPushFront(plist, 6);
	ListPushFront(plist, 7);
	ListPushFront(plist, 8);
	ListPrint(plist);

	ListPopBack(plist);
	ListPopBack(plist);
	ListPopBack(plist);
	ListPrint(plist);

	ListPopFront(plist);
	ListPopFront(plist);
	ListPopFront(plist);
	ListPrint(plist);

}

int main()
{
	TestList();

	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值