算法课外思考题二


问题一

问题描述

给出不同策略求两个整数的最大公约数GCD(m,n);并进行分析

问题分析

本问题求两数的最大公约数,第一个想法是暴力枚举法

在数学上,能够证明:$m、n\neq0,GCD(m,n)=GCD(n,mmodn)$,递归出最大公约数,也就是欧几里得算法,上述等式是欧几里得算法的数学基础;

对于两数的最大公约数$GCD(m,n)$,有:

$m<n,GCD(m,n)=GCD(n,m)$

$ mis ~even、nis~even,GCD(m,n)=2*GCD(m/2,n/2)$

$ mis ~even、nis~odd,GCD(m,n)=GCD(m/2,n)$

$ mis ~odd、nis~even,GCD(m,n)=GCD(m,n/2)$

$ mis ~odd、nis~odd,GCD(m,n)=GCD(n,m-n)$

基于上述理论进行递归,也就是二进制算法

算法思路

对于暴力枚举法,确定两数中最小值,从1~最小值进行循环,使用一个变量更新能被共同整除的数,循环结束时,返回变量的最新值;

对于欧几里得算法,编写GCD函数,两数都不为零时,按照规则递归调用GCD函数;

对于二进制算法,编写GCD函数,对m、n进行上述规则判断,然后进行递归调用GCD函数。

算法代码与分析

#include<iostream>
using namespace std;

//暴力枚举法
int Min(int m, int n);
int GCD1(int m, int n) {
	int num = 0;
	for (int i = 1; i <= Min(m, n); i++) {
		if (m % i == 0 && n % i == 0) {
			num = i;
		}
	}
	return num;
}
int Min(int m, int n) {
	return m > n ? n : m;
}

//欧几里得算法
int GCD2(int m, int n) {
	if (n == 0) {
		return m;
	}
	return GCD2(n, m % n);
}

//二进制算法
int GCD3(int m, int n) {
	if (m == 0) {
		return n;
	}
	if (n == 0) {
		return m;
	}

	if (m % 2 == 0 && n % 2 == 0) {
		return 2 * GCD3(m / 2, n / 2);
	}
	else if (m % 2 == 0 && n % 2 == 1) {
		return GCD3(m / 2, n);
	}
	else if (m % 2 == 1 && n % 2 == 0) {
		return GCD3(m, n / 2);
	}
	else {
		return GCD3(Min(m, n), abs(m - n));
	}
}
int main() {

	int m = 197456;
	int n = 400000;
	cout << GCD1(m, n) << endl;
	cout << GCD2(m, n) << endl;
	cout << GCD3(m, n) << endl;
	system("pause");
	return 0;
}

image-20230305174550409

后续查资料得知,GCD3函数并没有体现真正的二进制算法,没有避免大量取模运算,改进如下:

int GCD3(int m,int n){
    if(m==0){
        return n;
    }
    if(n==0){
        return m;
    }
    
    if((m&1)==0&&(n&1)==0){
        return GCD3(m>>1,n>>1)<<1;
    }
    else if((m&1)==0&&(n&1)!=0){
        return GCD3(m>>1,n);
    }
    else if((m&1)!=0&&(n&1)==0){
        return GCD3(m,n>>1);
    }
    else{
        return GCD3(Min(m,n),abs(m-n));
    }
}

对于数值较小的两数来说,三种方法的速度相差不大,默认$m>n$

则对于枚举法,其时间复杂度是$O(n)$;

对于欧几里得算法,其时间复杂度是$O(log_2n)$;

对于二进制算法,其时间复杂度是$O(log_2(m+n))$;

所以,对于大数来说,后两种算法的处理速度更快;针对后两种算法,改进的二进制算法避免了大量的取余运算。

问题二

问题描述

给出方案返回一可以动态变化的序列(可增可减)中的最大值,并对你的方案进行分析

问题分析

本序列是动态变化的,可以采用顺序栈的数据结构,对于求取序列最大值,采用辅助栈的方式处理;

算法思路

建立两个栈,一个栈存取元素,另一个栈存最大元素;

算法代码与分析

#include<iostream>
#define STACK_INIT_SIZE 100
#define LIST_SIZE 100
using namespace std;
typedef int SElemType;

//栈结构
typedef struct {
	SElemType* base;
	SElemType* top;
	int stacksize;
}SqStack;

typedef struct {
	SqStack Data;
	SqStack MaxData;
}Maxstack;

void InitStack(SqStack* s) {
	s->base = (SElemType*)malloc(STACK_INIT_SIZE * sizeof(SElemType));
	s->top = s->base;
	s->stacksize = STACK_INIT_SIZE;
}

void Push(SqStack* s, SElemType e) {
	if (s->top - s->base >= s->stacksize) {
		s->base = (SElemType*)realloc(s->base, (s->stacksize + 10) * sizeof(SElemType));
		s->top = s->base + s->stacksize;
		s->stacksize += 10;
	}
	*s->top++ = e;
}

void Pop(SqStack* s, SElemType* e) {
	*e = *--s->top;
}

SElemType GetTop(SqStack s) {
	return *(--s.top);
}

void DestroyStack(SqStack* s) {
	free(s->base);
	s->base = NULL;
	s->top = NULL;
	s->stacksize = 0;
}

void MaxInit(Maxstack* maxstack) {
	InitStack(&(maxstack->Data));
	InitStack(&(maxstack->MaxData));
}

void MaxDestroy(Maxstack* maxstack) {
	DestroyStack(&maxstack->Data);
	DestroyStack(&maxstack->MaxData);
}

void MaxPush(Maxstack* maxstack, SElemType e) {
	Push(&maxstack->Data, e);
	if (maxstack->MaxData.base == maxstack->MaxData.top) {
		Push(&maxstack->MaxData, e);
	}
	else {
		if (e >= GetTop(maxstack->MaxData)) {
			Push(&maxstack->MaxData, e);
		}
	}
}

void MaxPop(Maxstack* maxstack, SElemType* e) {
	if (GetTop(maxstack->Data) == GetTop(maxstack->MaxData)) {
		Pop(&maxstack->Data, e);
		Pop(&maxstack->MaxData, e);
		return;
	}
	Pop(&maxstack->Data, e);
}

SElemType GetMax(Maxstack* maxstack) {
	return GetTop(maxstack->MaxData);
}





int main() {
	Maxstack maxstack;
	SElemType e;
	MaxInit(&maxstack);

	MaxPush(&maxstack, 1);
	MaxPush(&maxstack, 2);
	MaxPush(&maxstack, 3);
	MaxPush(&maxstack, 1);

	cout << GetMax(&maxstack) << endl;
}

image-20230305233550207

对于栈方式存储动态序列以及求取最大值比较容易实现,对于最大值引入最大栈用空间换时间,时间复杂度达到$O(1)$,但是不够对于序列的操作不够灵活,只能在栈顶进行操作。


文章作者: J&Ocean
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 J&Ocean !
评论
  目录