线性表题解

NO.3:求循环小数

题目要求

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

输入: N M k

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

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

下面给出输入输出样例:

测试输入期待的输出
测试用例 1以文本方式显示
  1. 1 8 50↵
以文本方式显示
  1. 0.125↵
测试用例 2以文本方式显示
  1. 29 33 10↵
以文本方式显示
  1. 0.8787878787↵
测试用例 3以文本方式显示
  1. 7 18 52↵
以文本方式显示
  1. 0.3888888888888888888888888888888888888888888888888888↵
测试用例 4以文本方式显示
  1. 2 7 50↵
以文本方式显示
  1. 0.28571428571428571428571428571428571428571428571428↵

 预设代码

#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;  
}  

信息梳理

我们先来看一下预设代码中的信息:

先是结构体NODE,包含两个元素int data以及NODE *next

typedef struct node  
{   int  data;  
    struct node * next;  
} NODE;

然后两个基本操作,output(NODE *, int)已经给出,需要完成change(int, int, NODE * )

void output( NODE *, int );  
void change( int, int, NODE * ); 

我们想想该怎么做,根据题目意思我需要将一个真分数转化成小数,而这个小数可以是有限的,也可以是循环小数(这里注意,不用担心没有循环的情况,因为真分数都是有理数,而有理数都能转化成有限或者循环小数)。并且要求我们用链表表示小数部分,循环小数要求使用循环链表。

我们先来举个例子模拟一下(1/8):

不难发现第一位小数是,1借了一位变成10,然后10/8=1(商),第二位小数是10%8(余数)

后面以此类推。这里面涉及到三个关键步骤,乘10取商取余数

代码实现

其实题目并不难,我们只要按照逻辑一步一步写就行了:为了方便直接先开两个数组来存放商和余数:

int shang[10010], yu[10010];
memset(shang, 0, sizeof(shang));

memset(yu, 0, sizeof(yu));

// memset初始化数组的操作(全部赋0)

然后就是不断地取商,取余数。那么循环什么时候停止呢?

前面已经说了,真分数是一定可以转化成一个有限小数或者循环小数的,所以能够停止循环的条件有两个:

  1. 有一次的商为0,也就是被整除了
  2. 有一次的商和余数,与之前某一次的商和余数相等,那么将会循环

那么这个循环体,我们可以这样写:

int p1 = 0, p2 = 0;     //p1、p2用来记录出现商、余数相同时的两个位置
int flag = 0;               //flag是一个标志,用来标记是不是存在循环部分
int num = n * 10;      //因为是个真分数,所以第一步就要*10操作

for (int i = 0; ; i++)    //这是默认循环条件一直为真的意思,因为循环体中有终止条件
{
    shang[i] = num / m;
    yu[i] = num % m;

    for (int j = 0; j < i; j++)
    {
        /* 当商和余数与之前相同时,即标志着出现循环
        用p1、p2记录位置,flag=1表示存在循环 */            
        if(shang[j] == shang[i] && yu[j] == yu[i])
        {
            p1 = j;
            p2 = i;

            flag = 1;
            break;
        }
    }
    num = yu[i] * 10;

    if(!num)           //如果num被除尽,变为0,则没有循环
    {
        p1 = i + 1;
        break;
    }
    
    if(flag == 1)
        break;
}

然后跳出循环后,我们就应该将之前存好的商(也就是小数部分)依次存入链表

NODE *r = head;

for (int i = 0; i < p1; i++)          //p1一定的是循环跳出时的位置
{
        //尾插法建立链表
        NODE *q = (NODE*)malloc(sizeof(NODE));
        q->data = shang[i];
        q->next = NULL;
        r->next = q;
        r = q;
}

注意:这里一定要使用尾插法建立链表,有人问为什么。因为预设代码中output时就是从head一直往后的!!!如果用头插的话,就全反了!!!

最后我们是不是要通过flag标志来判断是不是要加上循环部分啊:

//补上循环
if (flag == 1)
{
        NODE *r_save = r;
        for (int i = p1; i < p2; i++)         //因为p1、p2是两次商、余数相同的两个位置
        {                                                 //所以p2-p1就是循环部分的长度
            NODE *q = (NODE*)malloc(sizeof(NODE));
            q->data = shang[i];
            q->next = NULL;
            r->next = q;
            r = q;
        }
        r->next = r_save->next;            //注意题目要求是循环部分用循环链表
    }
}

运行一下:

 

AC!!!过啦!!!

NO.4:求循环节 

题目要求

这道题实在与上一道的题目背景一样:

输入: N M

输出: 整个循环节

要求:编写一个尽可能高效的查找循环节起始点的函数: NODE * find( NODE * head, int * n ) 。函数的返回值为循环节的起点(即图中的指针p),n为循环节的长度。

说明:提交程序时请同时提交将分数转换为小数的函数 change( int n, int m, NODE * head ) (前面题目中已经编写)。

下面是部分测试样例:

测试输入期待的输出时间限制内存限制
测试用例 1以文本方式显示
  1. 1 3↵
以文本方式显示
  1. ring=1↵
  2. 3↵
1秒64M
测试用例 2以文本方式显示
  1. 1 8↵
以文本方式显示
  1. ring=0↵
  2. NULL↵
1秒64M
测试用例 3以文本方式显示
  1. 29 33↵
以文本方式显示
  1. ring=2↵
  2. 87↵
1秒64M
测试用例 4以文本方式显示
  1. 2 7↵
以文本方式显示
  1. ring=6↵
  2. 285714↵
1秒64M

预设代码

#include <stdio.h>  
#include <stdlib.h>  

typedef struct node  
{   int         data;  
    struct node * next;  
} NODE;  
  
NODE * find( NODE * , int * );  
void outputring( NODE * );  
void change( int , int , NODE * );  

void outputring( NODE * pring )  
{   NODE * p;  
    p = pring;  
    if ( p == NULL )  
        printf("NULL");  
    else  
        do  {   printf("%d", p->data);  
            p = p->next;  
        } while ( p != pring );  
    printf("\n");  
    return;  
}  
  
int main()  
{   int n, m;  
    NODE * head, * pring;  
  
    scanf("%d%d", &n, &m);  
    head = (NODE *)malloc( sizeof(NODE) );  
    head->next = NULL;  
    head->data = -1;  
  
    change( n, m, head );  
    pring = find( head, &n );  
    printf("ring=%d\n", n);  
    outputring( pring );  
  
    return 0;  
}  
  
/* Here is waiting for you. 
void change( int n, int m, NODE * head ) 
{  
} 
 
NODE * find( NODE * head, int * n ) 
{ 
} 
*/ 

信息梳理

预设代码与上一题几乎完全一样,就不再赘述了,我们来想一想怎么去找这个循环节。其实这道题可以钻一个空子,因为在上一道题写的change()函数中,已经存过遇见商、余数相同的两个位置p1、p2(又因为当时从前往后遍历的),所以p1就是第一个循环节,而p2-p1就是循环节长度ring。但是因为之前的的p1、p2变量是局部变量,跳出函数change()就失效了,但是这根本难不倒想偷懒的我!!!我直接将p1、p2用全局变量定义,不就能保留下来了吗,我真是个小天才!!!

代码实现

#include <string.h>
int p1 = 0, p2 = 0;

void change(int n, int m, NODE *head )
{
	//初始化
    int shang[10010], yushu[10010];
    memset(shang, 0, sizeof(shang));
    memset(yushu, 0, sizeof(yushu));
    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++)
    	{
    		/* 当商和余数与之前相同时,即标志着出现循环
    		用p1、p2记录位置,flag=1表示存在循环 */    		
    		if(shang[j] == shang[i] && yushu[j] == yushu[i])
    		{
    			p1 = j;
    			p2 = i;

    			flag = 1;
    			break;
    		}
    	}
    	num = yushu[i] * 10;

    	if(!num)//如果num被除尽,变为0,则没有循环
    	{
    		p1 = i + 1;
    		break;
    	}
    	if(flag == 1)
    	{
    		break;
    	}
    }
    /*根据 p1 的位置,如果没有循环,则建立完整链表;
    	如果有循环,则建立的是前缀。 */
    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 *r_save = 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 = r_save->next;
    }
}

NODE * find( NODE *head, int *n )
{
	if(p2 > p1)//存在循环
	{
		NODE *p = head->next;//初始化
		
        //计算长度
		*n = p2 - p1;
		//越过前缀
		for (int i = 0; i < p1; i++)
		{
			p = p->next;
		}
		return p;
	}
	else//不存在循环
	{
		*n = 0;
		return NULL;
	}
}

运行一下:

 

 哈哈哈哈!!!没有问题!!!

源码

求循环小数

求循环节

加油!!!国庆不会断更的!!!(^///^)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值