一些高效技巧与算法-学习笔记

打表

用空间换时间的技巧,一般指将所有可能需要用到的结果事先计算出来,这样后面需要用到时就可以直接查表获得
常见用法:
1.在程序中一次性计算出所有需要用到的结果,之后的查询直接取这些结果
这是最常用到的用法,在一个需要查询大量Fibonacci数F(n)的问题中,显然每次从头开始计算是非常耗时的,对q次查询会产生nq的时间复杂度;如果进行预处理,即把所有Fibonacci数预先计算并存在数组中,那么每次查询就只需要O(1)的时间复杂度,q次查询只需要n+q的时间复杂度。
2.在程序b中分一次或多次计算出所有需要用到的结果,手工把结果写在程序A的数组中,然后在程序A中就可以直接使用这些结果。
3.对一些感觉不会做的题目,先用暴力程序计算小范围数据的结果,然后找规律,或许能发现一些蛛丝马迹。

活用递推

涉及序列的题目
【pat b1040】有几个pat
字符串APPAPT中包含了两个单词“PAT”,现给定字符串,问一共可以形成多少个PAT?
思路:
对一个确定位置的A来说,以它形成的PAT的个数等于它左边P的个数乘以他右边T的个数。对APPAPT的中间那个A来说,它左边有两个P,右边有一个T,计算它左边P的个数与它右边T的个数的乘积,然后把所有A的这个乘积相加就是答案。
获得每一位左边P的个数的方法:设定一个数组leftNumP,记录每一位左边P的个数,接着从左到右遍历字符串,如果当前位i是P,那么leftNumP[i]就等于leftNumP[i-1]加1;如果当前位i不是P,那么leftNumP[i]就等于leftNumP[i-1]。
以同样方法可计算出每一位右边T的个数。定义变量rightNumT,记录当前累计右边T的个数。

#include <cstdio>
#include <cstring>
const int MAXN=100010;
const int MOD=1000000007;
char str[MAXN];	//字符串
int leftNumP[MAXN]={0}; 
int main(){
	//printf("请输入由P,A,T组成的字符串:"); 
	gets(str);	//读入字符串
	int len=strlen(str);
	for(int i=0;i<len;i++){
		if(i>0){
			leftNumP[i]=leftNumP[i-1];	//继承上一位的结果 
		}
		if(str[i]=='P'){
			leftNumP[i]++;
		}
	}
	int ans=0,rightNumT=0;	//ans为答案,rightNumT记录右边T的个数
	//从右往左遍历字符串,找T 
	for(int i=len-1;i>=0;i--){
		if(str[i]=='T'){
			rightNumT++;
		} else if(str[i]=='A'){
			ans=(ans+leftNumP[i]*rightNumT)%MOD;//累计乘积,左边的每一个P都要与T组合 
		}
	} 
	//printf("所能组成的PAT个数为:");
	printf("%d\n",ans);
	return 0;
}

随机选择算法

问题:如何从一个无序的数组中求出第K大的数,假设数组中的数各不相同。例如,对数组{5,12,7,2,9,3}来说,第三大的数是5,第五大的数是9。
首先想到的是对数组排序,然后直接取出第K 个元素即可。但这样做需要O(nlogn)的时间复杂度,仍可优化。
随机选择算法,对任何输入都可以达到O(n)的期望时间复杂度。

int randPartition(int A[],int left,int right){
	int p=(round(1.0*rand()/RAND_MAX*(right-left)+left)); //数组第一个数作为枢轴,取出 
	swap(A[p],A[left]);
	int pivot=A[left];
	while(left<right){
		while(left<right && A[right]>pivot) right--; //右指针往左移 
		A[left]=A[right];		//while条件不满足时,右边的数小,移到左边 
		while(left<right && A[left]<=pivot) left++;	//左指针往右移 
	 	A[right]=A[left];		//while条件不满足时,左边的数大,移到右边 
	}
	//while循环结束,left=right 
	A[left]=pivot;	//pivot给left与right相遇的地方
	return left; 	//返回相遇的下标 
} 
//随机选择算法,从A[left,right]中返回第K大的数
int randSelect(int A[],int left,int right,int K){
	if(left==right)	return A[left];
	int p=randPartition(A,left,right);
	int M=p-left+1;
	if(K==M) 	return A[p]; 	//找到第K大的数
	if(K<M){
		return randSelect(A,left,p-1,K);	//往左侧找
	}else{
		return randSelect(A,p+1,right,K-M);
	} 	
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值