5. 求循环小数

成绩10开启时间2021年09月13日 星期一 00:00
折扣0.8折扣时间2021年09月30日 星期四 23:55
允许迟交关闭时间2021年10月17日 星期日 23:55

对于任意的真分数 N/M ( 0 < N < M ),均可以求出对应的小数。如果采用链表表示各个小数,对于循环节采用循环链表表示,则所有分数均可以表示为如下链表形式。

输入: N M k

输出: 转换后的小数(不超过 k )

要求: 仅编写将分数转换为小数的函数 change( int n, int m, NODE * head ) 。

预设代码

前置代码

/* PRESET CODE BEGIN - NEVER TOUCH CODE BELOW */  
 
#include <stdio.h>  
#include <stdlib.h>  
  
typedef struct node  
{   int         data;  
    struct node * next;  
} NODE;  
  
void output( NODE *, int );  
void change( int, int, NODE * );  
  
void output( NODE * head, int kk )  
{   int k=0;  
  
    printf("0.");  
    while ( head->next != NULL && k<kk )  
    {   printf("%d", head->next->data );  
        head = head->next;  
        k ++;  
    }  
    printf("\n");  
}  
  
int main()  
{   int n, m,k;  
    NODE * head;  
  
    scanf("%d%d%d", &n, &m, &k);  
    head = (NODE *)malloc( sizeof(NODE) );  
    head->next = NULL;  
    head->data = -1;  
    change( n, m, head );  
    output( head,k );  
    return 0;  
}  
  
/* PRESET CODE END - NEVER TOUCH CODE ABOVE */  
测试输入期待的输出时间限制内存限制额外进程
测试用例 1以文本方式显示
  1. 1 8 50↵
以文本方式显示
  1. 0.125↵
1秒64M0
测试用例 2以文本方式显示
  1. 29 33 10↵
以文本方式显示
  1. 0.8787878787↵
1秒64M0
测试用例 3以文本方式显示
  1. 7 18 52↵
以文本方式显示
  1. 0.3888888888888888888888888888888888888888888888888888↵
1秒64M0
测试用例 4以文本方式显示
  1. 2 7 50↵
以文本方式显示
  1. 0.28571428571428571428571428571428571428571428571428↵
1秒64M0

代码

和上一篇一样,还是两份代码,一份学长的,一份自己的。

学长代码

#include <string.h>    
void change(int n,int m,NODE *head)    
{  
    int shang[10010],yushu[10010];    
    memset(shang,0,sizeof(shang));    
    memset(yushu,0,sizeof(yushu));    
    int p1=0,p2=0;    
    int flag=0;    
    int num=n*10;  
    for(int i=0;;i++)    
    {    
        shang[i]=num/m;    
        yushu[i]=num%m;    
    
        for(int j=0;j<i;j++)    
        {    
                         
            if(shang[j]==shang[i]&&yushu[j]==yushu[i])    
            {  
                p1=j;    
                p2=i;    
    
                flag=1;    
                break;    
            }    
        }    
        num=yushu[i]*10;    
    
        if(!num)    
        {    
            p1=i+1;    
            break;    
        }    
        if(flag==1)    
        {    
            break;    
        }    
    }    
        
    NODE *r=head;    
    for(int i=0;i<p1;i++)    
    {    
           
        NODE *q=(NODE*)malloc(sizeof(NODE));    
        q->data=shang[i];    
        q->next=NULL;    
        r->next=q;    
        r=q;   
    }  
    if(flag==1)    
    {    
        NODE *r1=r;    
        for (int i=p1;i<p2;i++)    
        {    
            NODE *q=(NODE*)malloc(sizeof(NODE));    
            q->data=shang[i];    
            q->next=NULL;    
            r->next=q;    
            r=q;    
        }    
        r->next=r1->next;    
    }    
}

自己代码

void change(int n,int m,NODE *head){
	
	NODE *q=(NODE*)malloc(sizeof(NODE));
	q=head;
	NODE *o=(NODE*)malloc(sizeof(NODE));
	o=head;
	
	int yushu[100]={0};
	int shang[100]={0};
	int flag=0;
	
	int len=0;
	while(n*10/m>0){
		NODE *p=(NODE*)malloc(sizeof(NODE));
		p->data=n*10/m;
		q->next=p;
		q=q->next;
		
		shang[len]=n*10/m;
		yushu[len++]=n*10%m;
		n=n*10%m; 
		if(n==0){
			q->next=NULL;
			break;
		}
		for(int i=0;i<len-1;i++){
			if(shang[i]==shang[len-1]&&yushu[i]==yushu[len-1]){

				int idx=0;
				while(idx<=i){
					o=o->next;	
					idx++;
						 
				}
				int idx2=0;
				NODE *r=(NODE*)malloc(sizeof(NODE));
				r=head;
				while(idx2<len-1){
					r=r->next;
					idx2++;
				}
				r->next=o;	
				flag=1;
				break;		
			}
		}
		if(flag==1){
			break;
		}
	}
}

希望学弟学妹们能帮忙测试一下我的垃圾代码,看看有什么问题,阿里嘎多。

解释

用第二份代码做题解。

核心思想

设两个数组商和余数,如果新一位的商和余数和之前某一位的商和余数相等,则该位为循环节,注意不是仅仅商相等(我改了好久才发现)。

然后类似于约瑟夫问题中循环链表的代码,把循环节连接到一块就行了。

准备工作

先搞一些指向结点的指针,这个根据个人需要弄,很多指针可以复用,我懒得想了,每次要用的时候就申请一个新指针,这使得我代码比较长,这一部分可以优化

另外准备两个记录商和余数的数组

void change(int n,int m,NODE *head){
	
	NODE *q=(NODE*)malloc(sizeof(NODE));
	q=head;
	NODE *o=(NODE*)malloc(sizeof(NODE));
	o=head;
	
	int yushu[100]={0};
	int shang[100]={0};
	int flag=0;//为0则可以整除,为1则循环小数

做除法

就跟我们正常除法一样,有商有余数,分别保存在两个数组里,把除法的商作为新结点的值:

	int len=0;
	while(n*10/m>0){//当除出来的结果不为0时 
		NODE *p=(NODE*)malloc(sizeof(NODE));//新结点 
		p->data=n*10/m;
		q->next=p;//一开始q是head,之后都是上一次插入的新结点 
		q=q->next;
		
		shang[len]=n*10/m;//记录商 
		yushu[len++]=n*10%m;//记录余数 
		n=n*10%m; //下一次的被除数就是这一次的余数 

两种情况

能整除的情况

假如可以整除,意味着最后一次除法的余数为0,也就是说最后一次的下一次被除数为0,即,只要能整除,那么总有一次n会变为0:

		if(n==0){
			q->next=NULL;
			break;
		}

不能整除的情况

在本题的条件下,不能整除的话会出现循环,我们的目的就是把循环的部分制成一个循环链表。

那么,我们只需找到循环节,然后让最后一个结点的next指针指向循环节对应的结点即可。

找到循环节需要用到我们的商和余数的数组

		for(int i=0;i<len-1;i++){
			if(shang[i]==shang[len-1]&&yushu[i]==yushu[len-1]){
			//商和余数都相等,下一次开始循环 
				int idx=0;
				while(idx<=i){
					o=o->next;	
					idx++;		 
				}//找到循环节的结点 
				 
				int idx2=0;
				NODE *r=(NODE*)malloc(sizeof(NODE));
				r=head;//r完全可以复用之前的指针,写的时候没想 
				while(idx2<len-1){
					r=r->next;
					idx2++;
				}//找到循环部分最后一个结点
				 
				r->next=o;//连接循环节和循环部分最后一个结点
				//构成一个循环链表 
				flag=1;//不能整除的情况已经完成,可以跳出while了 
				break;//跳出for		
			}
		}
		if(flag==1){
			break;//跳出while 
		}

总结

有了之前循环链表的基础,这题还是非常简单的。算法也很好想,主要就是coding了。

自己把代码敲出来还是挺爽的

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值