数据结构之单向循环链表

概述

单向循环链表里面的每一个节点的next都要有指向,最后一个节点的next指向head节点
单向循环链表跟普通的单链表的操作逻辑几乎—模一样,唯一的区别,结尾的判断,区别如下

建议学完单向非循环链表,再来对比
(本人先是在掘金上编辑的,然后复制粘贴自己的博客,所以图片都有掘金的水印,欢迎来我的掘金博客查看原文)

image.png

示例代码

//单向循环链表
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

typedef int DataType;

typedef struct Node
{
	DataType data;
	struct Node *next;
}listnode, *singly_list;

//typedef struct Node listnode;
//typedef struct Node *singly_list; 

//初始化链表
singly_list init_list(void)
{
	//为链表申请一个节点作为头结点
	singly_list head = malloc(sizeof(listnode));
	if (head != NULL)
	{
		head->next = head;
	}
	return head;
}

//判断空链表
bool empty(singly_list head)
{
	return head->next == head;
}

//新建节点
singly_list create_node(DataType data)
{
	singly_list new = malloc(sizeof(listnode));
	if (new != NULL)
	{
		new->data = data;
		new->next = NULL;
	}
	return new;
}

//插入节点(尾插)
void insert_node(singly_list head, singly_list new)
{
	//如果不为空,就要找到链表的最后一个节点
	singly_list p = head;
	//如果下一个节点不为空,就一直往后找,直到这个节点的next为空
	while(p->next != head)
	{
		p = p->next;
	}
	new->next = head;
	p->next = new;
}

//插入节点(头插)
void tail_node(singly_list head, singly_list new)
{
	new->next = head->next;
	head->next = new;
}

//遍历
void display(singly_list head)
{
	singly_list p = head;
	while(p->next != head)
	{
		p = p->next;
		printf("%d ", p->data);
	}
	printf("\n");
}

//查找节点
singly_list find_node(singly_list head, DataType data)
{
	singly_list p = head;
	while(p->next != head)
	{
		p = p->next;
		if (p->data == data)
		{
			//如果跟链表的节点数据相同,表示找到这个节点
			return p;
		}
	}
	//如果遍历了整个链表都没有找到数据data
	return NULL;
}

//删除节点(只删除匹配到的第一个节点)
void delete_node1(singly_list head, DataType data)
{
	singly_list p = head;
	if(find_node(head, data) != NULL)
	{
		printf("链表里面有这个值存在\n");
	}
	else
	{
		printf("链表里面没有这个值存在\n");
		return ;
	}
	while(p->next != head)
	{
		if (p->next->data == data)
		{
			singly_list delete = p->next;//表示下一个节点就是要删除的节点
			p->next = delete->next;
			free(delete);
			return ;
		}
		p = p->next;
	}
}

//删除节点(将所有相同的值删掉)
void delete_node2(singly_list head, DataType data)
{
	singly_list p = head;
	if(find_node(head, data) != NULL)
	{
		printf("链表里面有这个值存在\n");
	}
	else
	{
		printf("链表里面没有这个值存在\n");
		return ;
	}
	while(p->next != head)
	{
		if (p->next->data == data)
		{
			singly_list delete = p->next;//表示下一个节点就是要删除的节点
			p->next = delete->next;
			free(delete);
			//假设后面有连续的要删除的节点,那么我们不能删除一个之后就往后偏移
			continue ;
		}
		p = p->next;
	}
}

//删除节点(要删除的数据是一个节点)
void delete_node3(singly_list head, singly_list delete)
{
	singly_list p = head;
	if(find_node(head, delete->data) != NULL)
	{
		printf("链表里面有这个值存在\n");
	}
	else
	{
		printf("链表里面没有这个值存在\n");
		return ;
	}
	while(p->next != head)
	{
		if (p->next == delete)
		{
			p->next = delete->next;
			free(delete);		
			return ;
		}
		p = p->next;
	}
}

//修改节点
void update_node(singly_list head, DataType old_data, DataType new_data)	
{
	singly_list find = find_node(head, old_data);
	if (find == NULL)
	{
		printf("没有找到这个数据\n");
	}
	else
	{
		find->data = new_data;
		printf("数据修改成功\n");
	}
	return;
}

//清空链表
void clear_list(singly_list head)
{
	while(head->next != head)
	{
		singly_list dele = head->next;
		head->next = dele->next;
		free(dele);
	}
}

//取出节点
singly_list get_node(singly_list head, DataType data)
{
	if (empty(head))
	{
		return NULL;
	}
	singly_list p = head;
	while(p->next != head)
	{
		if (p->next->data == data)
		{
			singly_list node = p->next;//表示下一个节点就是要删除的节点
			p->next = node->next;	
			return node;
		}
		p = p->next;
	}
	return NULL;
}

//移动节点()将data2的节点移动到data1的前面
void move_node(singly_list head, DataType data1, DataType data2)
{
	//取出data2的节点
	singly_list node1 = get_node(head, data2);
	//找到data1的节点
	singly_list node2 = find_node(head, data1);
	node1->next = node2->next;
	node2->next = node1;
}

//指定位置插入节点
void insert_any_node(singly_list node1, singly_list node2)
{
	if (node1 == NULL || node2 == NULL)
	{
		return ;
	}
	// node1->next = node2->next;
	// node2->next = node1;
	tail_node(node2, node1);
}

int main(int argc, char const *argv[])
{
	//初始化一个空链表
	singly_list head = init_list();

	int i;
	for (i = 0; i < 5; ++i)
	{
		insert_node(head, create_node(1+i));
	}

	// listnode n = {.next = NULL, .data = 7};
	// insert_node(head, &n);

	display(head);

	singly_list node = get_node(head, 2);

	printf("node->data = %d\n", node->data);
	display(head);
	delete_node1(head, 4);
	// singly_list node1 = find_node(head, 1);
	// singly_list node3 = find_node(head, 3);


	// singly_list node2 = create_node(7);
	// singly_list node4 = create_node(10);
	// insert_any_node(node2, node1);
	// insert_any_node(node4, node3);
	// move_node(head,1,4);

	
	display(head);
	update_node(head, 1, 7);
	display(head);
	return 0;
}

分块剖析

节点设计及初始化

和单向非循环的链表区别就是,加了一个prev指针指向上一个数据的地址。

image.png

创建新节点

image.png

尾插法插入节点

先将要插入的New节点插在头节点head与head的上一个节点之间,再断开头节点head与head的上一个节点之间的联系。这里为了方便理解,将head的上一个节点命名为tail。
建议按照图示去理解连接与断开步骤,切不可先断开再连接,否则找不到原来的链表了。

image.png

头插法插入节点

头插法插入节点,即是插在头节点的下一个的位置。(头节点是不带数据的,一定要对头插、尾插理解到位)

image.png

遍历链表

遍历链表分为往前、往后,两种遍历方式(毕竟有prev、next两个指针,自然有两种遍历方式咯)
注意结束条件都是!=head 而不是 !=NULL

image.png

查找节点

image.png

删除节点

先使要删除节点的前后节点连接起来后,再断开要删除的节点。

image.png

image.png

判断空链表与更新节点

判断空链表时,是
head->next == head && head->prev == head

image.png

清空链表

image.png

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

jianglongyin

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

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

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

打赏作者

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

抵扣说明:

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

余额充值