2009年02月24日星期二下午 05:03
问题描述:约瑟夫环问题;有N个人围成一个环,从第一个人开始报数,报到M 的人退出环,并且由他的M值来代替原有的M值,要求输出离开环的顺序。#include
#include
using namespace std;
//结点中数据域的类型定义
typedef struct
{
int number;//标号
int chipher;//手中的值
}DataType;
//带头结点的单循环链表
typedef struct node
{
DataType data;
struct node *next;
}Scnode;
//初始化
void MyInit(Scnode **head)//指向指针的指针。
{
if((*head=(Scnode *)malloc(sizeof(Scnode)))==NULL) exit(1);//动态分配 (*head)->next=*head;
}
//插入
int MyInsert(Scnode *head,int i,DataType x)
{
Scnode *p,*q;
int j;
p=head->next;j=1;
while(p->next!=head&&j { p=p->next; j++; } if(j!=i-1&&i!=1) {cout<<"erro!!!!!!!!!"; return 0;} if((q=(Scnode *)malloc(sizeof(Scnode)))==NULL) exit(1); q->data=x; q->next=p->next; p->next=q; return 1; } //删除 int MyDelete(Scnode *head,int i,DataType *x) { Scnode *p,*q; int j; p=head;j=1; while(p->next!=head&&j { p=p->next; j++; } if(j!=i-1) {cout<<"erro!!!!!!!!!"; return 0;} q=p->next; *x=q->data; p->next=p->next->next; free(q); return 1; } //取数据元素 int MyGet(Scnode *head,int i,DataType *x) { Scnode *p; int j; p=head;j=0; while(p->next!=head&&j {p=p->next;j++;} if(j!=i) {cout<<"erro!!!!!!!!!"; return 0;} *x=p->data; return 1; } //判断是否为空 int MyNotEmpty(Scnode *head) { if(head->next==head) return 0; else return 1; } //删除P结点所指结点的下一个结点(也就是下面函数中的pre结点的下一个结点) void MyDelete(Scnode *p) { Scnode *q=p->next; p->next=p->next->next; free(q); } //关键的函数 void MyRing(Scnode *head,int m) { Scnode *pre,*curr; int i; pre=head; curr=head->next; while(MyNotEmpty(head)==1)//这个喜欢是外层的把人循环完 { for(int i=1;i pre=curr; curr=curr->next; if(curr==head)//防止curr结点指向head,为什么呢,因为curr=curr-next移动过程中是要计数的 { pre=curr; curr=curr->next; } } cout<<" "< m=curr->data.chipher;//这里重新赋值到新的密码m,注意此处的写法curr- >data.chipher curr=curr->next;//从下一个结点开始计数 if(curr==head) curr=curr->next;//问题同上。 MyDelete(pre);//删除被指定到的结点 } cout< } void main() { DataType test[7]={{1,3},{2,1},{3,7},{4,2},{5,4},{6,8},{7,4}}; int n=7,m=20,i; Scnode *head; MyInit(&head); for(i=1;i<=n;i++) MyInsert(head,i,test[i-1]); MyRing(head,m); } 数据结构:约瑟夫环问题的求解,链表数组,简单高效 2008-03-25 14:59 约瑟夫问题:12个人排成一圈,从1号报数,凡是数到5的人就走出队列(出局),然后继续报数,求最后一个出局的人。 编号为1,2,......,n的n个人按照顺时针方向围坐一圈。从第一个人开始顺时针方向自1开始报数,报到m时停止报数。报m 的人出列,从他在顺时针方向的下一个人开始重新报数,如此下去,直到所有人全部出列为止。方法1: /** * 名称: * * 描述: * * Copyright: Copyright 2008 * 创建日期 Mar 25, 2008 * 作者 zhangtianshun * E-mail zhangts8888@https://www.doczj.com/doc/1d6507557.html, * 版本 1.0 */ public class JosephCircle { public JosephCircle() { // TODO Auto-generated constructor stub } public static int josephCircle(int totalNum, int perNum) { int count[] = new int[totalNum]; for (int i = 0; i < count.length; i++) { count[i] = i + 1; } int flag = 0; int k = 0, n = 0; while (flag != 11) { for (k = 0; k < count.length; k++) { if (count[k] != 0) { n++; if (n % perNum == 0) { count[k] = 0; flag++; } } } } for (k = 0; k < count.length; k++) { if (count[k] != 0) { n = count[k]; break; } } return n; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub int total = 12; int per = 5; JosephCircle joseph = new JosephCircle(); int last = -1; last = joseph.josephCircle(total, per); System.out.println("最后出列人的编号是: " + last); } } 方法2: /** * 名称: * * 描述: * * Copyright: Copyright 2008 * 创建日期 Mar 25, 2008 * 作者 zhangtianshun * MSN zhangts8888@https://www.doczj.com/doc/1d6507557.html, * E-mail zhangts8888@https://www.doczj.com/doc/1d6507557.html, * 版本 1.0 */ public class Josephus { /** * 约瑟夫环的求解思路:最简单的方法是用循环链表实现. * 现在用数组实现链表的效果.数组下标+1为数字编号, * 数组的值为下一个数组的地址.这样实现了链表的效果. * 当报数count=step-1时,删除下一个节点,节点值赋为-1. * 以此循环,直到数组元素都值为-1,最后删除的即为结果. */ public Josephus() { super(); } /** * 约瑟夫问题的求解 * @param array 待处理的整形数组 * @param size 待处理的数组长度 * @param step 步长 * @return Int 最后出列人的编号. */ public int joseph(int array[],final int size,final int step) { int flag = -1; //初始化数组链表,数组元素的值为下一个节点下标//即为: 1->2->3......(size-1)->0 for(int i=0;i array[i] = i+1; array[size-1] = 0; /** * intRemain 当前剩下的有效节点 * intDeleted 最好删除的一个节点的下班 * intCurrent 当前数组元素的下标值 * intCount 计数器 */ int intRemain,intDeleted,intCurrent,intCount; intRemain = size; intDeleted = -1; intCurrent = 0; intCount = 0; do { //当前节点有效的标准是:不等于-1(flag) if(array[intCurrent] != flag) { intCount++; //删除一个节点 if(step - intCount == 1) { //删除后一个节点,主要是将当前节点的向量指向下一个节点的下一个节点intDeleted = array[intCurrent]; array[intCurrent] = array[intDeleted]; array[intDeleted] = -1; intRemain--; intCount = 0; } } //当删除最好一个节点元素后,intCurrent的值变为-1.这之后数组就不能再被访问了. intCurrent = array[intCurrent]; }while(intRemain !=0 ); return intDeleted +1 ; } /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub char num = '7'; System.out.println((int)num); int array[] = new int[12]; //人数 int size = 12; //出局的倍数 int step = 5; int last = -1; Josephus ysf = new Josephus(); last = ysf.joseph(array, size, step); System.out.println("最后出列人的编号是: "+last); 单循环链表解决约瑟夫环问题] 约瑟夫问题的:编号为1,2,....,N的N个人按顺时针方向围坐一圈,每人持有一个密码(正整数),一开始任选一个正整数作为报数上限值M,从第一个人开始按顺时针方向自1开始顺序报数,报到M时停止报数。报M的人出列,将他的密 码作为新的M值,从他在顺时针方向上的下一个人开始重新从1报数,如此下去,直至所有人全部出列为止。试设计一个程序求出出列顺序。 解决思路还是很简单的,主要是要会熟练运用单循环链表的数据结构,通过 单循环链表模拟围坐的一圈人,然后根据相应的密码进行报数,然后删除相应的链表节点。下面是C代码: #include #include #define MAX_NODE_NUM 100 #define TRUE 1U #define FALSE 0U typedef struct NodeType { int id; int cipher; struct NodeType *next; } NodeType; /* 创建单向循环链表 */ static void CreaList(NodeType **, const int); /* 运行"约瑟夫环"问题 */ static void StatGame(NodeType **, int); /* 打印循环链表 */ static void PrntList(const NodeType *); /* 得到一个结点 */ static NodeType *GetNode(const int, const int); /* 测试链表是否为空, 空为TRUE,非空为FALSE */ static unsigned EmptyList(const NodeType *); int main(void) { int n, m; NodeType *pHead = NULL; while (1) { printf("请输入人数n(最多%d个): ", MAX_NODE_NUM); scanf("%d", &n); printf("和初始密码m: "); scanf("%d", &m); if (n > MAX_NODE_NUM) { printf("人数太多,请重新输入!\n"); continue; } else break; } CreaList(&pHead, n); printf("\n------------ 循环链表原始打印 -------------\n"); PrntList(pHead); printf("\n-------------删除出队情况打印 -------------\n"); StatGame(&pHead, m); } static void CreaList(NodeType **ppHead, const int n) { int i, iCipher; NodeType *pNew, *pCur; for (i = 1; i <= n; i++) { printf("输入第%d个人的密码: ", i); scanf("%d", &iCipher); pNew = GetNode(i, iCipher); if (*ppHead == NULL) { *ppHead = pCur = pNew; pCur->next = *ppHead; } else { pNew->next = pCur->next; pCur->next = pNew; pCur = pNew; } } printf("完成单向循环链表的创建!\n"); } static void StatGame(NodeType **ppHead, int iCipher) { int iCounter, iFlag = 1; NodeType *pPrv, *pCur, *pDel; pPrv = pCur = *ppHead; /* 将pPrv初始为指向尾结点,为删除作好准备 */ while (pPrv->next != *ppHead) pPrv = pPrv->next; while (iFlag) { for (iCounter = 1; iCounter < iCipher; iCounter++) { pPrv = pCur; pCur = pCur->next; } if (pPrv == pCur) iFlag = 0; pDel = pCur; /* 删除pCur指向的结点,即有人出列 */ pPrv->next = pCur->next; pCur = pCur->next; iCipher = pDel->cipher; printf("第%d个人出列, 密码: %d\n", pDel->id, pDel->cipher); free(pDel); } *ppHead = NULL; getchar(); } static void PrntList(const NodeType *pHead) { const NodeType *pCur = pHead; if (EmptyList(pHead)) return; do { printf("第%d个人, 密码: %d\n", pCur->id, pCur->cipher); pCur = pCur->next; } while (pCur != pHead); getchar(); } static NodeType *GetNode(const int iId, const int iCipher) { NodeType *pNew; pNew = (NodeType *)malloc(sizeof(NodeType)); if(!pNew) { printf("Error, the memory is not enough!\n"); exit(-1); } pNew->id = iId; pNew->cipher = iCipher; pNew->next = NULL; return pNew; } static unsigned EmptyList(const NodeType *pHead) { if(!pHead) { printf("The list is empty!\n"); return TRUE; } return FALSE; } 原题: 用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。(约瑟夫环问题 Josephus) 提示: 由于当某个人退出圆圈后,报数的工作要从下一个人开始继续,剩下的人仍然是围成一个圆圈的,可以使用循环表,由于退出圆圈的工作对应着表中结点的删除操作,对于这种删除操作频繁的情况,选用效率较高的链表结构,为了程序指针每一次都指向一个具体的代表一个人的结点而不需要判断,链表不带头结点。所以,对于所有人围成的圆圈所对应的数据结构采用一个不带头结点的循环链表来描述。设头指针为p,并根据具体情况移动。 为了记录退出的人的先后顺序,采用一个顺序表进行存储。程序结束后再输出依次退出的人的编号顺序。由于只记录各个结点的number值就可以,所以定义一 个整型一维数组。如:int quit[n];n为一个根据实际问题定义的一个足够大的整数。 代码: /******************************************************************** created: 2006/06/14 filename: C:\Documents and Settings\Administrator\桌面\tmpp\josephus.c file path: C:\Documents and Settings\Administrator\桌面\tmpp file base: josephus file ext: c author: A.TNG version: 0.0.1 purpose: 实现 Josephus 环问题 用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值, 直至全部输出。写出C程序。(约瑟夫环问题 Josephus) ********************************************************************* / #include #include #include #include /* 结构体和函数声明 */ typedef struct _node_t { int n_num; struct _node_t *next; } node_t; node_t *node_t_create(int n); node_t *node_t_get(node_t **pn, int m); /* 功能函数实现 */ /* * name: node_t_create * params: * n [in] 输入要构造的链表的个数 * return: * 返回构造成功的环形单向链表指针 * notes: * 构造节点数量为 n 的环形单向链表 * * author: A.TNG 2006/06/14 17:56 */ node_t * node_t_create(int n) { node_t *p_ret = NULL; if (0 != n) { int n_idx = 1; node_t *p_node = NULL; /* 构造 n 个 node_t */ p_node = (node_t *) malloc(n * sizeof(node_t)); if (NULL == p_node) return NULL; else memset(p_node, 0, n * sizeof(node_t)); /* 内存空间申请成功 */ p_ret = p_node; for (; n_idx < n; n_idx++) { p_node->n_num = n_idx; p_node->next = p_node + 1; p_node = p_node->next; } p_node->n_num = n; p_node->next = p_ret; } return p_ret; } /* * name: main * params: * none * return: * int * notes: * main function * * author: A.TNG 2006/06/14 18:11 */ int main() { int n, m; node_t *p_list, *p_iter; n = 20; m = 6; /* 构造环形单向链表 */ p_list = node_t_create(n); /* Josephus 循环取数 */ p_iter = p_list; m %= n; while (p_iter != p_iter->next) { int i = 1; /* 取到第 m-1 个节点 */ for (; i < m - 1; i++) { p_iter = p_iter->next; } /* 输出第 m 个节点的值 */ printf("%d\n", p_iter->next->n_num); /* 从链表中删除第 m 个节点 */ p_iter->next = p_iter->next->next; p_iter = p_iter->next; } printf("%d\n", p_iter->n_num); /* 释放申请的空间 */ free(p_list); system("PAUSE"); } 代码一: #include int main() { const int n=10; int a[n],c=5;