数据结构之双向链表

目录

1、链表的创建(创建并返回链表的头节点)

 2、链表的销毁

3、链表的打印

4、尾插

5、尾删

6、头插

7、头删

8、查找

9、在pos的前面进行插入

10、在pos位置删除


 特点:相比于无头单向不循环链表来说,该链表结构比较复杂,但是功能实现比较简单。

功能实现:

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

1、链表的创建(创建并返回链表的头节点)

链表的头节点需要用调用malloc函数开辟,然后要将开辟出的指针强制类型转换为带头双向循环链表型。

ListNode* ListCreate()
{
	ListNode* pHead = (ListNode*)malloc(sizeof(ListNode));
	if (pHead == NULL)
	{
		printf("malloc is fail \n");
		exit(-1);
	}
	pHead->next = pHead;
	pHead->prev = pHead;
	return pHead;
}

 2、链表的销毁

假设该链表为以下情况,链表的销毁不仅需要销毁到链表的头,还需要销毁链表的数据,定义一个指针指向数据的第一个,还需要定义一个指针用来迭代cur,知道数据销毁完毕。但是需要注意的是结束循环的条件不是cur!=NULL,由图可知当next->next指向pHead时,链表的数据已经到尾了。

 

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

3、链表的打印

跟链表的销毁一个逻辑,但是不需要记录cur的next,因为cur指向的空间不会被释放。

// 双向链表打印
void ListPrint(ListNode* pHead)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	while (cur != pHead)
	{

		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

4、尾插

尾插时需要开辟一个新的结点,然后将结点的之前一个指向头,头的下一个指向结点。结点的下一个指向也只向头,头的前一个指向结点。

 

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	ListNode* newnode = BuyDListNode(x);
	ListNode* tail = pHead->prev;
	tail->next = newnode;
	newnode->prev = tail;
	newnode->next = pHead;
	pHead->prev = newnode;

}

5、尾删

尾删需要考虑两种情况,正常情况下,链表是不为空的,当链表为空时,就不能删除数据了,所以需要加上一个断言,判断链表不为空。因为链表是一个循环列表,所以在判断链表是否为空的时候,只需要判断pHead的next是否指向自己。

 由图可知,双向链表的尾结点就是头节点的前一个,为了增加代码的可读性,可以定义一个尾结点和尾结点的前一个结点,只需要将prev的下一个指向pHead,pHead的前一个指向prev,就可以组成一个新的链表,最后释放tail所指向的结点,就完成删除。

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->next != pHead);
	ListNode* tail = pHead->prev;
	ListNode* prevtail = tail->prev;
	prevtail->next = pHead;
	pHead->prev= prevtail;
	free(tail);
}

6、头插

头插时需要在定义一个指针记录刚开始链表第一个数据的位置,然后插入。

 

void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);
		ListNode* newnode = BuyDListNode(x);
		ListNode* Next = pHead->next;
		pHead->next = newnode;
		newnode->prev = pHead;
		newnode->next = Next;
		Next->prev = newnode;
}

7、头删

头删同样需要考虑链表为空的情况,操作与尾插一样。

需要确定链表的头位置与第二个位置。

 

void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->next != pHead);
	ListNode* head = pHead->next;
	ListNode* next = head->next;
	pHead->next = next;
	next->prev = pHead;
	free(head);
}

8、查找

查找的思维跟打印其实是一样的,找到了就返回地址,找不到就返回空,同时也需要知道截至循环的条件,也是cur的next指向头节点。

// 双向链表查找
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;
}

9、在pos的前面进行插入

在插入之前需要记住pos的前一个位置。

 

void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* newcode = BuyDListNode(x);
	ListNode* prev = pos->prev;
	prev->next = newcode;
	newcode->prev = prev;
	newcode->next = pos;
	pos->prev = newcode;
}

10、在pos位置删除

需要记得pos的前一个位置和pos的后一个位置,然后让这两个位置互相指向。

 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

开拓的嚣张

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

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

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

打赏作者

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

抵扣说明:

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

余额充值