Ac.Wing 136 邻值查找

题解:
这个题目是有两种写法的,y总在视频里只讲了list的写法,这里我们也用STL set写了一个。
先说一下两种写法的思路:

  1. 链表写法(OffLine)
    其实不管是使用链表存储还是set存储,算法的核心就是找到每一个点的前驱和后继节点,然后通过比较这两个节点和选定值差的大小关系决定使用哪一个作为最后的答案。我们假设把所有数据存入链表后进行排序,同时用一个数组记录排序后的元素在原链表中的位置是多少,我们从n开始,找每一个节点的前后驱节点,比较后存入一个记录答案集合中,然后把这个节点删除,继续找下一个节点。这里是因为我们可以选择的元素都是在当前元素之前出现的,所以需要及时删除后来的节点。这里使用pair实现的算法,具体见代码
    AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define inf -1e12
#define INF 1e12
#define ios std :: ios :: sync_with_stdio(false)
#define PII pair<ll ,ll>

using namespace std;

const int maxn = 1e5 + 10;

PII a[maxn],b[maxn],ans[maxn];
ll l[maxn],r[maxn];
map<ll ,ll> mp;

int main()
{
    ios;
    int n,cnt = 0;
    cin >> n;
    for(int i = 1;i <= n;i++) {
        cin >> a[i].first;
        a[i].second = i;
    }
    sort(a + 1,a + 1 + n);
    a[0].first = inf;a[n + 1].first = INF; //加入边界便于查找
    for(int i = 1;i <= n;i++){
        l[i] = i - 1;r[i] = i + 1; //记录左右驱节点
        mp[a[i].second] = i; //记录每个节点在排序后的位置
    }
    for(int i = n;i >= 2;i--){
        ll j = mp[i],left = l[j],right = r[j];
//        cout << left << ' ' << right << endl;
        ll maxx = abs(a[j].first - a[left].first),minn = abs(a[j].first - a[right].first);
        if(maxx <= minn){ //优先选择值小的
            ans[++cnt].first = maxx;
            ans[cnt].second = a[left].second;
        }
        else{
            ans[++cnt].first = minn;
            ans[cnt].second = a[right].second;
        }
        l[right] = left;r[left] = right; //删除节点
    }
    for(int i = cnt;i >= 1;i--) cout << ans[i].first << ' ' << ans[i].second << endl;
    return 0;
}
  1. set做法(Online)
    平衡树做法的话就直接可以使用setset是一种自带排序的集合**(默认升序)**,我们就可以每输入一个值,就去查找它的前驱后继节点,其余步骤和链表写法无异。
    但是需要注意的是:在set中,s.end()返回的是最后一个元素再后一位的指针,s.begin()返回的是第一个元素的地址。在实现算法的时候需要注意!
    AC代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<set>
#include<bitset>
#include<queue>
#include<map>
#include<vector>
#define ll long long
#define inf -1e12
#define INF 1e12
#define ios std :: ios :: sync_with_stdio(false)
#define PII pair<ll ,ll>

using namespace std;

const int maxn = 1e5 + 19;

PII a[maxn];

set<PII> s;

int main()
{
    ios;
    int n,x;
    cin >> n >> x;
    s.insert({inf,0});
    s.insert({INF,n + 1});
    s.insert({x,1});
    for(int i = 2;i <= n;i++) {
        cin >> x;
        PII sum;
        ll ans = INF;
        s.insert({x, i});
        auto it = s.find({x,i});
        if(++it != s.end() && ans > abs(it -> first - x)) {
            ans = abs(it -> first - x);
            sum = {ans,it -> second};
        }
        it--;
        if(it-- != s.begin() && ans >= abs(it -> first - x)){
            ans = abs(it -> first - x);
            sum = {ans,it -> second};
        }
        cout << sum.first << ' ' << sum.second << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CUCKyrie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值