原題連結:https://www.luogu.com.cn/problem/P5076
題意解讀:此題本質上是要實現一個二叉搜尋樹的功能。
解題思路:
從資料規模10^4來看,只要複雜度在n^2範圍內基本上是可以透過的,下面給出兩種做法:
1、有序陣列法
對應5個操作的實現邏輯如下:
操作一:查x的排名。直接透過二分查詢>=x的第一個數的位置pos,pos即x的排名,複雜度O(logN);
操作二:查排名x的數。直接返回x下標的元素,複雜度O(1);
操作三:x的前驅。透過二分查詢>=x的最小的數的位置pos,x的前驅即pos-1位置的數,不存在要輸出 −2147483647,複雜度O(logN);
操作四:x的後繼。透過二分查詢<=x的最大的數的位置pos,x的後繼即pos+1位置的數,不存在要輸出2147483647,複雜度O(logN);
操作五:插入x。透過二分查詢>=x的最小的數的位置pos,如果pos不存在,則將x新增到最後,否則從pos開始把所有數往後移一位,將x放入pos,複雜度<=O(logN + N)。
總體複雜度<O(N^2)
100分程式碼:
#include <bits/stdc++.h>
using namespace std;
const int N = 10005;
int a[N], idx;
int bs1(int x)
{
int l = 1, r = idx, ans = -1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(a[mid] >= x) ans = mid, r = mid - 1;
else l = mid + 1;
}
if(ans == -1) ans = idx + 1; //如果不存在,要找的位置就是當前最後一個數的下一個位置
return ans;
}
int bs2(int x)
{
int l = 1, r = idx, ans = -1;
while(l <= r)
{
int mid = (l + r) >> 1;
if(a[mid] <= x) ans = mid, l = mid + 1;
else r = mid - 1;
}
if(ans == -1) ans = 0; //如果不存在,要找的位置就是當前第一個數前一個位置
return ans;
}
int main()
{
int q, op, x;
cin >> q;
while(q--)
{
cin >> op >> x;
if(op == 1)
{
int r = bs1(x); //查詢第一個>=x的位置,即x的排名
cout << r << endl;
}
else if(op == 2) cout << a[x] << endl; //返回排名x的數
else if(op == 3)
{
int pos = bs1(x); //查詢>=x的最小的數的位置pos
if(pos - 1 > 0) cout << a[pos - 1] << endl; //x的前驅即pos-1位置的數
else cout << -2147483647 << endl; //不存在要輸出 −2147483647
}
else if(op == 4)
{
int pos = bs2(x); //查詢<=x的最大的數的位置pos
if(pos + 1 <= idx) cout << a[pos + 1] << endl; //x的後繼即pos+1位置的數
else cout << 2147483647 << endl; //不存在要輸出2147483647
}
else
{
int pos = bs1(x); //查詢>=x的最小的數的位置pos
if(pos == idx + 1) a[++idx] = x; //如果沒有找到,說明所有數都比x小,新增到最後
else
{
for(int i = idx; i >= pos; i--)
a[i + 1] = a[i]; //從pos開始把所有數往後移一位
a[pos] = x; //將x放入pos
idx++; //最後一個元素的位置更新
}
}
}
return 0;
}
2、set法(底層是紅黑樹-一種平衡的二叉搜尋樹)
set有兩個實現了二分查詢功能的函式要介紹一下:
s.lower_bound(x); //返回容器中第一個大於等於x的數的迭代器
s.upper_bound(x);//返回容器中第一個大於x的數的迭代器
對應5個操作的實現邏輯如下:
操作一:查x的排名。透過lower_bound(x)查詢第一個>=x的數的迭代器it,從迭代器s.begin()遍歷到it,累計cnt即為x的排名,複雜度<=O(logN + N);
操作二:查排名x的數。從迭代器s.begin()遍歷x次,取第x個元素的值,複雜度<=O(N);
操作三:x的前驅。透過lower_bound(x)查詢第一個>=x的數的迭代器it,x的前驅即it--位置的數,不存在要輸出 −2147483647,複雜度O(logN);
操作四:x的後繼。透過upper_bound(x)查詢第一個>x的數的迭代器it,x的後繼*it,不存在要輸出2147483647,複雜度O(logN);
操作五:插入x。透過insert(x)插入資料,複雜度O(logN)。
總體複雜度<O(N^2)
100分程式碼:
#include <bits/stdc++.h>
using namespace std;
set<int> tree;
int main()
{
int q, op, x;
cin >> q;
while(q--)
{
cin >> op >> x;
if(op == 1)
{
int cnt = 1;
set<int>::iterator end = tree.lower_bound(x); //查詢第一個>=x的數的迭代器
for(set<int>::iterator it = tree.begin(); it != end; it++) cnt++; //從迭代器s.begin()遍歷到it,累計cnt即為x的排名
cout << cnt << endl;
}
else if(op == 2)
{
int cnt = 1;
set<int>::iterator it = tree.begin();
while(it != tree.end() && cnt != x) it++, cnt++; //從迭代器s.begin()遍歷x次,取第x個元素的值
cout << *it << endl;
}
else if(op == 3)
{
set<int>::iterator it = tree.lower_bound(x); //查詢第一個>=x的數的迭代器it
if(it != tree.begin()) cout << *(--it) << endl; //x的前驅即--it位置的數
else cout << -2147483647 << endl; //不存在要輸出 −2147483647
}
else if(op == 4)
{
set<int>::iterator it = tree.upper_bound(x); //查詢第一個>x的數的迭代器it
if(it != tree.end()) cout << *it << endl; //x的後繼*it
else cout << 2147483647 << endl; //不存在要輸出2147483647
}
else tree.insert(x); //插入資料
}
return 0;
}