[数据结构] 双向链表

前言:

      相较于单链表,有一种更优的链表,下面我来介绍一下双向链表的相关知识。

一、双向循环链表的实现

     双向链表在创建时会建立两个指针,可以向前链接,也可以向后链接;

    如图所示:

   

   结构体的创建:

   

typedef int ListDataType;
typedef struct ListNode
{
	ListDataType data;
	struct ListNode* prev;
	struct ListNode* next;

}ListNode;

  创建新节点:

ListNode* BuyListNode(ListDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->prev = NULL;
	newnode->next = NULL;

	return newnode;
}

  链表的初始化:

void ListInit(ListNode* phead)
{
	assert(phead);
	phead->next = phead;
	phead->prev = phead;

}

链表的初始化用的一级指针,为了和其他的函数统一用一级,所以我需要在main函数中提前

    ListNode* phead = BuyListNode(-1);

  链表的打印:

void ListPrint(ListNode* phead)
{
	assert(phead);
	printf("哨兵位<==>");
	ListNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d<==>",cur->data);
		cur = cur->next;
	}
}

 

判断链表是否只有头节点:

bool ListEmpty(ListNode* phead)
{
	assert(phead);

	return phead->next == phead;
}

 

双向链表尾插:

void ListPushBack(ListNode* phead,ListDataType x)
{
	assert(phead);
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = phead->prev;

	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = phead;
	phead->prev = newnode;
}

  双向链表头插:

void ListPushFront(ListNode* phead, ListDataType x)
{
	assert(phead);
	ListNode* next = phead->next;
	ListNode* newnode = BuyListNode(x);

	phead->next = newnode;
	newnode->prev = phead;
	newnode->next = next;
	next->prev = newnode;
  
}

 链表查找:

ListNode* ListFind(ListNode* phead, ListDataType x)
{
	assert(phead);
	ListNode* cur = phead;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

  头删:

void ListPopFront(ListNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	ListNode* del = phead->next;
	ListNode* next = del->next;

	phead->next = next;
	next->prev = phead;
	free(del);

}

 尾删:

void ListPopBack(ListNode* phead)
{
	assert(phead);
	assert(!ListEmpty(phead));

	ListNode* tail = phead->prev;
	ListNode* tailPrev = tail->prev;

	tailPrev->next = phead;
	phead->prev = tailPrev;
	free(tail);
}

在pos位置之后 插入:

void ListInsert(ListNode* pos, ListDataType x)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* newnode = BuyListNode(x);

	prev->next = newnode;
	newnode->prev = prev;
	newnode->next = pos;
	pos->prev = newnode;

}

 删除pos位置:

void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* posPrev = pos->prev;
	ListNode* posNext = pos->next;

	posPrev->next = posNext;
	posNext->prev = posPrev;

	free(pos);
}

二、顺序表和链表的区别

不同点顺序表链表
存储空间上物理上一定连续逻辑上连续,物理不一定
随机访问支持不支持

任意位置插入

或者删除元素

可能需要搬移元素,效率低只需要修改指针指向
插入动态顺序表,空间不够需要扩容没有容量的概念
应用场景元素高效存储+频繁访问任意位置插入和删除频繁
缓存利用率

总结:

 这就是对双向链表的简要介绍,更新不易,希望各位小伙伴动动小手,三连走一走,你们的三连对我真的很重要!!!!!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值