链表
邻值查找
给定一个长度为 n 的序列 A,A 中的数各不相同。对于 A 中的每一个数 Ai,求:
1 ⩽ j ⩽ i m i n ∣ A i − A j ∣ _{1\leqslant j\leqslant i}^{min}|Ai−Aj| 1⩽j⩽imin∣Ai−Aj∣
以及令上式取到最小值的 j(记为 Pi)。若最小值点不唯一,则选择使 Aj 较小的那个。
如果暴力求解,遍历数列中的每个数,并搜索其与前面所有数的最小差值,则总的时间复杂度是
O
(
n
2
)
O(n^2)
O(n2)。
我们可以考虑借助链表,将原数列排序,用链表来存储,并建立原数列位置和链表数列位置之间的映射。这样我们可以从后向前遍历原数列,找到链表中其位置,因为链表是排好序的,所以其相邻位置的两个数是与其最接近的,我们比较左右相邻的差值即可。之后再在链表中删除该元素,保证我们我们从后向前遍历原数组的时候,链表中的元素都位于当前遍历元素之前。
如上图所示我们从后向前遍历原数组,当遍历到黄色矩形时,我们根据映射找到其在链表中的位置,并比较两侧差值,最后删除该元素,把两侧元素相连。
#include <iostream>
#include <algorithm>
#include <limits.h>
using namespace std;
const int N = 100010;
//n表示数列长度,index记录排序后的索引,le和ri分别是链表两侧的差值
int n, index, le, ri;
//nums的first记录值、second记录序号,res存储结果
pair<int, int> nums[N], res[N];
//id保存原数列到排序后序列的映射关系,l和r存储链表指针,分别存储左侧和右侧的索引
int id[N], l[N], r[N];
int main(){
cin >> n;
for(int i=1; i<=n; i++){
cin >> nums[i].first;
nums[i].second = i;
}
sort(nums+1, nums+n+1);
//对排序后的数组形成链表,并生成id
for(int i=1; i<=n; i++){
id[nums[i].second] = i;
l[i] = i-1;
r[i] = i+1;
}
//处理边界条件
nums[0].first = 2e9;
nums[n+1].first = -2e9;
//从后向前遍历
for(int i=n; i>1; i--){
index = id[i];
le = abs(nums[index].first - nums[l[index]].first);
ri = abs(nums[index].first - nums[r[index]].first);
//比较当前数与左右两数的差值,存储结果
if(le < ri){
res[i].first = nums[l[index]].second;
res[i].second = le;
}
else if(le > ri){
res[i].first = nums[r[index]].second;
res[i].second = ri;
}
else{
res[i].first = nums[r[index]].first > nums[l[index]].first? nums[l[index]].second: nums[r[index]].second;
res[i].second = le;
}
//删除链表元素
r[l[index]] = r[index];
l[r[index]] = l[index];
}
for(int i = 2; i<=n; i++){
cout << res[i].second << " " << res[i].first << endl;
}
return 0;
}