数据结构与算法分析学习笔记---第二章

最大子序列和问题:

#include <iostream>
using namespace std;
#define BUFFER_SIZE 100
/************************************************************************/
/*
问题描述:
	给定整数A1,A2...AN(可能有负数),求从k = i 到k = j的Ak求和的最大值。
	例如:输入 -2,11, -4,13,-5,-2时,答案为20(从A2到A4)。
*/
/************************************************************************/

//========================================================================
//时间复杂度为o(n3)
//=========================================================================
int MaxSubsequenceSum1(const int a[], int n)
{
	int max, ThisSum, i, j, k;
	max = 0;
	for(i=0; i<n; ++i)
		for(j=i; j<n; ++j)
		{
			ThisSum = 0;
			for(k=i; k<j; ++k)
			{
				ThisSum += a[k];
				if(ThisSum > max)
					max = ThisSum;
			}
		}
	return max;
}

//=========================================================================
//时间复杂度为o(n2)
//=========================================================================
int MaxSubsequenceSum2(const int a[], int n)
{
	int max, ThisSum, i, j;
	max = 0;
	for(i=0; i<n; ++i)
	{
		ThisSum = 0;
		for(j=i; j<n; ++j)
		{
			ThisSum += a[j];
			if(ThisSum > max)
				max = ThisSum;
		}
	}
		return max;
}

//=========================================================================
//时间复杂度为o(nlogn)
/*
最大序列和可能出现在三处地方:整个出现在输入数据的做部分;或者整个出现在右部分;或者跨越输入数据的中部分从而占据左右两部分。
前两两种情况可以递归求解。第三种情况和最大和可以通过求出半部分的最大和(包含前半部分的最后一个元素)以及后半部分最大和(包含后半部分第一个元素)而得到。然后将这两个和加在一起。
*/
//=========================================================================
int MaxSubsequenceSum3(const int a[], int left, int right)
{
	int max3(int x,int y,int z);
	int MaxLeftSum, MaxRightSum;
	int MaxLeftBorderSum, MaxRightBorderSum;
	int LeftBorderSum, RightBorderSum;
	int Center, i;

	if(left == right)		//Base case;
	{
		if(a[left] > 0)
			return a[left];
		else
			return 0;
	}
	Center = (left + right) / 2;
	//=============================================================
	//求不带边界的左、右部分最大子序列和,递归。
	//=============================================================
	MaxLeftSum = MaxSubsequenceSum3(a, left, Center);
	MaxRightSum = MaxSubsequenceSum3(a, Center + 1, right);

	//=============================================================
	//求左边带边界的最大和
	//=============================================================
	MaxLeftBorderSum = 0; LeftBorderSum = 0;
	for(i=Center; i>=left; --i)
	{
		LeftBorderSum += a[i];
		if(LeftBorderSum > MaxLeftBorderSum)
		{
			MaxLeftBorderSum = LeftBorderSum;
		}
	}

	//=============================================================
	//求右边带边界的最大和
	//=============================================================
	MaxRightBorderSum = 0; RightBorderSum = 0;
	for(i=Center+1; i<=right; ++i)
	{
		RightBorderSum += a[i];
		if(RightBorderSum > MaxRightBorderSum)
		{
			MaxRightBorderSum = RightBorderSum;
		}
	}
	return max3(MaxLeftSum, MaxRightSum,
				MaxLeftBorderSum + MaxRightBorderSum);
}
//求三个数中的最大值
int max3(int x, int y, int z)
{
	int max = x > y ? x : y;
	max = max > z ? max : z;
	return max;	
}

//=========================================================================
//时间复杂度为o(n)
//=========================================================================
int MaxSubsequenceSum4(int a[], int n)
{
	int ThisSum, i, MaxSum;
	MaxSum = 0;
	ThisSum = 0;
	for(i=0; i<n; ++i)
	{
		ThisSum += a[i];
		if(ThisSum > MaxSum)
			MaxSum = ThisSum;
		else if(ThisSum < 0)
			ThisSum = 0;
	}
	return MaxSum;
}
//=========================================================================
//测试函数
//=========================================================================
int main()
{
	int a[BUFFER_SIZE], i, N;
	i = 0;
	cout << "please input total numbers N = ";
	cin >> N;
	cout <<endl;
	cout<< "please input N numbers :";
	while(i < N)
		cin >> a[i++];
	cout <<endl;
	cout << "first function :" << MaxSubsequenceSum1(a, N)<<endl;
	cout << "second function :" << MaxSubsequenceSum2(a, N)<<endl;
	cout << "third function :" << MaxSubsequenceSum3(a, 0, N-1)<<endl;
	cout << "fourth function :" << MaxSubsequenceSum4(a, N)<<endl;
	cin >> N;
	return 1;
}

看到习题2.13中的求素数问题,突然想到以前想加一个python群,里面的验证问题如下:

10亿以内与987654互质的数的总和是什么?包括10亿。

当时用来最笨的方法,求10亿以内与987654的最大公因数为1的数的总和,也就是循环gcd(1000000000,987654);

现在想到一个比较快的方法:可以先求出987654的素数分解Array[] = {2,3,97,1987},然后对10亿内的数遍历,如果能整除Array中的任意一个,则进入下次循环,否则将当前的值加入总和中。代码如下:

#include <iostream>
#include <vector>
#include <math.h>
using namespace std;

//=================================================================================
//大数质因数分解
//=================================================================================
int getSushu(const unsigned long long &n, vector<unsigned long long> &Sushu)
{
	unsigned long long ThisNumber = n;
	unsigned long long i = 2;
	unsigned long long num = 0;
	for(;ThisNumber !=1; ++i)
	{
		if(ThisNumber % i == 0)
		{
			ThisNumber /= i;
			Sushu.push_back(i);
			++num;
			--i;
		}
	}
	return num;
}
//======================================================================
//求小于10亿且与某个数N互质的数的和,某群入群验证题。。。
//======================================================================
unsigned long long getSum(unsigned long long N)
{
	unsigned long long sum = 0;
	unsigned long long base = 1000000000;
	vector<unsigned long long> Sushu;
	int i = 0;
	int sushuNum = getSushu(N,Sushu);
	bool isHuzhi = false;
	int *sushuArray = new int[sushuNum];
	//此处之所以转为数组,是因为如果在while循环里面直接用vector的话,速度会慢的吓人
	for(i = 0;i < sushuNum;++i)
	{
		sushuArray[i] = Sushu[i];
	}	
	while(base)
	{
		isHuzhi = true;
		for(i = 0;i < sushuNum;++i)
		{
			if(!(base % sushuArray[i]))
			{
				isHuzhi = false;
				break;
			}
		}
		isHuzhi ? sum += base : 0;
		--base;
	}
	delete sushuArray;
	sushuArray = NULL;
	return sum;
}
//======================================================================
//判断一个数是否为素数。。。
//======================================================================
bool isSushu(int n)
{
	int i = 2;
	for(;i< (int)sqrt((float)n);++i)
	{
		if(n % i == 0)
		{
			return false;
		}
	}
	return true;
}

//======================================================================
//测试
int _tmain(int argc, _TCHAR* argv[])
{
	unsigned long long N;
	int i = 0;
	cout <<"请输入要求解的数字: ";
	cin >> N;

	vector<unsigned long long> Sushu;
	getSushu(N,Sushu);
	cout <<"素数分解的结果为:";
	for(; i<(int)Sushu.size(); ++i)
	{
		cout << Sushu[i] << " ";
	}
	cout << endl;
	
	cout <<"求小于10亿且与某个数N互质的数的和:";
	cout << getSum(987654) << endl; //正确值为164851253423261119

	cin >> i;
	return 0;
}

运行时间中的对数

有二分查找、Gcd(欧几里德算法求最大公因数)、幂运算

#include <iostream>
using namespace std;
#define NOT_FOUND -1
//===================================================================
//对分查找,也就是我们经常说的二分法查找
//时间复杂度:logn
//注意事项:
//		1.首位不要忘记
//		2.判断输入数组a[]与数组长度N是否匹配,有可能越界。若输入N = 0,那查个毛啊,或者数组只有100个数,输入了一个102,那也会出错。
//		3.前提:这个数组得是排过序的。
//===================================================================
int BinarySearch(const int a[], int X, int N)
{
	int middle, low, high;
	low = 0;high = N - 1;
	while(low <= high)			//此处一定要是<=,要不然就不会判断到边界
	{
		middle = (high + low) / 2;
		if(a[middle] < X)
			low = middle + 1;	//加1,是因为a[middle]已经判断过了,就不需要再判断
		else if(a[middle] > X)
			high = middle - 1;
		else
			return a[middle];
	}
	return NOT_FOUND;
}


//===================================================================
//欧几里德算法,也就是我们经常说的计算最大公因数Gcd
//时间复杂度:logn
//不用考虑M和N哪个大,如果N大于M,则第一次迭代后他们两个的值就会调换
//===================================================================
unsigned int Gcd(unsigned int M, unsigned int N)
{
	unsigned int Rem;
	while(N > 0)
	{
		Rem = M % N;
		M = N;
		N = Rem;
	}
	return M;
}

//===================================================================
//幂运算,即求整数X的N次方。
//常用的方法是使用N-1次自乘。下面是使用递归的方法
//===================================================================
long int Pow(long int X,unsigned int N)
{
	if(N == 0)
		return 1;
	if(N == 1)
		return X;
	if(N % 2)
		return Pow(X*X , N/2) * X;
	else
		return Pow(X*X , N/2);
}
//===================================================================
//测试
//===================================================================
int main()
{
	int a[10] = {1,2,3,4,5,6,7,8,9,10};
	cout << "a[7] = "<<BinarySearch(a,7,10)<<endl;		//二分查找

	int M, N;
	M = 50; N = 15;
	cout <<"50个15的最大公因数为:"<<Gcd(50,15)<<endl;

	cin>>M;
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值