数据结构之双向循环链表

[写文目的]

为了更深入的了解链表,于是在这里写出单向链表的升级版双向循环链表!

[链表介绍]

1.建立链表

首先需要定义一个结构,一般双向循环链表的结构用于存放储存在该节点的值,一个prev指针指向前一个节点,一个rnext指针指向下一个节点,对用结构代码如下:

typedef struct Node* node;
struct Node{
	int data;
	node prev;
	node next;
};
创建一个 双向循环链表的代码如下

node creatList(){
	node L=(node)malloc(sizeof(Node));
	L->prev=NULL;
	L->next=NULL;
	return L;
}
2.插入元素

示意图为

数据结构之双向循环链表_第1张图片

代码为:

void insertItem(node L,int n){
	node tempNode=creatList();
	tempNode->data=n;
	L->next->prev=tempNode;
	tempNode->next=L->next;
	L->next=tempNode;
	tempNode->prev=L;
}
3.删除元素

示意图为:

数据结构之双向循环链表_第2张图片
代码为:

void deleteItem(node L,int n){
	node head=L;//保存L(相当于头结点),在循环链表这一步也可以去掉
	while(L->data!=n){
		L=L->next;
	}//这里默认n值在链表中存在
	//找到存储n的节点后
	node p=L;
	L->next->prev=L->prev;
	L->prev->next=L->next;
	free(p);
	L=head;
}
[总结]

对于双向循环链表,我使用的测试代码为

#include<stdio.h>
#include<malloc.h>
typedef struct Node* node;
struct Node{
	int data;
	node prev;
	node next;
};

node creatList(){
	node L=(node)malloc(sizeof(Node));
	L->prev=L;
	L->next=L;
	return L;
}
void insertItem(node L,int n){
	node tempNode=creatList();
	tempNode->data=n;
	L->next->prev=tempNode;
	tempNode->next=L->next;
	L->next=tempNode;
	tempNode->prev=L;
}
void deleteItem(node L,int n){
	node head=L;//保存L(相当于头结点),在循环链表这一步也可以去掉
	while(L->data!=n){
		L=L->next;
	}//这里默认n值在链表中存在
	//找到存储n的节点后
	node p=L;
	L->next->prev=L->prev;
	L->prev->next=L->next;
	free(p);
	L=head;
}
int main(){
	node n;
	n=creatList();
	n->data=4;
	insertItem(n,5);
	insertItem(n,8);
	insertItem(n,6);
	insertItem(n,9);
	deleteItem(n,8);
	for(int i=0;i<12;i++){
		printf("%d\n",n->data);
		n=n->next;
	}
	return 0;
}


[问题提出]

对于该数据结构,我引入一个题目,便于更好的掌握双向循环链表!

题目描述:
编号为 1 - n 的 n 个人按顺时针方向围坐一圈,从第一个人开始按顺时针方向自 1 开
始报数,报到 m 时停止,在报到 m 的那个人的左右的两个人出列,从他在顺时针方向下一
个人开始重新从 1 报数,如此下去。如果还在游戏的人数少于 3 个,则游戏终止。在一行
内输出最后留下来的人编号
★数据输入
输入第一行为 T ,代表接下去有 T 组测试, 0 < T < =10
接下去 T行,每行两个数代表 n 和m,0 < n,m < =1000
★数据输出
对于每个输入,在一行内输出最后留下来的人编号,如果有两个人留下来,则用空格隔
开,并按从小到大排序。
输入示例      输出示例
3              
1 2                 1
3 1                 1
4 2                 2 4

[解题思路]

首先我们需要建立双循环链表,节点存储编号n,以及left跟right(也就相当于prev跟next)用于指向左右节点

typedef struct LinkedList* List;
struct LinkedList
{
	List right;
	List left;
	int n;
};
建立循环链表,由于是1-n的顺序,所以我们是在链表尾部,对应代码为:

List creatList(int peopleNum)//传入参与游戏的人数
{
	List mainList;
	List leftList;
	List headList;        //保存指向第一个节点的指针,后面用于返回
	headList=mainList=leftList=NULL;
	for(int i=1;i<=peopleNum;i++)//依次增加节点
	{
		leftList=(List)malloc(sizeof(LinkedList));
		leftList->n=i;
		if(mainList==NULL){ 
			mainList=headList=leftList; //保存第一个节点
		}
		else {//在mainList后面依次增加节点
			mainList->left=leftList;
			leftList->right=mainList;
			mainList=leftList;
		}
	}
	mainList->left=headList;  //这两段代码用于首尾相连,构成循环链表
	headList->right=mainList;
	return headList;    //返回第一个节点
}

查找并依次删除符合点检的节点的左右节点

void searchList(List mainList,int n,int m)
{
	while(n>2)//当目前参与游戏的人数大于2的时候进行以下的操作
	{
		int mTemp=(m%n);
		while(mTemp--)//找到符合删除条件的节点
		{
			mainList=mainList->left;
		}
		List p,q;//待删节点,为mainList的左节点和右节点
		p=mainList->right;
		q=mainList->left;
		mainList->right=p->right;
		p->right->left=mainList;
		
		mainList->left=q->left;
		q->left->right=mainList;
		free(p);
		free(q);
		n-=2;//删除操作完成后人数减去两个人
	}
	if(n==1) printf("%d\n",mainList->n);
	else printf("%d %d\n",(mainList->n)>(mainList->left->n)?(mainList->left->n):(mainList->n),\
		(mainList->n)<(mainList->left->n)?(mainList->left->n):(mainList->n));
}
这个题目的难点就是以上的建立链表一个删除符合条件的节点两个,对应的完整代码如下:

#include<stdio.h>
#include<malloc.h>
typedef struct LinkedList* List;
struct LinkedList
{
	List right;
	List left;
	int n;
};
List creatList(int peopleNum)//传入参与游戏的人数
{
	List mainList;
	List leftList;
	List headList;        //保存指向第一个节点的指针,后面用于返回
	headList=mainList=leftList=NULL;
	for(int i=1;i<=peopleNum;i++)//依次增加节点
	{
		leftList=(List)malloc(sizeof(LinkedList));
		leftList->n=i;
		if(mainList==NULL){ 
			mainList=headList=leftList; //保存第一个节点
		}
		else {//在mainList后面依次增加节点
			mainList->left=leftList;
			leftList->right=mainList;
			mainList=leftList;
		}
	}
	mainList->left=headList;  //这两段代码用于首尾相连,构成循环链表
	headList->right=mainList;
	return headList;    //返回第一个节点
}

void searchList(List mainList,int n,int m)
{
	while(n>2)//当目前参与游戏的人数大于2的时候进行以下的操作
	{
		int mTemp=(m%n);
		while(mTemp--)//找到符合删除条件的节点
		{
			mainList=mainList->left;
		}
		List p,q;//待删节点,为mainList的左节点和右节点
		p=mainList->right;
		q=mainList->left;
		mainList->right=p->right;
		p->right->left=mainList;
		
		mainList->left=q->left;
		q->left->right=mainList;
		free(p);
		free(q);
		n-=2;//删除操作完成后人数减去两个人
	}
	if(n==1) printf("%d\n",mainList->n);
	else printf("%d %d\n",(mainList->n)>(mainList->left->n)?(mainList->left->n):(mainList->n),\
		(mainList->n)<(mainList->left->n)?(mainList->left->n):(mainList->n));
}
int main()
{
	int m,n;
	int num;
	scanf("%d",&num);
	while(num--){
		scanf("%d%d",&n,&m);
		List list;
		list=creatList(n);
		searchList(list->right,n,m);
	}
}





你可能感兴趣的:(数据结构,算法,链表)