线性表-循环链表

循环链表的定义

将单链表中最后一个数据元素的next指针指向第一个元素,即把链表的两头连接,形成了一个环状链表,称为循环链表

在循环链表中可以定义一个“当前”指针,称为游标,通过游标来遍历链表中所有元素;

循环链表和动态链表相比,唯一的不同就是循环链表首尾相连,其他都完全一样

可以用两次打印循环链表,直观看下是否为循环链表

循环链表的应用-约瑟夫环问题

   约瑟夫环问题,是一个经典的循环链表问题,题意是:已知 n 个人(以编号1,2,3,…,n分别表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,要求找到最后出列的那个人。

例如:n=7;从k=1开始顺时针报数,数到m=3的那个人出列 ->3出列

#include<stdio.h>
#include<malloc.h>

typedef struct node{
	int number;
	struct node *next;
}person;

循环链表初始化

person *initLink(int n){
	person *head = (person *)malloc(sizeof(person)); //结构体头指针分配内存空间 
	head->number =1;  //数据域值1 
	head->next = NULL;  //指针域指向NULL 
	person *cyclic = head; //head首地址赋值给cyclic  
	int i;
	for(i=2;i<=n;i++){
		person *body = (person *)malloc(sizeof(person)); //添加结点 
		body->number =i;                                //数据域赋值 
		body->next = NULL;                              //指针域指向NULL 
		cyclic->next = body;                           // 将新建结点添加到圆 
		cyclic = cyclic->next;                         
	}
	cyclic->next = head;  //首尾相连
	return head; 
}

找打该元素并出列

void findAndOut(person *head,int k, int m){
	person *tail = head;
	printf("head %d\n", tail->number);
	//找到链表第一个结点的上一个结点,为删除操作做准备
	while(tail->next != head){
		tail = tail->next;
	} 
	person *p =head;
	//找到编号为k的人
	while(p->number !=k){
		tail = p;
		p=p->next;
	} 
	//从编号为k的人开始,只要符合条件p->next == p时,说明链表中除了p结点,所有编号都出列了
	while(p->next !=p){
		//找到从p报数1开始,包m的人,并且还要知道m-1的人的位置tail,方便做删除操作。
		int i;
		for(i=1;i<m;i++){
			tail = p;
			p=p->next;
		} 
		tail ->next = p->next;   //从链表上将p结点摘下来
		printf("出列人的编号为:%d\n",p->number);
		free(p);
		p = tail->next;//继续使用p指针指向列出编号的下一个编号,游戏继续 
	} 
	printf("出列人的编号为:%d\n",p->number);
	free(p);
}

完整代码

#include<stdio.h>
#include<malloc.h>

typedef struct node{
	int number;
	struct node *next;
}person;
person *initLink();
void findAndOut();
void display();

int main(){
	printf("输入圆桌上的人数:\n");
	int n;
	scanf("%d", &n);
	person *head = initLink(n);
	
	printf("从第k人开始报数(k>1且k<%d):",n);
//	display(head);
	int k;
	scanf("%d", &k);
	printf("数到m的人出列:\n");
	int m;
	scanf("%d",&m);
	findAndOut(head,k,m); 
} 

person *initLink(int n){
	person *head = (person *)malloc(sizeof(person)); //结构体头指针分配内存空间 
	head->number =1;  //数据域值1 
	head->next = NULL;  //指针域指向NULL 
	person *cyclic = head; //head首地址赋值给cyclic  
	int i;
	for(i=2;i<=n;i++){
		person *body = (person *)malloc(sizeof(person)); //添加结点 
		body->number =i;                                //数据域赋值 
		body->next = NULL;                              //指针域指向NULL 
		cyclic->next = body;                           // 将新建结点添加到圆 
		cyclic = cyclic->next;    
		//printf("cyclic:%d \n", cyclic->number);                     
	}
	printf("cyclic:%d \n", cyclic->number);  
	cyclic->next = head;  //首尾相连
	return head; 
}

//链表显示
void display(person *p){
	person* temp = p;    //将temp指针重新指向头结点
	//只要temp指针指向的结点next不是NULL,就执行输出语句。
	while(temp->next)
	{
		temp = temp->next; //这里指向首元结点,头结点一般为空 
		printf("%d ",temp->number);
	 } 
	 printf("\n");
}

void findAndOut(person *head,int k, int m){
	person *tail = head;
	printf("head %d\n", tail->number);
	//找到链表第一个结点的上一个结点,为删除操作做准备
	while(tail->next != head){
		tail = tail->next;
	} 
	person *p =head;
	//找到编号为k的人
	while(p->number !=k){
		tail = p;
		p=p->next;
	} 
	//从编号为k的人开始,只要符合条件p->next == p时,说明链表中除了p结点,所有编号都出列了
	while(p->next !=p){
		//找到从p报数1开始,包m的人,并且还要知道m-1的人的位置tail,方便做删除操作。
		int i;
		for(i=1;i<m;i++){
			tail = p;
			p=p->next;
		} 
		tail ->next = p->next;   //从链表上将p结点摘下来
		printf("出列人的编号为:%d\n",p->number);
		free(p);
		p = tail->next;//继续使用p指针指向列出编号的下一个编号,游戏继续 
	} 
	printf("出列人的编号为:%d\n",p->number);
	free(p);
}


循环链表插入元素

1)普通插入元素(和单链表一样)

2)尾插法:(和单链表一样,单链表写法支持尾插法;因为辅助指针后跳length次,指向最后面那个元素)

在尾部插入结点,将新增结点的next域指针指向头结点;

3)头插法

(要进行头插法,需要求出尾结点,与单链表不一样,保证是循环链表)

第一次插入元素是,让游标指向0号结点

新增结点A插入在头结点之后,头结点的next指针指向新增结点A,A指向“首元结点”,表尾结点指向新增结点A;

完成插入操作之后,新增结点A变成了新的首元结点;

4)第一次插入元素

此时链表为空,只有一个头结点(一般头结点不辅助),新增结点为首元结点;

头结点next域指针指向首元结点,

line *head =NULL;
head = (line *)malloc(sizeof(line));
head->next = NULL;
line *temp =head; //遍历结点
line *body = (line *)malloc(sizeof(line));
body->number = 1;
body->next = NULL;

temp->next = body;
body->next = body;

循环链表删除元素

1)删除普通结点

2)删除头结点(删除0号位置处元素),需要求出尾结点

循环链表优缺点:

优点:功能增强,在单链表的基础做了一个加强,可以取代单链表使用;其next和Current操作能高效的遍历链表中所有元素;

缺点:代码复杂度变高;(有吗,相比于单链表,循环链表是把单链表首尾相连了。。)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值