最优链表&&链表与顺序表的优缺点.

🧸🧸🧸各位巨佬大家好,我是猪皮兄弟
在这里插入图片描述

🚒前言

    链表有几种结构
1.单向或者双向
2.带头或者不带头
3.循环或者不循环

这样算下来也就是八种结构

其中,最重要的就是单链表和双向带头循环链表(最优链表)
下面是双向带头循环链表的结构

typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
	
}LTNode;

一、⛄头文件中需要提供的相关接口

//void ListInit(LTNode**pphead);
LTNode* ListInit();

void ListPrint(LTNode* phead);
void ListPushFront(LTNode* phead, LTDataType x);
void ListPushBack(LTNode*phead,LTDataType x);
void ListPopBack(LTNode* phead);
void ListPopFront(LTNode* phead);
bool ListEmpty(LTNode*phead);
void ListInsert(LTNode* pos, LTDataType x);
void ListErase(LTNode* pos);
int ListSize(LTNode* phead);
void ListDestroy(LTNode*phead);

二、⛄初始化返回结构体的优势

    返回结构体更方便,在C语言中,避免了需要用二级指针去操控的问题

LTNode* CreateListNode(LTDataType x)
{
	LTNode* node = (LTNode*)malloc(sizeof(LTNode));
	if (node == NULL)
	{
		perror("CreateListNode::malloc");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}

//void ListInit(LTNode**pphead)
//{
//	*pphead = CreateListNode(-1);
//	(*pphead)->next = *pphead;
//	(*pphead)->prev = *pphead;
//}

//在初始化的时候就malloc哨兵位
LTNode* ListInit()
{
	LTNode*phead = CreateListNode(-1);
	phead->next = phead;
	phead->prev = phead;
	return phead;
}

三、⛄为什么这里只传一级指针就可以了呢?

在不带头的单链表中,是必须传二级指针的,因为我们知道,传一级指针是对传过来的参数的拷贝(&plist),所以想对第一个结点进行增添,修改和删除的话,并不会影响原结点的值,这只是临时的拷贝,那么带头了之后传一级就可以搞定呢??

   void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	/*LTNode* tail = phead->prev;
	LTNode* newnode = CreateListNode(x);
	newnode->next = tail->next;
	phead->prev = newnode;
	newnode->prev = tail;
	tail->next = newnode;*/
	ListInsert(phead, x);
}

    传一级指针只是说不能改变phead指向的结构体的值,phead后面的结构体还是可以改变的,因为,虽然是临时拷贝,但是->next指向的结构体是一样的,除了phead指向的结构体外,其他的都能改变,因为后面的是准确的找到了,而不是说是临时拷贝,就因为不能通用,所以传二级指针,但当带了头之后,这个问题也就迎刃而解


四、⛄相关代码的实现

其实,增添删除只需要完成Insert和Erase就可以了,其他的都只需要复用这两个函数


void ListPrint(LTNode* phead)
{
	assert(phead);
	printf("-1->");
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		printf("%d->",cur->data);
		cur = cur->next;
	}
	printf("-1\n");
}

void ListPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);
	/*LTNode* tail = phead->prev;
	LTNode* newnode = CreateListNode(x);
	newnode->next = tail->next;
	phead->prev = newnode;
	newnode->prev = tail;
	tail->next = newnode;*/
	ListInsert(phead, x);
}
void ListPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);
	/*LTNode* newnode = CreateListNode(x);
	LTNode* head = phead->next;
	newnode -> next = head;
	head->prev = newnode;
	phead->next = newnode;
	newnode->prev = phead;*/
	ListInsert(phead->next, x);

}

bool ListEmpty(LTNode* phead)
{
	assert(phead);
	return phead->next == phead;
}

//链表为空就有问题了
void ListPopBack(LTNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));//真就没事,假就报错
	LTNode* tail = phead->prev;
	LTNode* tailPrev = tail->prev;
	tailPrev->next = phead;
	phead->prev = tailPrev;
	free(tail);
}
//void ListPopFront(LTNode* phead);
//一样的思路



//在pos位置之前插入x
void ListInsert(LTNode* pos, LTDataType x)
{
	assert(pos);
	LTNode* posPrev = pos->prev;
	LTNode* newnode = CreateListNode(x);
	posPrev->next = newnode;
	newnode->next = pos;
	newnode->prev = posPrev;
	pos->prev = newnode;
}
//删除pos位置
void ListErase(LTNode* pos)
{
	assert(pos);
	LTNode* prev = pos->prev;
	LTNode* next = pos->next;
	free(pos);
	pos = NULL;
	prev->next = next;
	next->prev = prev;	

}


//前面的插入删除都是O(1),这个倒成了O(N),不太好,所以封装成一个结构体
int ListSize(LTNode* phead)
{
	assert(phead);
	int count=0;
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		cur = cur->next;
		++count;
	}
	return count;
}



void ListDestroy(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur!=phead)
	{
		LTNode* des=cur;
		cur = cur->next;
		free(des);
		des = NULL;
	}
	free(phead);
	phead = NULL;
}

五、⛄顺序表和链表的优缺点!!!

顺序表:
优点:
1.下标的随机访问(因为内存地址连续)
2.缓存利用率高(CPU高速缓存命中率高)
缺点:
1.头部或者中间想要增添或者删除,需要挪动数据,效率太低
2.扩容的问题
   a.首先,扩容就有一定程度的消耗,realloc扩容有两种可能,一是当前位置后面的空间足够,直接扩容,这个还好,二是当前位置后面的空间不够了,需要在堆区重新找一块足够的空间来进行扩容,并把现在的数据拷贝过去,再释放掉,如果数据太大,消耗是很大很大的。
   b.扩容一般是扩充1.5倍或2倍,没有被使用的空间也就造成了浪费。

链表(主要是带头双向循环):
优点:
1.按需申请释放空间
2.插入删除的时间内复杂度都是O(1);
缺点:
1.不支持下标的随机访问,因为空间不连续
2.缓存利用率低(CPU高速缓存命中率低)

从中可以看出,链表和顺序表是相辅相成的


六、⛄真心流露

        😶‍🌫️😶‍🌫️😶‍🌫️。创作不易,如果看到这里,觉得有什么帮助的话,还请多多支持

在这里插入图片描述

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 22
    评论
链表顺序都是线性的存储结构,它们各有优缺点顺序点是查找元素的效率较高,因为可以通过下标直接访问元素,同时内存中的元素是连续存储的,可以利用局部性原理提高缓存命中率。而顺序的缺点是插入和删除元素的效率较低,因为需要移动后续元素。另外,顺序的大小是固定的,需要进行扩容操作当元素数量超过容量时。 相比之下,链表点是插入和删除元素的效率较高,因为只需要改变节点之间的指针指向。同时,链表的大小是动态的,不需要预先定义容量。然而,链表的缺点是查找元素的效率较低,需要从头开始遍历节点,直到找到目标元素。 综上所述,顺序适合于频繁进行查找操作且元素数量相对稳定的场景,而链表适合于频繁进行插入和删除操作以及元素数量变化较大的场景。选择使用哪种结构应根据实际需求来决定。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [顺序链表优缺点总结](https://blog.csdn.net/lfm1010123/article/details/125381778)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

猪皮兄弟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值