算法(一)基础算法之递归、二分、前缀和

一、递归

1.1递归

一个函数直接或间接调用自己为递归函数;

递归算法:把问题转化为规模缩小了的同类问题的子问题,然后调用递归函数表示问题的解,其思想是将一个大型而复杂的问题层层简化,转化为一个与 原问题相似的规模较小且简单的子问题,通过多次调用子问题得到最终复杂问题的解。
在递归调用的过程中,系统为每一层的返回点、局部量等开辟了栈来存储,为了避免栈溢出,递归需要有边界条件,必须有明确的递归出口。

1.2分治

把一个复杂的问题分解成若干简单子问题去求解,对于每个子问题再继续分成更小的子问题去求解,对于每个子问题再继续分成更小的子问题,一直分下去,直到最后的子问题可以直接求解时结束。然后将这些子问题合并,得到原问题的解。

需要注意的时,分解的子问题需要满足相互之间相互独立且互不影响,且子问题与原问题形式相同规模不同担忧受原问题的影响。

1.3例题

递归例题
1.母牛的故事

有一头母牛,它每年年初生一头小母牛。每头小母牛从第四个年头开始,每年年初也生一头小母牛。请编程实现在第n年的时候,共有多少头母牛?

解法:直接递归
直接递归存在多次调用,会出现TLE,所以需要用数组进行记忆化

#include<bits/stdc++.h>
using namespace std;
int Cow[999];
int CowNum(int n){
	if(Cow[n]){
		return Cow[n];
	}
	if(n < 4){
		return Cow[n] = n;
	}
	else{
		return Cow[n] = CowNum(n - 1) + CowNum(n - 3);
	}
}
int main(){
	int n;
	while(cin >> n){
		if(n != 0){
			cout << CowNum(n) << endl;
		}
	}
}

数组版

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n;
	while(cin >> n){
	int num[n + 1];
	if(n == 0){
			num[0] =0;
			continue;
		}
	for(int i = 1; i <= n; i++){
		if(i < 4){
			num[i] = i;
		}
		else{
			num[i] = num[i - 3] + num[i - 1];
		}
	}
    if(num[n] != 0){
    	cout << num[n] << endl;
    }
	}
}

2.数的计算
题目描述

我们要求找出具有下列性质数的个数(包含输入的正整数 n)。先输入一个正整数 n(n≤1000),然后对此正整数按照如下方法进行处理:

1. 不作任何处理;
2. 在它的左边加上一个正整数,但该正整数不能超过原数的一半;
3. 加上数后,继续按此规则进行处理,直到不能再加正整数为止。

输入格式

1 个正整数 n(n≤1000)

输出格式

1 个整数,表示具有该性质数的个数。

二、二分

2.1二分搜索

二分搜索又称为折半搜索。使用二分时,要确保数列是具有有序性的,通过比较中间值,不断将搜索范围缩小为原来的一半,大大缩短了查找的时间,时间复杂度为O(logn);
⭐函数lower_bound()在begin和end中的左闭右开区间进行二分查找,返回大于或等于val的第一个元素位置(迭代器)。如果所有元素都小于val,则返回last的位置。(数组必须时排好序的顺序)
注意STL中设计区间都是左闭右开,即[begin,end)。

upper_bound函数:函数upper_bound()在begin和end中的左闭右开区间进行二分查找,返回的是被查序列中第一个大于查找值的位置(迭代器)。

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int a[10];
	for(int i = 0; i < 10; i++){
		a[i] = i;
	}
?
	pos = lower_bound(a,a + 10, 6) - a;  //第一个大于等于6的 
	pos2 = upper_bound(a,a + 10, 6) - a; //第一个大于6的 ,下标从0开始
	cout << pos << endl;
	cout << pos2;
}

vector和set自带lower_bound(first,last,a);

#include<bits/stdc++.h>
#include<vector>
#include<set>
using namespace std;
int main(){
	vector<int> v;
	set<int> st;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	st.insert(1);
	st.insert(2);
	st.insert(3);
	st.insert(4);
	vector<int>::iterator t = lower_bound(v.begin(),v.end(),3);
	set<int>::iterator s = lower_bound(st.begin(),st.end(),3);
	cout << *s <<" " << *s;
} 

set 自带的 lower_bound 和 upper_bound 的时间复杂度为 O(logn)。
但使用 algorithm 库中的 lower_bound 和 upper_bound 函数对 set 中的元素进行查询,时间复杂度为 0(n)
总结:对于可随机访问的有序容器使用 algorithm 库中的 lower_bound 和 upper_bound 函数时间复杂度为O(logn),
但对于set,multiset这种不能随机访问的有序容器,要用其自带的 lower_bound 和 upper_bound 的时间复杂度才为 O(logn)。

2.2从有序数组中查找值

常见问题:判断某个值是否出现在数组中,如果出现求出坐标;X值打一次在数组中的位置等;
题目一:
一个长度为N的有序且不重复的数组,请判断X是否出现在数组中。
输入待查序列有序
❤️1.查找等于某个值的最小下标

#include<bits/stdc++.h>
#include<vector>
#include<set>
using namespace std;
const int N = 1e6 + 10;
int arr[N];
int binsearch(int arr[],int n,int key){
	int ans = -1;
	int l = 0,r = n - 1;
	while(l <= r){
		int mid = (l + r)/2;
		if(arr[mid] == key){
			ans = mid;
			r = mid - 1;   //此时的中间值等于key,有可能前面更小的下标值也为这个 
		}
		else{
			if(arr[mid] > key){
				r = mid - 1;
			}else{
				l = mid + 1;
			}
		} 
	}return ans;
}
int main(){
	int n,k;
	cin >> n >> k;
	for(int i = 0; i < n; i++){
		arr[i] = i;
	}
	cout << binsearch(arr,n,k);
} 

❤️2.如果要查找第一个大于或等于某个值的下标

#include<bits/stdc++.h>
#include<vector>
#include<set>
using namespace std;
const int N = 1e6 + 10;
int arr[N];
int binsearch(int arr[],int n,int key){
	int ans = -1;
	int l = 0,r = n - 1;
	while(l <= r){
		int mid = (l + r)/2;
		🧡if(arr[mid] >= key){
			ans = mid;
			r = mid - 1; }  
		else{
			if(arr[mid] > key){
				r = mid - 1;
			}else{
				l = mid + 1;
			}
		} 
	}return ans;
}
int main(){
	int n,k;
	cin >> n >> k;
	for(int i = 0; i < n; i++){
		arr[i] = i;
	}
	cout << binsearch(arr,n,k);
} 

❤️3.如果要查找第一个大于某个值的下标

#include<bits/stdc++.h>
#include<vector>
#include<set>
using namespace std;
const int N = 1e6 + 10;
int arr[N];
int binsearch(int arr[],int n,int key){
	int ans = -1;
	int l = 0,r = n - 1;
	while(l <= r){
		int mid = (l + r)/2;
		if(arr[mid] > key){
			ans = mid;
			r = mid - 1; }  
		else{
			if(arr[mid] > key){
				r = mid - 1;
			}else{
				l = mid + 1;
			}
		} 
	}return ans;
}
int main(){
	int n,k;
	cin >> n >> k;
	for(int i = 0; i < n; i++){
		arr[i] = i;
	}
	cout << binsearch(arr,n,k);
} 

❤️4.求解在数组arr中找出值为key的元素个数

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int a[10];
	for(int i = 0; i < 10; i++){
		cin >> a[i];
		}
	int num = upper_bound(a, a + 10, 2) - a - (lower_bound(a,a + 10, 2) - a);
	cout << num;
}

如果要求arr中key的个数,只需要调用upperBound - lowerBound,求它们的差就可以了!是不是超简单!

三、前缀和

3.1前缀和

前缀和就是从位置1到位置i这个区间内的所有的数字之和。

for (int i = 1; i <= n; i ++ )
{ cin >> a[i]; s[i] = s[i - 1] + a[i]; }

在这里插入图片描述
在这里插入图片描述
缀和的优势:以(o1)的时间复杂度得到某块区间的总和
在这里插入图片描述

3.2前缀和例题

输入

一个长度为n的整数序列。 接下来再输入m个询问,每个询问输入一对l, r。 对于每个询问,输出原序列中从第l个数到第r个数的和

输入格式

第一行包含两个整数n和m。
第二行包含n个整数,表示整数数列。
接下来m行,每行包含两个整数l和r,表示一个询问的区间范围。

输出格式

共m行,每行输出一个询问的结果。

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 1e6 + 10;
int n,m;
int a[N],s[N];
int main(){
	cin >> n >> m;
	for(int i = 1; i <= n; i++){
		cin >> a[i];
		s[i] = s[i - 1]  + a[i];
	}
	while(m--){
		int l,r;
		cin >> l >> r;
		cout << s[r] - s[l - 1] << endl;
	}
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

释怀°Believe

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

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

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

打赏作者

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

抵扣说明:

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

余额充值