数据结构----链表高阶

一.双向循环带头链表

所谓双向循环带头链表可以分为以下3类来进行理解:

首先是双向:双向表示后一个节点可以找到自己的前驱节点,这一点和单链表有本质的区别。

其次是带头:若链表带头的话说明,链表有一个头结点,这个头结点也可称为哨兵位的头结点,该节点和普通节点一样,依然有自己的前驱和后继节点,但是头结点的data/val一般为随机值。

最后是循环:循环链表指的是链表的尾结点的后继节点是头结点,头结点的前驱节点是尾结点。构成了一个大大的环状结构。

请添加图片描述

二.双向循环链表代码实现

typedef int LTDateType;

typedef struct ListNode
{
	LTDateType data;
	struct ListNode* next;//指向下一个数据
	struct ListNode* prev;//指向前一个数据
}LTNode;

三.常用函数的实现

节点初始化,由于是带头结点,因此一定要创建一个哨兵位头节点

1.节点初始化

L* ListInit()
{
	//节点初始化 双向循环带头节点
	L* head = (L*)malloc(sizeof(L));
	head->next = head;
	head->pre = head;


	return head;
}

由于以后的增删改查都要涉及到节点的创建,所以实现一个这样的函数简化代码

2.创建新节点

L* CreateNode(LI x)
{
	//创建新节点
	L* newnode = (L*)malloc(sizeof(L));
	newnode->next = NULL;
	newnode->pre = NULL;
	newnode->data = x;
	
	return newnode;
}

3.遍历打印

void ListPrint(L* head)
{

	assert(head);

	//打印
	L* cur = head->next;
	if (cur == head || cur == head->next);
	{
		printf("头节点");
	}
	while (cur != head)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	
	printf("\n");
}

4.尾插元素

void ListPushBack(L* head, LI x)
{
	assert(head);


	L* tail = head->pre;
	L* nexttail = CreateNode(x);
	
	//迭代
	tail->next = nexttail;
	nexttail->next = head;
	nexttail->pre = tail;
	head->pre = nexttail;

}

5.头插元素

void ListPushFront(L* head, LI x)
{
	assert(head);
	创建新节点
	//L* newnode = CreateNode(x);
	//L* nexthead = head->next;

	前插  改变指向
	//newnode->next = nexthead;
	//newnode->pre = head;
	//nexthead->pre = newnode;
	//head->next = newnode;

	//相当于头插
	ListInsert(head->next, x);
}

6.尾删元素

void ListPopBack(L* head)
{
	assert(head);
	assert(head->next != head);

	L* tail = head->pre;
	L* pretail = tail->pre;

	//改变指向 尾删
	pretail->next = head;
	head->pre = pretail;

	free(tail);

}

7.头删元素

void ListPopFront(L* head)
{
	assert(head);
	assert(head->next != head);

	L* headnext = head->next;
	L* nextnext = headnext->next;

	//改变指向 头删
	nextnext->pre = head;
	head->next = nextnext;

	free(headnext);
}

8.查找值为X的节点(返回地址)

L* ListFind(L* head, LI x)
{
	assert(head);

	L* findnode = head->next;
	
	while (findnode->next != head)
	{
		if (findnode->data == x)
		{
			return findnode;
		}
		//迭代
		findnode = findnode->next;
	}
	return NULL;
}

9.插入节点到指定位置(头插)

void ListInsert(L* pos, LI x)
{
	assert(pos);
	L* insert = CreateNode(x);
	L* prepos = pos->pre;

	//往prepos 和pos 中间插入节点
	prepos->next = insert;
	insert->next = pos;
	insert->pre = prepos;
	pos->pre = insert;
}

10.删除指定位置的节点


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

	L* prepos = pos->pre;
	L* nextpos = pos->next;

	//改变pos两边节点指向
	prepos->next = nextpos;
	nextpos->pre = prepos;

	//删除节点 释放内存
	free(pos);
}

11.删除链表。释放内存

void destory(L* head)
{
	assert(head);
	L* cur = (head)->next;
	while (cur != head)
	{
		L* next = cur->next;
		free(cur);
		cur = next;
	}
	free(head);
	head = NULL;
}

**

注意事项:双向循环带头链表传参不需要传入二级指针,因为是带头节点,带头的节点(哨兵位)不需要发生改变。只需要改变哨兵位后面的节点就可以了。

**

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值