线性表之链表

线性表之链表

链表概述

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

  • 线性表的链式存储结构称为链表,其中每个存储结点不仅包含元素本身的信息(数据域),而且包含表示元素之间逻辑关系的信息,在C/C++语言中采用指针实现,称为指针域

  • 链表的结构很多,有单向/双向、带头/不带头、循环/非循环

  • 常用的只有无头单向不循环带头双向循环链表

    • 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。

    • 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单.

    • 如图:

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nc8BJ7qK-1663322107309)(C:\Users\何锦\AppData\Roaming\Typora\typora-user-images\image-20220916173507292.png)]

单链表 – 无头单向不循环

单链表的接口

/* 这里是SingleLinkedList.h文件 */
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>

typedef int ElemType;

typedef struct SingleListNode
{
	ElemType data;    //存放元素值
	struct SingleListNode* next;   //指向后继结点
}SLinkNode;  //单链表结点类型

/* 
接口函数的声明 
*/

/* 动态申请一个结点 */
SLinkNode* CreateLinkNode(ElemType x);

/* 打印单链表 */
void PrintSingleLinkedList(SLinkNode* plist);

/* 单链表尾插 */
void PushBackSingleLinkedList(SLinkNode** pplist, ElemType x);

/* 单链表头插 */
void PushFrontSingleLinkedList(SLinkNode** pplist, ElemType x);

/* 单链表尾删 */
void PopBackSingleLinkedList(SLinkNode** pplist);

/* 单链表头删 */
void PopFrontSingleLinkedList(SLinkNode** pplist);

/* 单链表查找 */
SLinkNode* SearchSingleLinkedList(SLinkNode* plist, ElemType x);

/* 单链表插入 */
void InsertSingleLinkedList(SLinkNode** pplist, SLinkNode* pos, ElemType x);

/* 单链表删除 */
void RemoveSingleLinkedList(SLinkNode* pos);

/* 单链表销毁 */
void DestorySingleLinkedList(SLinkNode** pplist);

单链表接口的实现

/* 这里是SingleLinkedList.c文件*/
#include "SingleLinkedList.h"

/* 动态申请一个结点 */
SLinkNode* CreateLinkNode(ElemType x)
{
	SLinkNode* newNode = (SLinkNode*)malloc(sizeof(SLinkNode));
	if (newNode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	return newNode;
}

/* 打印单链表 */
void PrintSingleLinkedList(SLinkNode* plist)
{
	assert(plist);
	SLinkNode* cur = plist;
	while (cur)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

/* 单链表尾插 */
void PushBackSingleLinkedList(SLinkNode** pplist, ElemType x)
{
	assert(pplist);
	SLinkNode* newNode = CreateLinkNode(x);
	if (*pplist == NULL)
	{
		*pplist = newNode;
	}
	else
	{
		SLinkNode* tail = *pplist;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = newNode;
	}
}

/* 单链表头插 */
void PushFrontSingleLinkedList(SLinkNode** pplist, ElemType x)
{
	assert(pplist);
	SLinkNode* newNode = CreateLinkNode(x);
	newNode->next = *pplist;
	*pplist = newNode;
}

/* 单链表尾删 */
void PopBackSingleLinkedList(SLinkNode** pplist)
{
	assert(pplist);
	//判断是否存在元素
	assert(*pplist);
	SLinkNode* tail = *pplist;
	if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	else
	{
		while (tail->next->next != NULL)
		{
			tail = tail->next;
		}
		free(tail->next);
		tail->next = NULL;
	}
}

/* 单链表头删 */
void PopFrontSingleLinkedList(SLinkNode** pplist)
{
	assert(pplist);
	//判断是否有元素
	assert(*pplist);
	SLinkNode* head = *pplist;
	*pplist = (*pplist)->next;
	free(head);
}

/* 单链表查找 */
SLinkNode* SearchSingleLinkedList(SLinkNode* plist, ElemType x)
{
	assert(plist);
	SLinkNode* cur = plist;
	while (plist)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	return NULL;
}

/* 单链表插入 */
//注意是在pos位置之后插入新的元素
void InsertSingleLinkedList(SLinkNode** pplist, SLinkNode* pos, ElemType x)
{
	assert(pplist && pos);
	SLinkNode* newNode = CreateLinkNode(x);
	SLinkNode* cur = *pplist;
	while (cur != pos)
	{
		cur = cur->next;
	}
	newNode->next = cur->next;
	cur->next = newNode;
}

/* 单链表删除 */
//注意是在pos位置之后删除元素
void RemoveSingleLinkedList(SLinkNode* pos)
{
	assert(pos);
	assert(pos->next);
	SLinkNode* next = pos->next;
	pos->next = next->next;
	free(next);
	next = NULL;
}

/* 单链表销毁 */
void DestorySingleLinkedList(SLinkNode** pplist)
{
	SLinkNode* curFront = *pplist;
	SLinkNode* cur = curFront->next;
	while (cur)
	{
		free(curFront);
		curFront = cur;
		cur = cur->next;
	}
	free(curFront);
	curFront = NULL;
}

双链表 – 带头双向循环

双链表的接口

/* 这里是DoubleLinkedList.h文件 */
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int ElemType;

typedef struct DoubleLinkNode
{
	ElemType data;  //数据域
	struct DoubleLinkNode* next;  //指向后继结点
	struct DoubleLinkNode* prev;  //指向前驱结点
}DLinkNode;  //双链表结点类型

/* 
接口函数的声明
*/

/* 创建头节点 */
DLinkNode* InitDoubleLinkedList();

/* 动态申请一个结点 */
DLinkNode* CreateNode(ElemType x);

/* 销毁双链表 */
void DestroyDoubleLinkedList(DLinkNode* phead);

/* 打印双向链表 */
void PrintDoubleLinkedList(DLinkNode* phead);

/* 双向链表尾插 */
void PushBackDoubleLinkedList(DLinkNode* phead, ElemType x);

/* 双向链表尾删 */
void PopBackDoubleLinkedList(DLinkNode* phead);

/* 双向链表头插 */
void PushFrontDoubleLinkedList(DLinkNode* phead, ElemType x);

/* 双向链表头删 */
void PopFrontDoubleLinkedList(DLinkNode* phead);

/* 双向链表查找 */
DLinkNode* SearchDoubleLinkedList(DLinkNode* phead, ElemType x);

/* 双向链表在pos的前面进行插入 */
void InsertDoubleLinkedList(DLinkNode* pos, ElemType x);

/* 双向链表删除pos位置的节点 */
void RemoveDoubleLinkedList(DLinkNode* pos);

双链表接口的实现

/* 这里是DoubelLinkedList.c文件 */
#include "DoubelLinkedList.h"

/* 创建头节点 */
DLinkNode* InitDoubleLinkedList()
{
	//哨兵位头结点
	DLinkNode* phead = (DLinkNode*)malloc(sizeof(DLinkNode));
	if (phead == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

/* 动态申请一个结点 */
DLinkNode* CreateNode(ElemType x)
{
	DLinkNode* newNode = (DLinkNode*)malloc(sizeof(DLinkNode));
	if (newNode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newNode->data = x;
	newNode->next = NULL;
	newNode->prev = NULL;
	return newNode;
}

/* 打印双向链表 */
void PrintDoubleLinkedList(DLinkNode* phead)
{
	DLinkNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

/* 双向链表尾插 */
void PushBackDoubleLinkedList(DLinkNode* phead, ElemType x)
{
	assert(phead);
	DLinkNode* tail = phead->prev;
	DLinkNode* newNode = CreateNode(x);
	tail->next = newNode;
	newNode->prev = tail;
	newNode->next = phead;
	phead->prev = newNode;
	/* 实现了在pos位置前面插入元素的函数之后,直接复用 */
	InsertDoubleLinkedList(phead, x);
}

/* 双向链表尾删 */
void PopBackDoubleLinkedList(DLinkNode* phead)
{
	assert(phead);
	//如果 phead->next == phead,那么链表为空,无法继续删除
	assert(phead->next != phead);
	DLinkNode* tail = phead->prev;
	tail->prev->next = phead;
	phead->prev = tail->prev;
	free(tail);
	/* 实现了删除pos位置的函数之后,直接复用 */
	RemoveDoubleLinkedList(phead->prev);
}

/* 双向链表头插 */
void PushFrontDoubleLinkedList(DLinkNode* phead, ElemType x)
{
	assert(phead);
	DLinkNode* newNode = CreateNode(x);
	newNode->next = phead->next;
	phead->next->prev = newNode;
	newNode->prev = phead;
	phead->next = newNode;
	/* 实现了在pos位置前面插入元素的函数之后,直接复用 */
	InsertDoubleLinkedList(phead->next, x);
}

/* 双向链表头删 */
void PopFrontDoubleLinkedList(DLinkNode* phead)
{
	assert(phead);
	assert(phead->next != phead);
	DLinkNode* cur = phead->next;
	phead->next = cur->next;
	cur->next->prev = phead;
	free(cur);
	/* 实现了删除pos位置的函数之后,直接复用 */
	RemoveDoubleLinkedList(phead->next);
}

/* 双向链表查找 */
DLinkNode* SearchDoubleLinkedList(DLinkNode* phead, ElemType x)
{
	assert(phead);
	DLinkNode* cur = phead;
	while (cur->next != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
}

/* 双向链表在pos的前面进行插入 */
void InsertDoubleLinkedList(DLinkNode* pos, ElemType x)
{
	assert(pos);
	DLinkNode* newNode = CreateNode(x);
	pos->prev->next = newNode;
	newNode->prev = pos->prev;
	newNode->next = pos;
	pos->prev = newNode;
}

/* 双向链表删除pos位置的节点 */
void RemoveDoubleLinkedList(DLinkNode* pos)
{
	assert(pos);
	pos->next->prev = pos->prev;
	pos->prev->next = pos->next;
	free(pos);
}

/* 销毁双向链表 */
void DestroyDoubleLinkedList(DLinkNode* phead)
{
	assert(phead);
	DLinkNode* cur = phead->next;
	while (cur != phead)
	{
		cur = cur->next;
		free(cur->prev);
	}
	free(phead);
}

链表的优缺点

  • 注意这里指的是带头双向循环链表

  • 优点:

    • 任意位置插入效率高,时间复杂度为O(1)
    • 按需申请释放空间
  • 缺点:

    • 不支持随机访问(用下标访问),查找的时间复杂度为O(N);这意味着一些排序,二分查找等在这种结构上不适用
    • 链表存储数据的同时要存储指针,存在一定的消耗
    • CPU高速缓存命中率低
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ALMAN8

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值