题目要求,先输入一个数组,然后求出数组中某一个区间中的最大值。
是不是觉得,我就直接用for循环就可以写啦,贼好写。。
这样子讲吧,如果你只搜索一次的话,是看不出什么区别的,但是如果你要搜索M次,M又很大呢? 每一次都要用循环去一个一个比较吗? O(n*M)的时间复杂度是不是会让你体验极差、、
这个题目在经过自己思考与借鉴其他人的博客以后,判断应该是有两种方法可以解决这个问题,分别是线段树和RMQ算法。
先使用线段树算法来做这个题吧,线段树是一个近似于完全二叉树的结构,具体对线段树的了解可以参考这个网站:
https://blog.csdn.net/x314542916/article/details/7837276。。。感觉还可以,话不多说上代码:
#include <iostream>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
using namespace std;
const int MAX = 10003;
int maxv[MAX*3];
int data[MAX];
int n;
void build(int node,int begin,int end){
if(begin == end){
maxv[node] = data[begin];
}else{
int m = (begin+end)/2;
build(2*node,begin,m);
build(2*node+1,m+1,end);
if(maxv[2*node] >= maxv[2*node+1])
maxv[node] = maxv[2*node];
else
maxv[node] = maxv[2*node+1];
}
}
int query(int k,int l,int r,int ql,int qr){
if(ql <= l && r <= qr)
return maxv[k];
int m = l+(r - l)/2;
int ans = -1;
if(ql <= m) ans = max(ans,query(k*2,l,m,ql,qr));
if(qr > m) ans = max(ans,query(2*k+1,m+1,r,ql,qr));
return ans;
}
void update(int k,int l,int r,int p,int v){
int m = l+(r-l)/2;
if(l == r)
maxv[k] = v;
else{
if(p <= m)
update(k*2,l,m,p,v);
else
update(k*2+1,m+1,r,p,v);
maxv[k] = max(maxv[k*2],maxv[k*2+1]);
}
}
void print()
{
for(int i=1;i<2*n;i++)
printf("%d ",maxv[i]);
printf("\n");
}
int main()
{
cin>>n;
for(int i = 0;i < n;i ++){
cin>>data[i];
}
build(1,0,n-1);
int T;
cin>>T;
while(T --){
int ql,qr;
cin>>ql>>qr;
cout<<query(1,0,n-1,ql,qr)<<endl;
}
return 0;
}
还有一种做法叫RMQ算法,这个算法的灵性在于:用一个二维数组存储数据,在查询结果的时候,直接到二维数组中找结果就行了,取结果的时间复杂度为O(1);同时计算的过程的时间复杂度也只需O(n*logn),是一个很高效的算法。下文对RMQ算法的理解引用于https://blog.csdn.net/ACMore_Xiong/article/details/51920019;
- 初始化的过程MAX[i][j] 表示的是区间 [i, i + (1 << j) - 1] 的最大值,区间长度是1<<j:将区间 [i, i + (1 << j) - 1] 分成两个长度都为 1 << (j - 1) 的区间 ,令m = i + (1 << (j - 1)) - 1;[i, m]、[m + 1, m + (1 << (j - 1)) - 1],那么, 我们写出状态转移方程:MAX[i][j] = max(MAX[i][j - 1], MAX[m + 1][j - 1])= max(MAX[i][j - 1], MAX[ i + (1 << (j - 1))][j - 1])
- 查询过程
假如查询区间为[L, R]。区间查询长度为 R - L + 1,令k = (int)log2(R - L + 1), 即小于区间长度的最大的2的幂次方。即k 满足大小关系2^k <= (R-L+1)<=2^(k+1),那么区间[L, R]的最大值就必在区间[L,L + (1 << k) - 1], 或者区间[R-(1<<k) + 1,R]中。
最后给大家看看RMQ的算法代码,大家一起探讨学习啊。
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <iomanip>
#include <iostream>
#include <algorithm>
using namespace std;
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w",stdout)
#define fst first
#define snd second
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef __int64 LL;
//typedef long long LL;
typedef unsigned int uint;
typedef pair<int, int> PII;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
const int MAXN = 10000 + 5;
const int MAXM = 50000 + 5;
const int MAXBIT = 16;
int N, Q;
int a, b;
struct RMQ {
int MAX[MAXN][MAXBIT];
int log2[MAXN];
int n, k;
void init(int _n) {
n = _n;
log2[0] = -1;
for (int i = 1; i <= n; i ++) {
scanf("%d", &MAX[i][0]);
if (i) log2[i] = i & (i - 1) ? log2[i - 1] : log2[i - 1] + 1;
}
for (int j = 1; j <= log2[n]; j ++) {
for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
MAX[i][j] = max(MAX[i][j - 1], MAX[i + (1 << (j - 1))][j - 1]);
}
}
}
int query(const int& L, const int& R) {
k = log2[R - L + 1];
return max(MAX[L][k], MAX[R - (1 << k) + 1][k]);
}
} rmq;
int main() {
#ifndef ONLINE_JUDGE
FIN;
#endif // ONLINE_JUDGE
while (~scanf("%d", &N)) {
rmq.init(N);
scanf("%d", &Q);
while (Q --) {
scanf("%d %d", &a, &b);
a ++, b ++;
int res = rmq.query(a, b);
printf("%d\n", res);
}
}
return 0;
}
今天就写到这里吧,每一天都为了不辜负下一天而奋斗!