约瑟夫问题/猴王问题(两种种实现方法)

问题描述:
在这里插入图片描述
一、循环链表实现约瑟夫问题
思路:
创建一个循环链表,按顺序依次列入结点。进行遍历,遍历为第 m 个的结点删除,连接该节点的前一个和后一个结点,保持链表的完整性

循环链表实现的关键点:
1、如何搭建循环链表
2、如何查找并且删除指定结点,且不影响整个链表的完整性
实现:
循环链表的搭建(在研究此代码前,建议先了解一下链表的原理图像)

Link headnode = (Link)malloc(sizeof(Monkey));
headnode->next = NULL;
Link tailNode,pNode,qNode;
while(1){
    
    
	scanf("%d %d",&n,&m);
	if(n==0 || m==0){
    
    
		free(headnode);
		break;
	}else{
    
    
		tailNode = headnode;   //tailNode指针一定要在这里进行初始化,如果在循环外初始化,那么第一次
								//约瑟夫环完成之后尾结点就无法初始化指向头结点,后续就不能重新插入结点 
		while(i<=n){
    
               //循环,插入结点 
			pNode = (Link)malloc(sizeof(Monkey))
			pNode->data = i;      
	        //尾插法插入结点 
			tailNode->next = pNode;         //将pNode放入链表中
			pNode->next = headnode->next;   //关键:令最后一个结点的指针域指向第一个结点的位置,构成循环 
			tailNode = pNode;               //令尾指针指向新的结点pNode      
			i++;
 		}
 	}
 }

查找并删除结点

	Link headnode = (Link)malloc(sizeof(Monkey));
	headnode->next = NULL;
	Link tailNode,pNode,qNode;
	int answer[300];
	int count = 0;
	int n,m;
	int i=1;
	while(1){
    
    
 			pNode = headnode->next;            //对要进行遍历查找的指针进行初始化
 			qNode = tailNode;                  //这个指针指向遍历查找的指针的前一个结点,便于结点的删除操作 
 			while(pNode != qNode){
    
                 //循环,如果pNode != qNode,即链表不只含有一个结点时 
 				if(i!=m){
    
                          //判断,一个一个数,如果没有数到第 m 个,就都向后移动一位 
 					qNode = pNode;
 					pNode = pNode->next;
					i++; 
				}else{
    
    
					qNode->next = qNode->next->next;   //数到了,令查找的指针pNode的前一个指针pNode的指针域指向 pNode的
					                                   //下一个结点的指针域,即将pNode结点跳过 
					free(pNode);                  //删除节点 
					pNode = qNode->next;          //再初始化pNode结点,继续数 
					i = 1; 
				}
			}
		}
	}

源码:

#include<stdio.h>
#include<malloc.h>
typedef struct monkey{
    
    
	int data;
	struct monkey* next;
}Monkey,*Link;
int main(){
    
    
	Link headnode = (Link)malloc(sizeof(Monkey));
	headnode->next = NULL;
	Link tailNode,pNode,qNode;
	int answer[300];
	int count = 0;
	int n,m;
	int i=1;
	while(1){
    
    
		scanf("%d %d",&n,&m);
		if(n==0 || m==0){
    
    
			free(headnode);
			break;
		}else{
    
    
			tailNode = headnode;   //tailNode尾指针一定要在这里进行初始化,如果在循环外初始化,那么第一次约瑟夫环完成之后尾指针就无法初始化指向头结点,后续就不能重新插入结点 
			while(i<=n){
    
               //循环,插入结点 
				pNode = (Link)malloc(sizeof(Monkey));
				pNode->data = i;                //尾插法插入结点 
				tailNode->next = pNode;
				pNode->next = headnode->next;   //令最后一个结点的指针域指向第一个结点的位置,构成循环 
				tailNode = pNode;               //令尾指针指向新的结点pNode      
				i++;
 			}
 			pNode = headnode->next;            //对要进行遍历查找的指针进行初始化 
 			i = 1;
 			qNode = tailNode;                  //这个指针指向遍历查找的指针的前一个结点,便于结点的删除操作 
 			while(pNode != qNode){
    
                 //循环,如果pNode != qNode,即链表不只含有一个结点时 
 				if(i!=m){
    
                          //判断,一个一个数,如果没有数到第 m 个,就都向后移动一位 
 					qNode = pNode;
 					pNode = pNode->next;
					i++; 
				}else{
    
    
					qNode->next = qNode->next->next;   //数到了,令查找的指针pNode的前一个结点pNode的指针域指向 pNode的下一个结点的指针域,即将pNode结点跳过 
					free(pNode);                  //删除节点 
					pNode = qNode->next;          //再初始化pNode结点,继续数 
					i = 1; 
				}
			}
		}
		headnode->next = qNode;                //该语句是保障头结点与最后留下的结点保持链接 
		answer[count] = pNode->data;           //用数组记录此次循环找到的“猴王” 
		count++;
		free(pNode);
		headnode->next = NULL;                 //再初始化头结点 
	}
	for(int j=0;j<count;j++)                   //遍历每次找到的猴王 
		printf("%d\n",answer[j]);
	return 0;
} 

二、数组下标实现约瑟夫问题
思路:定义一个数组,初始化存在猴子的元素。遍历时如果元素为0,则跳过,如果元素不为0,则将该元素变为0,直到数组中只剩下一个数组元素不为0
关键:
在遍历到最后一个元素的时候,如何回到第一个元素再遍历
如何在遍历到最后一个元素后再回到第一个元素

	int n,m,Monkey;
	int count = 0;
	int answer[count];
	int i = 1;
	int pos;
	while(1){
    
    
			i = 1;
			Monkey = n;
			pos = 0;                        //下标标志,重中之重的标志 
			while(Monkey>1){
    
                    //循环条件:如果猴子不止一个 
				if(monkey[pos]>0){
    
              //判断,数组中这个位置的元素是不是为零,若为零,则这里的猴子被丢出去了;不为零,则有猴子存在 
					if(i != m){
    
                     //再进行判断,如果这个位置不是要数到的第 m 个,则往后 pos 移动一位 
						i++;
						pos = (pos+1)%n;        //关键:它使得每次下标都会移动后一位,而且如果下标指向了最后一个猴子,那么他就会移
						                        //动到数组的第一个位置,即第一个猴子的位置 
					}else{
    
                         
						monkey[pos] = 0;       //如果判断成功,是第 m 个,那么将这个位置的猴子丢了,并且数组元素赋值为 0 ,表示没有猴子 
						i = 1;
						Monkey--;
						pos = (pos+1)%n;
					}
				}else{
    
    
					pos = (pos+1)%n;
				}
			}
	    } 

源码:

#include<stdio.h>
int main(){
    
    
	int n,m,Monkey;
	int count = 0;
	int answer[count];
	int i = 1;
	int pos;
	while(1){
    
    
		scanf("%d %d",&n,&m);
		if(n==0 || m==0)
			break;
		else{
    
    
			int monkey[301] = {
    
    0};   //数组初始化 
			for(int j=0;j<n;j++){
    
    
				monkey[j] = i;
				i++;
			}
			i = 1;
			Monkey = n;
			pos = 0;                        //下标标志,重中之重的标志 
			while(Monkey>1){
    
                    //循环条件:如果猴子不止一个 
				if(monkey[pos]>0){
    
              //判断,数组中这个位置的元素是不是为零,若为零,则这里的猴子被丢出去了;不为零,则有猴子 
					if(i != m){
    
                 //再进行判断,如果这个位置不是要数到的第 m 个,则往后 pos 移动一位 
						i++;
						pos = (pos+1)%n;    //这个语句非常重要,它使得每次下标都会移动后一位,而且如果下标指向了最后一个猴子,那么他就会移动到数组的第一个位置,即第一个猴子的位置 
					}else{
    
                      ///如果判断成功,是第 m 个,那么将这个位置的猴子丢了,并且数组元素赋值为 0 ,表示没有猴子 
						monkey[pos] = 0;   
						i = 1;
						Monkey--;
						pos = (pos+1)%n;
					}
				}else{
    
    
					pos = (pos+1)%n;
				}
			}
			for(int j=0;j<n;j++){
    
                   //遍历猴子数组,将不为 0 的数组元素赋值到answer[]数组中 
				if(monkey[j]>0){
    
    
					answer[count] = monkey[j];
					count++;
				}
			} 
		}
	} 
	for(int j=0;j<count;j++)
		printf("%d\n",answer[j]);
	return 0;
}

可以复制这个代码在编译软件再研究,如果仍然有问题,推荐一个视频,这个视频讲的非常的清晰
懒猫老师约瑟夫问题

猜你喜欢

转载自blog.csdn.net/Cristiano_san/article/details/106525511