C语言提高篇(七)

本文介绍双链表的一些接口函数,以及实现方法。

在一个双链表中,每个节点都包含两个指针 ——指向前一个节点的指针和指向后一个节点的指针。这可以使我们以任何方向遍 历双链表,甚至可以忽前忽后地在双链表中访问。下面的图展示了一个双链表

现在,存在两个根指针:一个指向链表的第1个节点,另一个指向最后一个节 点。这两个指针允许我们从链表的任何一端开始遍历链表。 我们可能想把两个根指针分开声明为两个变量。但这样一样,我们必须把两个 指针都传递给插入函数。为根指针声明一个完整的节点更为方便,只是它的值字段 绝不会被使用。在我们的例子中,这个技巧只是浪费了一个整型值的内存空间。对 于值字段非常大的链表,分开声明两个指针可能更好一些。另外,我们也可以在根 节点的值字段中保存其他一些关于链表的信息,例如链表当前包含的节点数量。 

实现一个双链表需要定义一个节点的数据结构

typedef struct Node 
{
	struct Node* next;
	struct Node* prev;
	int data;

}ListNode ,*nodep;

再使用动态内存空间创建一个头结点:

nodep ListNode_Creat(void)
{
	nodep root = (nodep)malloc(sizeof(ListNode));
	if (root == NULL) return NULL;

	root->data = 0;
	root->next = root;
	root->prev = root;
	return root;

}

接下来可以写一个插入函数,将数据一个个插入到链表中,就可以实现一个简单的双向链表。

头部插入:

int ListNode_Add_Head(nodep root, int val)
{
	if (root == NULL) return -1;

	nodep first = root->next;

	nodep new = (nodep)malloc(sizeof(ListNode));
	if (new == NULL)return 0;

	new->data = val;

	new->next = first;

	first->prev = new;

	new->prev = root;

	root->next = new;

	return 1;
	
}

它的功能是在一个双向链表的头部插入一个新的节点。它的参数是 root(链表的头节点)和 val(新节点的数据)。它的返回值是 0(成功)或 -1(失败)。它的具体步骤如下:

  • 首先判断 root 是否为空,如果为空,说明链表不存在,返回 -1。
  • 然后获取 root 的下一个节点,即链表的第一个节点,赋值给 first。
  • 接着分配内存空间给新节点 new,如果分配失败,返回 0。
  • 然后给新节点 new 的数据域赋值为 val。
  • 然后把新节点 new 的下一个节点指向 first,即把新节点插入到链表的开头。
  • 然后把 first 的前一个节点指向 new,即把新节点和原来的第一个节点连接起来。
  • 然后把新节点 new 的前一个节点指向 root,即把新节点和头节点连接起来。
  • 最后把 root 的下一个节点指向 new,即更新头节点的指针。
  • 返回 1,表示插入成功。

尾部插入:

int ListNode_Add_Tail(nodep root,int val)
{
	if(root == NULL) return -1;

	nodep tail = root->prev;

	nodep new = (nodep)malloc(sizeof(ListNode));

	if(new == NULL) return 0;

	new->data = val;

	new->prev = tail;

	tail->next = new;

	new->next = root;

	root->prev = new;

	return 1;

}

尾部移除:

int ListNode_Remove_Tail(nodep root)
{
	if (root == NULL)return -1;

	nodep tail = root->prev;

	nodep newtail = tail->prev;

	root->prev = newtail;

	newtail->next = root;

	free(tail);

	return 1;
}

的功能是在一个双向链表的尾部删除一个节点。它的参数是 root(链表的头节点)。它的返回值是 1(成功)或 -1(失败)。它的具体步骤如下:

  • 首先判断 root 是否为空,如果为空,说明链表不存在,返回 -1。
  • 然后获取 root 的前一个节点,即链表的最后一个节点,赋值给 tail。
  • 然后获取 tail 的前一个节点,即链表的倒数第二个节点,赋值给 newtail。
  • 然后把 root 的前一个节点指向 newtail,即把倒数第二个节点作为新的尾节点。
  • 然后把 newtail 的下一个节点指向 root,即把链表形成一个循环。
  • 然后释放 tail 占用的内存空间,即删除原来的尾节点。
  • 返回 1,表示删除成功。

头部移除:

int ListNode_Remove_Head(nodep root)
{
	if (root == NULL) return -1;

	nodep head = root->next;

	nodep newhead = head->next;

	root->next = newhead;

	newhead->prev = root;

	free(head);

	return 1;

}

在指定节点前后添加:

int ListNode_Add_AheadNode(nodep node,int val)
{
	if (node == NULL)return -1;

	nodep new = (nodep)malloc(sizeof(ListNode));
	if (new == NULL)return 0;

	new->data = val;

	nodep last = node->prev;

	last->next = new;

	new->prev = last;

	new->next = node;

	node->prev = new;

	return 1;

}
int ListNode_Add_BehindNode(nodep node, int val)
{
	if (node == NULL)return -1;

	nodep next = node->next;

	nodep new = (nodep)malloc(sizeof(ListNode));
	if (new == NULL)return 0;

	new->data = val;

	new->prev = node;

	node->next = new;

	new->next = next;

	next->prev = new;

	return 1;

}

在一个双向链表中在指定的节点前后插入新的节点。它们的参数都是 node(要插入的位置的节点)和 val(新节点的数据)。它们的返回值都是 1(成功)或 -1(失败)。它们的具体步骤如下:

  • ListNode_Add_AheadNode 函数是在 node 节点的前面插入新节点。它的步骤是:
    • 首先判断 node 是否为空,如果为空,说明链表不存在,返回 -1。
    • 然后分配内存空间给新节点 new,如果分配失败,返回 0。
    • 然后给新节点 new 的数据域赋值为 val。
    • 然后获取 node 的前一个节点,即链表的前一个节点,赋值给 last。
    • 然后把 last 的下一个节点指向 new,即把新节点和前一个节点连接起来。
    • 然后把 new 的前一个节点指向 last,即把新节点和前一个节点连接起来。
    • 然后把 new 的下一个节点指向 node,即把新节点和当前节点连接起来。
    • 然后把 node 的前一个节点指向 new,即把当前节点和新节点连接起来。
    • 返回 1,表示插入成功。
  • ListNode_Add_BehindNode 函数是在 node 节点的后面插入新节点。它的步骤是:
    • 首先判断 node 是否为空,如果为空,说明链表不存在,返回 -1。
    • 然后分配内存空间给新节点 new,如果分配失败,返回 0。
    • 然后给新节点 new 的数据域赋值为 val。
    • 然后获取 node 的下一个节点,即链表的后一个节点,赋值给 next。
    • 然后把 new 的前一个节点指向 node,即把新节点和当前节点连接起来。
    • 然后把 node 的下一个节点指向 new,即把当前节点和新节点连接起来。
    • 然后把 new 的下一个节点指向 next,即把新节点和后一个节点连接起来。
    • 然后把 next 的前一个节点指向 new,即把后一个节点和新节点连接起来。
    • 返回 1,表示插入成功

查找节点:

nodep ListNode_FindVal(nodep root, int val)
{
	if (root == NULL)return -1;

	nodep cur = root->next;

	while (cur != root && cur->data != val)
	{
		cur = cur->next;
	}

	return cur;
}

删除节点:

int ListNode_RemoveNode(nodep node)
{
	if (node == NULL) return -1;

	node->prev->next = node->next;

	node->next->prev = node->prev;

	free(node);

	return 1;

}

清空链表:

int ListNode_FreeAll(nodep root)
{
	if (root == NULL)return -1;

	nodep cur = root->next;

	while (cur != root)
	{
		cur = cur->next;

		nodep  temp = cur;

		free(temp);
	}

	free(root);

	return 1;

}

打印链表:

int ListNode_Show(nodep root)
{
	if (root == NULL)return -1;

	nodep cur = root->next;

	while (cur != root)
	{
		printf("%d -> ", cur->data);
		cur = cur->next;
		
	}
	printf("NULL\n");

	return 1;
	
}

  • 17
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值