约瑟夫循环链表(C语言)

约瑟夫问题:输入一个数n,表述人数,再输入一个目标值target,从第一个人开始每数到target就将数到的人踢出去,直到剩余最后一个,用循环链表解决。

谈到链表,那么就一定有结构体及结构体指针,我们先声明一个结构体:

typedef struct Node{
	int num;          //人数序号
	struct Node *next;//结构体指针
}node;

结构体中包含了两个元素:序号和指针。这里的指针,是指向下一个结构体指针,后面还会更多讲解。

结构体声明完毕,完成了链表创建的前置步骤,接下来我们创建链表:

node *CreatlistNode(int n){
	node *head=NULL;       //头指针
	node *prev=NULL;       //前置指针
	for(int i=1;i<=n;i++){
		node *new_node=(node*)malloc(sizeof(node));  //新链表节点创建
		new_node->num=i;      //该节点中序号赋值
		if(head==NULL){       //创建第一个节点
			head=new_node;
		}else{                //链接下一个节点
			prev->next=new_node; 
		}
		prev=new_node;     //更新前置指针
	}
	prev->next=head;      //尾节点指向头节点
	return head;
}

头指针:一个链表开始的地方,丢失头指针意味着丢失整个链表。

前置指针:指向当前指针的上一个指针

新链表节点创建:链表创建核心,采用malloc函数赋予每个节点各自的内存

创建第一个节点/链接下一个节点:创建第一个节点时,头指针一定是指向NULL,随后前置指针指头指针(更新前置指针),下一次创建节点时,前置指针便可以链接当前的new_node指针(指向new_node地址),实现节点之间的链接。当循环完后,完成了从头指针到尾指针的链接,还需要将尾指针与头指针链接起来,形成闭合环。链表创建完毕。返回头指针。

这里也许有些同学会有疑问,为什么new_node能重新创建?不是同一个名字吗?链表如何实现的?这里需要注意,new_node是一个指针,它指向一个地址,当new_node被创建时,实际上是一段新内存被创建,new_node指向这段地址没错,当prev指向head时,实际上指的是这段地址,此后再重新创建new_node,实际上就是new_node指向另一个地址,有独立的内存,这种方法有赖于malloc函数的特性,然后由于prev还未更新,指向的是上一个地址,就可以以prev->next=new_node的方式链接上一个节点和当前节点,此后prev更新,指向当前地址,由此完成链表节点的链接。

链表创建完毕,接下来就是运用。

void ysf(int n,int target){
	node *current=CreatlistNode(n);    //创建头指针(也是当前节点),同时创建链表,
	node *prev=NULL;                   //前置指针
	while(current->next!=current){     //若当前节点的下一个节点不为本身
		for(int i=1;i<target;i++){     //遍历target-1个节点,找出第target个节点
			prev=current;              //更新前置指针,指向当前节点
			current=current->next;     //更新当前节点
		}
		printf("%d -> ",current->num);
		prev->next=current->next;      //将前置指针与当前节点指向的下一个节点链接
		free(current);                 //释放当前节点内存
		current=prev->next;            //更新当前节点
		
	}
	printf("%d\n",current->num);     //最后一个节点
	printf("赢家是%d",current->num);
	free(current);                 //释放当前节点内存
}

函数有两个形参,n是用于链表的创建,表示节点个数,target用于循环查找,每到第target个节点就删除该节点。

第一步,创建头指针,更合理的说法应该是当前节点,因为它总是指向当前的节点,最先指向头节点,因此也可以说是头指针。

然后创建前置指针。

随后进入链表循环,结束条件为还剩最后一个节点时跳出,意味着当前指针指向自己,用for循环进行循环查找,遍历target-1个节点(在循环中,prev前置指针总是指向当前指针的上一个节点),此时当前指针指向的是第target节点,于是可以将前置节点与当前节点指向的下一个节点链接,然后释放当前节点内存,更新当前指针,指向当前节点指向的下一个节点,实现节点的删除。

此外,千万注意内存的释放,有malloc函数就一定要有free函数,防止内存泄漏,要养成良好习惯。

#include "stdio.h"
#include "stdlib.h"

typedef struct Node{
	int num;
	struct Node *next;
}node;

node *CreatlistNode(int n){
	node *head=NULL;
	node *prev=NULL;
	for(int i=1;i<=n;i++){
		node *new_node=(node*)malloc(sizeof(node));
		new_node->num=i;
		if(head==NULL){
			head=new_node;
		}else{
			prev->next=new_node; 
		}
		prev=new_node;
	}
	prev->next=head;
	return head;
}

void ysf(int n,int target){
	node *current=CreatlistNode(n);
	node *prev=NULL; 
	while(current->next!=current){
		for(int i=1;i<target;i++){
			prev=current;
			current=current->next;
		}
		printf("%d -> ",current->num);
		prev->next=current->next;
		free(current);
		current=prev->next;
		
	}
	printf("%d\n",current->num);
	printf("赢家是%d",current->num);
	free(current);
}


int main(){
	int n,target;
	printf("请输入人数:");
	scanf("%d",&n);
	printf("\n请输入目标值: ");
	scanf("%d",&target);
	ysf(n,target);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值