您的位置:首页 > 理论基础 > 数据结构算法

THU数据结构编程作业一:查询范围(Range)

2015-09-24 21:00 453 查看
查询范围(Range)

描述

数轴上有n个点,对于任一闭区间 [a, b],试计算落在其内的点数。

输入

第一行包括两个整数:点的总数n,查询的次数m。

第二行包含n个数,为各个点的坐标。

以下m行,各包含两个整数:查询区间的左、右边界a和b。

输出

对每次查询,输出落在闭区间[a, b]内点的个数。

样例

Input

5 2
1 3 7 9 11
4 6
7 12


Output

0
3


限制

0 ≤ n, m ≤ 5×10^5

对于次查询的区间[a, b],都有a ≤ b

各点的坐标互异

各点的坐标、查询区间的边界a、b,均为不超过10^7的非负整数

时间:2 sec

内存:256 MB

先上完整程序:

#include<iostream>
#include<cstdio>
using namespace std;

const int SZ = 1<<20;
struct fastio{   //fast io
char inbuf[SZ];
char outbuf[SZ];
fastio(){
setvbuf(stdin,inbuf,_IOFBF,SZ);
setvbuf(stdout,outbuf,_IOFBF,SZ);
}
}io;

int binary_search(int *a, int Length, int goal, int &border);
void QuickSort(int *a, int lo, int hi);

int main(){
#ifndef _OJ_
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif

int IntervalSize(0), QueryTimes(0);         // interval size and query times
int *points;                                //store interval points
scanf("%d %d", &IntervalSize, &QueryTimes);
points = new int[IntervalSize + 2]();       //new memory for interval points
for(int i = 1; i < IntervalSize + 1; ++i)
scanf("%d", &points[i]);
points[0] = -1; //set a sentry
points[IntervalSize + 1] = 10000000+1; //set a sentry
QuickSort(points, 0, IntervalSize + 1);

int (*query)[2] = new int[QueryTimes][2](); //new memory for query interval
for(int i = 0; i < QueryTimes; ++i)
scanf("%d %d", &query[i][0], &query[i][1]);

int *result = new int[QueryTimes]();        //new memory to store results
for(int i = 0; i < QueryTimes; ++i){
//find the subscripts of query intervals and get the subtrations as the resluts
int borderL(0), borderR(0), diff;
diff = binary_search(points, IntervalSize+2, query[i][1], borderR) - binary_search(points, IntervalSize+2, query[i][0], borderL);
if(borderL && borderR)
result[i] = diff + 1;
else if(borderL && !borderR)
result[i] = diff + 1;
else
result[i] = diff;

}

for(int i = 0; i < QueryTimes; ++i)
printf("%d\n", result[i]);

delete[] points;
delete[] query;
delete[] result;
return 0;
}
int binary_search(int *a, int Length, int goal, int &border){
int lo = 0, hi = Length - 1;
while (lo <= hi){
int mid = (lo + hi) / 2;
if(a[mid] <= goal && goal < a[mid + 1]){
if(a[mid] == goal){
border = 1;
return mid ;
}else
return mid;
}else if(a[mid] > goal)
hi = mid ;
else
lo = mid ;
}
}
//quick sort 1
void QuickSort(int *a, int lo, int hi){
if(lo < hi){
int i = lo, j = hi, x = a[lo];
while(i < j){
while(i < j && a[j] >= x)
--j;
if(i < j)
a[i++] = a[j];
while(i < j && a[i] < x)
++i;
if(i < j)
a[j--] = a[i];
}
a[i] = x;
QuickSort(a, lo, i-1);
QuickSort(a, i+1, hi);
}
}
//quick sort 2
/*
inline void Swap( int &i, int &j){
int k;
k = i;
i = j;
j = k;
}

int quick_sort(int *a, int lo, int hi){
int index = lo, x = a[lo];
Swap(a[lo], a[hi]);
for(int i = lo; i < hi; ++i){
if(a[i] < x)
Swap(a[index++], a[i]);
}
Swap(a[index], a[hi]);
return index;
}
void QuickSort(int *a, int lo, int hi){
if(lo < hi){
int index = quick_sort(a, lo, hi);
QuickSort(a, lo, index-1);
QuickSort(a, index+1, hi);
}
}
*/


以上程序能够100%通过THU的OJ。

本题设计的主要思路是:

···首先对数轴上的点进行排序(开始以为给定的输出点已经排好序,后来发现并不是);

···确定给定的区间端点在点序列中的位置应该在哪里,注意区间是闭合的,端点问题需要着重考虑的;

···确定两端点的位置之后就可以计算出该区间包含有多少个点了。

注意:题目中的坐标和查询范围大于等于0并且均不超过10^7,因此我在坐标数组的开头和结尾分别插入一个哨兵(sentry)。见程序的30行和31行,方便确定查询边界点在坐标数组中的位置,也因此数组的大小增加了2.

一下部分程序的解释:

1、 OJ上测试的数据量是非常大的,所以在设计程序时必须考虑时间复杂度。首先为了实现快速输入输出,调大流缓冲区。

const int SZ = 1<<20;
struct fastio{   //fast io
char inbuf[SZ];
char outbuf[SZ];
fastio(){
setvbuf(stdin,inbuf,_IOFBF,SZ);
setvbuf(stdout,outbuf,_IOFBF,SZ);
}
}io;


开始我没有想到要把流缓冲区调大,所以程序运行的速度比较慢,在做到第二题的时候看到大神的代码才恍然大悟:/article/11199971.html

2、 输入输出重定向

#ifndef _OJ_
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif


为了方便调试,将输入输出(printf,scanf)进行了重定向,直接从文件读取并输出到文件。c++中hi、还有另外一种方法对cin,cout重定向:

std::ifstream in("in.txt");
std::streambuf *cinbuf = std::cin.rdbuf(); //save old buff
std::cin.rdbuf(in.rdbuf()); //redirect std::cin to in.txt!

std::ofstream out("out.txt");
std::streambuf *coutbuf = std::cout.rdbuf(); //save old buf
std::cout.rdbuf(out.rdbuf()); //redirect std::cout to out.txt!


主函数结束之前重置输入输出:

std::cin.rdbuf(cinbuf);   //reset to standard input again
std::cout.rdbuf(coutbuf); //reset to standard output again


3、本体的重点应该在排序和查找算法上,因为数据量很大,因此我选择了二分查找(时间复杂度log(n))和快速排序算法

(最理想的时间复杂度为nlog(n),有看到过说随机快速排序更好点)

二分查找:因为要考虑边界问题所以我加入了一个记录边界是否包含的函数参数(int & border)。

int binary_search(int *a, int Length, int goal, int &border){
int lo = 0, hi = Length - 1;
while (lo <= hi){
int mid = (lo + hi) / 2;
if(a[mid] <= goal && goal < a[mid + 1]){
if(a[mid] == goal){
border = 1;
return mid ;
}else
return mid;
}else if(a[mid] > goal)
hi = mid ;
else
lo = mid ;
}
}


快速排序有两种写法:

//quick sort 1
void QuickSort(int *a, int lo, int hi){
if(lo < hi){
int i = lo, j = hi, x = a[lo];
while(i < j){
while(i < j && a[j] >= x)
--j;
if(i < j)
a[i++] = a[j];
while(i < j && a[i] < x)
++i;
if(i < j)
a[j--] = a[i];
}
a[i] = x;
QuickSort(a, lo, i-1);
QuickSort(a, i+1, hi);
}
}


/*
inline void Swap( int &i, int &j){
int k;
k = i;
i = j;
j = k;
}

int quick_sort(int *a, int lo, int hi){
int index = lo, x = a[lo];
Swap(a[lo], a[hi]);
for(int i = lo; i < hi; ++i){
if(a[i] < x)
Swap(a[index++], a[i]);
}
Swap(a[index], a[hi]);
return index;
}
void QuickSort(int *a, int lo, int hi){
if(lo < hi){
int index = quick_sort(a, lo, hi);
QuickSort(a, lo, index-1);
QuickSort(a, index+1, hi);
}
}


另外,THU的OJ中去掉了例如vector,algorithm的头文件,所以写C++的时候会有点纠结到底哪些头文件可以用哪些不可以用,所以尽量只适用基本的数据类型和操作。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: