严题集3-32双向循环队列计算k阶斐波那契数列前n+1项

一、题目要求

利用循环队列编写k阶斐波那序列的前k+1项(f0,f1,f2……fn)的算法,要求满足:fn <=max而fn+1 >max,其中max为某个约定的常数。所用循环队列的容量仅为k,则在算法执行结束时,留在循环队列中的元素应是所求k阶斐波那契序列中的最后k项fn-k+1, …, fn。

二、分析

首先回顾一下k阶斐波那序列的定义:
f0 = 0, f1 = 0, … , fk-2 = 0, fk-1 = 1;
fn = fn-1 + fn-2 + … + fn-k , n = k , k + 1, …
说白了就是每一项都是前k项之和,同时定义数列前f0-fk-2项为0,fk-1为1。即k阶斐波那序列从第fk-1项开始非0。
因为最后要求在队列里保留阶斐波那契序列中的最后k项,所以对队列的操作函数进行了修改了,增加了参数MAXQSIZE来控制队列的容量。
例如队列的EnQueue函数:

Status EnQueue(SqQueue &Q,QElemType e,int MAXQSIZE)  //在队尾插入元素 
{
	if((Q.rear+1)%(MAXQSIZE+1)==Q.front) return ERROR;//队列满
	Q.base[Q.rear]=e;
	Q.rear=(Q.rear+1)%(MAXQSIZE+1);
	return OK; 
}

值得注意的是在初始化函数中申请的空间是MAXQSIZE+1,多一个空间用于判断队列是否满了。因为队列在满和空的情况下都有Q.front=Q.rear。现在多以空间,判断队列是否空只用判断Q.rear-Q.front+MAXQSIZE+1)%(MAXQSIZE+1)的值即可。

队列初始化函数:

Status InitQueue(SqQueue &Q,int MAXQSIZE)
{
	Q.base=(QElemType*)malloc((MAXQSIZE+1)*sizeof(QElemType));
	if(!Q.base) return ERROR;
	Q.front=Q.rear=0;
	return OK;
} 

队列长度函数:

int QueueLength(SqQueue Q,int MAXQSIZE)
{
	return(Q.rear-Q.front+MAXQSIZE+1)%(MAXQSIZE+1);
}

队列的操作函数在后面的完整代码里都有,这里不一一列出了。
解决了基本队列操作函数后,就可以开始了。
前面提到过,每一项都是前k项之和同时定义数列前f0-fk-2项为0,fk-1为1
最初的思路:只需要在生成队列前k项后,将前k项加在一起得到第k+1项,然后讲队列中第一项出队,将第k+1项在队尾入队。
核心算法如下:

Status Fibonacci(int k,int max) //3-32 循环队列计算k阶斐波那契数列前n+1项,Fn<=max,Fn+1>max
{
	if(k<1||max<0) return ERROR;
	if(k==1) 
	{
		printf("数列恒为1,找不到符合要求的项\n");
		return ERROR;
	}
	SqQueue Q;
	int MAXQSIZE=k; 
	InitQueue(Q,MAXQSIZE);
	int n=k;
	for(int i=0;i<k-1;i++) 
	{
		EnQueue(Q,0,MAXQSIZE);
		printf("第f%d项是0\n",i);
	}
	EnQueue(Q,1,MAXQSIZE);  
	printf("第f%d项是1\n",MAXQSIZE);
	int temp,e;
	int j; 
	for(int i=Q.front,j=0;j<QueueLength(Q,k+1);j++,i=(i+1)%(k+1)); 
	temp=temp+Q.base[i];
    while(temp<=max)
    {	
		DeQueue(Q,e,MAXQSIZE);
    	EnQueue(Q,temp,MAXQSIZE);
    	printf("第f%d项是%d\n",n,temp);
    	for(int i=Q.front, j=0;j<QueueLength(Q,k+1);j++,i=(i+1)%(k+1)) temp=temp+Q.base[i];
		n++;
	}
	printf("第f%d项是%d\n",n,temp);
	printf("题中要求的第n项为第%d项",n-1); 
	return OK;
} 

需要修改的地方:
我们注意到,在while(temp<=max)前面将是生成队列f0至fk-1项遍历相加,这是不科学的。因为根据定义,数列f0-fk-2项为0,fk-1==1,所以可以删去22-23行的for循环,直接令temp=1;
同时,在后续的操作中,第29行for循环依然是将队列中的所有数相加求和,在k较大的情况下还是很浪费时间的。因此可以利用该数列的一个递推公式进行修改(请读者自行推导):
f(n)=2f(n-1)-f(n-k-1)
在k=3, max=100000000 本函数执行次数 N=1000000,修改后减少两个for循环使用递推的函数用时0.72s,修改前用时1.88s,平均节约一半多的时间。

三、完整代码

经过修改后,完整代码如下。

#include<stdio.h>
#include<iostream>
#include<math.h>  
#include<time.h>
#include<stdlib.h>
#define Status int 
#define OK 1
#define ERROR -1
//#define OVERFLOW -1
#include <malloc.h>
#include<string.h>
//#define MAXQSIZE 100
#define QElemType int
using namespace std;  //双向循环队列,为了满足2-32题目需求,所有队列操作函数需要输入队列MAXQSIZE,实际是MAXQSIZE+1,多一个用于判断队列是否满 
typedef struct
{
	QElemType *base;
	int front;
	int rear;
}SqQueue;

Status InitQueue(SqQueue &Q,int MAXQSIZE)
{
	Q.base=(QElemType*)malloc((MAXQSIZE+1)*sizeof(QElemType));
	if(!Q.base) return ERROR;
	Q.front=Q.rear=0;
	return OK;
} 

int QueueLength(SqQueue Q,int MAXQSIZE)
{
	return(Q.rear-Q.front+MAXQSIZE+1)%(MAXQSIZE+1);
}

Status EnQueue(SqQueue &Q,QElemType e,int MAXQSIZE)  //在队尾插入元素 
{
	if((Q.rear+1)%(MAXQSIZE+1)==Q.front) return ERROR;//队列满
	Q.base[Q.rear]=e;
	Q.rear=(Q.rear+1)%(MAXQSIZE+1);
	return OK; 
}

Status DeQueue(SqQueue &Q,QElemType &e,int MAXQSIZE)  //删除队头元素 
{
	if(Q.front==Q.rear) return ERROR;//队列空
	e=Q.base[Q.front];
	Q.front=(Q.front+1)%(MAXQSIZE+1);
	return OK; 
}

Status PrintQueue(SqQueue Q,int MAXQSIZE)
{
	int length=QueueLength(Q,MAXQSIZE);
	if(Q.front==Q.rear) return ERROR;//队列空
	int j;
	for(int i=Q.front,j=0;j<length;j++,i=(i+1)%(MAXQSIZE+1)) 
	printf("%d",Q.base[i]);
}

Status Fibonacci(int k,int max) //3-32 循环队列计算k阶斐波那契数列前n+1项,Fn<=max,Fn+1>max
{
	if(k<1||max<0) return ERROR;
	if(k==1) 
	{
		printf("数列恒为1,找不到符合要求的项\n");
		return ERROR;
	}
	SqQueue Q;
	int MAXQSIZE=k; 
	InitQueue(Q,MAXQSIZE);
	int n=k;
	for(int i=0;i<k-1;i++) 
	{
		EnQueue(Q,0,MAXQSIZE);
		printf("第f%d项是0\n",i);
	}
	EnQueue(Q,1,MAXQSIZE);  
	printf("第f%d项是1\n",MAXQSIZE);
	int temp,e;
//	int j; 
//	for(int i=Q.front,j=0;j<QueueLength(Q,k+1);j++,i=(i+1)%(k+1))  这两行也是效率低下的,此时队列中有k-1个0和一个1,没必要遍历求和,直接令temp=1; 
//	temp=temp+Q.base[i];
    temp=1;
    while(temp<=max)
    {	
		DeQueue(Q,e,MAXQSIZE);
    	EnQueue(Q,temp,MAXQSIZE);
    	printf("第f%d项是%d\n",n,temp);
    	temp=2*temp-e;     //与下一行算法相比,这里运用简单地推公式f(n)=2f(n-1)-f(n-k-1) 会更加高效
	//在k=3, max=100000000  本函数执行次数 N=1000000下,递推用时0.72s,for循环用时1.88s,平均节约一半多的时间 
    //	for(int i=Q.front, j=0;j<QueueLength(Q,k+1);j++,i=(i+1)%(k+1)) temp=temp+Q.base[i];
		n++;
	}
	printf("第f%d项是%d\n",n,temp);
	printf("题中要求的第n项为第%d项",n-1); 
	return OK;
} 
int main()
{
	/* 队列操作函数验证代码 
	SqQueue Q;
	int e;
	int MAXQSIZE=100;
	InitQueue(Q,MAXQSIZE);
	for(int i=0;i<=5;i++) EnQueue(Q,i,MAXQSIZE);
	PrintQueue(Q,MAXQSIZE);
	*/
  /*//  3-32效率验证代码	
	int N=10000000;
	clock_t start, stop;
	double duration;
	start=clock();int k=20;
	int max=100000000;
	for(int i=0;i<N;i++)   	Fibonacci( k,max);//my function  循环多次计算,不然时间太短测不出来 
	stop=clock();
	duration=((double)(stop-start))/CLK_TCK;
	printf("算法花费时间为%f\n",duration);
 */ 
	
	int k=3;
	int max=30;
	Fibonacci( k,max);

}
  • 4
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
循环队列是一种非常适合实现队列的数据结构,对于那契数列,我们可以使用循环队列来实现。 首先,我们需要定义一个循环队列的结构体,包含队列的头部、尾部、队列的最大长度和队列中元素的个数: ```c #define MAXSIZE 100 typedef struct { int *base; // 队列元素的基地址 int front; // 队列头指针 int rear; // 队列尾指针 int maxSize; // 队列最大长度 int count; // 队列中元素的个数 } SqQueue; ``` 然后,我们可以使用循环队列来实现k那契数列计算。k那契数列的递推公式为: F(n) = F(n-1) + F(n-2) + ... + F(n-k) 我们可以使用一个长度为k的循环队列来存储最近的k个那契数,每次计算新的那契数时,将队列中的k个数相加即可。 以下是完整的代码实现: ```c #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 typedef struct { int *base; // 队列元素的基地址 int front; // 队列头指针 int rear; // 队列尾指针 int maxSize; // 队列最大长度 int count; // 队列中元素的个数 } SqQueue; // 初始化队列 void InitQueue(SqQueue *Q, int k) { Q->base = (int *)malloc(sizeof(int) * k); Q->front = Q->rear = 0; Q->maxSize = k; Q->count = 0; } // 判断队列是否为空 int IsEmpty(SqQueue Q) { return Q.count == 0; } // 判断队列是否已满 int IsFull(SqQueue Q) { return Q.count == Q.maxSize; } // 入队 void EnQueue(SqQueue *Q, int x) { if (IsFull(*Q)) { printf("队列已满,无法插入元素!\n"); return; } Q->base[Q->rear] = x; Q->rear = (Q->rear + 1) % Q->maxSize; Q->count++; } // 出队 int DeQueue(SqQueue *Q) { if (IsEmpty(*Q)) { printf("队列为空,无法删除元素!\n"); return -1; } int x = Q->base[Q->front]; Q->front = (Q->front + 1) % Q->maxSize; Q->count--; return x; } // 计算k那契数列的第n项 int KFibonacci(int n, int k) { SqQueue Q; InitQueue(&Q, k); int i, sum = 0; for (i = 0; i < n; i++) { if (i < k) { EnQueue(&Q, 1); sum++; } else { int x = DeQueue(&Q); EnQueue(&Q, sum); sum = sum + sum - x; } } return sum; } int main() { int n, k; printf("请输入n和k:"); scanf("%d %d", &n, &k); int result = KFibonacci(n, k); printf("k那契数列的第%d项为:%d\n", n, result); return 0; } ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雨林木风11

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值