目录
一、带头双向循环链表的处理和介绍
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);
}四、总代码
五、代码运行实例











