链表

链表

邻值查找

给定一个长度为 n 的序列 A,A 中的数各不相同。对于 A 中的每一个数 Ai,求:
1 ⩽ j ⩽ i m i n ∣ A i − A j ∣ _{1\leqslant j\leqslant i}^{min}|Ai−Aj| 1jiminAiAj
以及令上式取到最小值的 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值