0
点赞
收藏
分享

微信扫一扫

数据结构:带头双向循环链表——增加、删除、查找、修改,详细解析


目录

 一、带头双向循环链表的处理和介绍

1、带头双向循环链表的概念

2、带头双向循环链表的结构

 3、节点的处理

4、头节点的处理

5、节点(结构体)内存空间的开辟

二、链表的主体框架

1、整体框架

 2、主菜单

 三、功能的实现

1、创建一个新节点的函数

2、初始化创建头节点的函数

3、打印节点数据的函数

4、尾插和头插增加节点的函数

4.1 从尾部插入节点的函数

4.2 从头部插入节点的函数

5、尾删和头删减少节点的函数

5.1 从尾部删除节点的函数

 5.2 从头部删除节点的函数

 6、提供一个数据,找到这个数据所在的节点的函数

7、给一个数据,在其所在节点前面插入一个节点的函数

8、给一个数据,删除这个数据所在的节点的函数

 9、修改数据的函数

10、部分功能的简化与升级

 10.1 尾插和头插函数的简化升级

 四、总代码

 五、代码运行实例


 一、带头双向循环链表的处理和介绍

1、带头双向循环链表的概念


2、带头双向循环链表的结构


 3、节点的处理

typedef int ListDataType;//类型名重定义

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


4、头节点的处理

	phead->prev = phead;//头节点不存放数据
	phead->next = phead;


5、节点(结构体)内存空间的开辟

	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->next = NULL;//因为是新节点,都先初始化置为空指针
	newnode->prev = NULL;
	newnode->data = x;//将想要的数据放入进去


二、链表的主体框架

1、整体框架

int main()
{
	int input = 0;
	do
	{
		meun();
		printf("请输入想要进行的操作:");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
		{
			printf("从尾部插入节点:\n");
			testList1();//验证尾插函数
			printf("\n");
			break;
		}
		case 2:
		{
			printf("从头部插入节点:\n");
			testList2();//验证头插函数
			printf("\n");
			break;
		}
		case 3:
		{
			printf("从尾部删除节点:\n");
			testList3();//验证尾删函数
			printf("\n");
			break;
		}
		case 4:
		{
			printf("从头部删除节点:\n");
			testList4();//验证头删函数
			printf("\n");
			break;
		}
		case 5:
		{
			printf("给数据找其节点:\n");
			testList5();//验证查找节点
			printf("\n");
			break;
		}
		case 6:
		{
			printf("修改数据:\n");
			testList6();//验证修改数据
			printf("\n");
			break;
		}
		case 7:
		{
			printf("插入节点:\n");
			testList7();//验证插入节点
			printf("\n");
			break;
		}
		case 8:
		{
			printf("删除节点:\n");
			testList8();//验证删除节点
			printf("\n");
			break;
		}
		case 0:
		{
			printf("退出操作\n");
			break;
		}
		default:
		{
			printf("选择错误,请重新选择操作\n");
			break;
		}
		}
	} while (input);

	return 0;
}


 2、主菜单

//主菜单
void meun()
{
	printf("*****************************************************\n");
	printf("*           1、尾插               2、头插           *\n");
	printf("*           3、尾删               4、头删           *\n");
	printf("*           5、查找               6、修改           *\n");
	printf("*           7、任意数据前插入     8、删除任意数据   *\n");
	printf("*           0、退出                                 *\n");
	printf("*****************************************************\n");
}


 三、功能的实现

1、创建一个新节点的函数

//创建一个新节点的函数
ListNode* BuyListNode(ListDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->next = NULL;//因为是新节点,都先初始化置为空指针
	newnode->prev = NULL;
	newnode->data = x;//将想要的数据放入进去

	return newnode;
}


2、初始化创建头节点的函数

//创建一个新节点的函数
ListNode* BuyListNode(ListDataType x)
{
	ListNode* newnode = (ListNode*)malloc(sizeof(ListNode));
	newnode->next = NULL;//因为是新节点,都先初始化置为空指针
	newnode->prev = NULL;
	newnode->data = x;//将想要的数据放入进去

	return newnode;
}


//初始化创建头节点
ListNode* ListInit()
{
	ListNode* phead = BuyListNode(0);//这里给的数据0是无用的,只是方便使用创建新节点函数
	phead->prev = phead;//头节点里date不把数据0放入(因为无用),并且其内的指针prev和next都指向自己
	phead->next = phead;

	return phead;
}


3、打印节点数据的函数

//打印节点的数据
void ListPrint(ListNode* phead)
{
	assert(phead);//断言函数:防止忘记初始化创建头节点
	ListNode* cur = phead->next;
	printf("Phead <-> ");
	while (cur != phead)//遍历一遍:循环最后一个节点里的指针next,next存储的是头节点phead的地址
	{
		printf("%d <-> ", cur->data);
		cur = cur->next;
	}
	printf("phead\n");
}


4、尾插和头插增加节点的函数

4.1 从尾部插入节点的函数

//尾插函数
void ListPushBack(ListNode* phead,ListDataType x)
{
	assert(phead);//断言函数:防止忘记初始化创建头节点
	ListNode* tail = phead->prev;//尾插前,通过头节点找到最后一个节点tail
	ListNode* newnode = BuyListNode(x);//创建需要插入的新节点

	tail->next = newnode;//使节点phead、tail、newnode里的prev或next指向对应的节点
	newnode->prev = tail;

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


4.2 从头部插入节点的函数

//头插函数
void ListPushFront(ListNode* phead,ListDataType x)
{
	assert(phead);//断言函数:防止忘记初始化创建头节点
	ListNode* first = phead->next;//头插前,通过头节点找到第一个节点first
	ListNode* newnode = BuyListNode(x);//创建需要插入的新节点

	newnode->next = first;//使节点phead、first、newnode里的prev或next指向对应的节点
	first->prev = newnode;

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


5、尾删和头删减少节点的函数

5.1 从尾部删除节点的函数

//尾删函数
void ListPopBack(ListNode* phead)
{
	assert(phead);//断言函数:防止忘记初始化创建头节点
	if (phead->next != phead)//防止链表里一个节点都没有,就不需要删除
	{
		ListNode* tail = phead->prev;//通过头节点phead找到最后一个节点tail
		ListNode* prev = tail->prev;//再通过最后一个节点tail找到其前面的节点prev

		free(tail);//释放最后一个节点的空间
		tail == NULL;

		prev->next = phead;//使节点phead、prev里的指针prev或next指向对应的节点
		phead->prev = prev;
	}
}


 5.2 从头部删除节点的函数

//头删函数
void ListPopFront(ListNode* phead)
{
	assert(phead);//断言函数:防止忘记初始化创建头节点
	if (phead->next != phead)//防止链表里一个节点都没有,就不需要删除
	{
		ListNode* first = phead->next;//通过头节点phead找到第一个节点first
		ListNode* second = first->next;//再通过第一个节点first找到第二个节点second

		free(first);//释放最后一个节点的空间
		first = NULL;

		phead->next = second;//使节点phead、second里的指针prev或next指向对应的节点
		second->prev = phead;
	}
}

​ 


 6、提供一个数据,找到这个数据所在的节点的函数

//提供一个数据,找到这个数据所在的节点的函数
ListNode* ListFindData(ListNode* phead,ListDataType x)
{
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)//遍历:循环最后一个节点里的指针next,next存储的是头节点phead的地址
	{
		if (cur->data == x)
		{
			printf("找到了这个节点\n");
			return cur;
		}
		else
		{
			cur = cur->next;
		}
	}
	printf("找不到这个节点\n");
	return NULL;
}


7、给一个数据,在其所在节点前面插入一个节点的函数

 

//给一个数据,在其所在节点前面插入一个节点的函数
void ListErase(ListNode* phead, ListDataType x, ListDataType y)//x是查找的节点数据,y是新节点的数据
{
	assert(phead);
	ListNode* pos = ListFindData(phead, x);
	if (pos != NULL)//防止没找到节点,就不用继续插入
	{
		ListNode* prev = pos->prev;//通过节点pos找到它前面的节点prev
		ListNode* newnode = BuyListNode(y);//创建新节点

		newnode->prev = prev;//使节点prev、newnode、pos里的指针prev或next指向对应的节点
		prev->next = newnode;

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

8、给一个数据,删除这个数据所在的节点的函数

//给一个数据,删除这个数据所在的节点的函数
void ListInsert(ListNode* phead, ListDataType x)
{
	assert(phead);
	ListNode* pos = ListFindData(phead, x);
	if (pos != NULL)//防止没找到节点,就不用删除
	{
		ListNode* prev = pos->prev;//通过节点pos找到它前面的节点prev
		ListNode* next = pos->next;//通过节点pos找到它后面的节点next

		free(pos);//释放节点pos的空间
		pos = NULL;

		prev->next = next;//使节点prev、next里的指针prev或next指向对应的节点
		next->prev = prev;
	}
}

 9、修改数据的函数

 

//修改数据的函数
void ListChangeData(ListNode* phead,ListDataType x)
{
	int data = 0;//定义想要改成的数据
	assert(phead);
	ListNode* cur = phead->next;
	while (cur != phead)//遍历:循环最后一个节点里的指针next,next存储的是头节点phead的地址
	{
		if (cur->data == x)
		{
			printf("找到了这个数据,请输入想要改成的数据:");
			scanf("%d", &data);
			cur->data = data;
			break;
		}
		else
		{
			cur = cur->next;
		}
	}
	if (cur == phead)
	{
		printf("想要修改的数据不存在\n");
	}
}

10、部分功能的简化与升级

 10.1 尾插和头插函数的简化升级

//尾插函数
void ListPushBack(ListNode* phead, ListDataType x)
{
	ListInsert(phead, x);
}

//头插函数
void ListPushFront(ListNode* phead, ListDataType x)
{
	ListInsert(phead->next, x);
}

 四、总代码


 五、代码运行实例

举报

相关推荐

0 条评论