【ACWing】136. 邻值查找

题目地址:

https://www.acwing.com/problem/content/138/

给定一个长度为 n n n的序列 A A A A A A中的数各不相同。对于 A A A中的每一个数 A i A_i Ai,求: min ⁡ 1 ≤ j < i ∣ A i − A j ∣ \min_{1≤j<i}|A_i−A_j| 1j<iminAiAj以及令上式取到最小值的 j j j(记为 P i P_i Pi)。若最小值点不唯一,则选择使 A j A_j Aj较小的那个。

输入格式:
第一行输入整数 n n n,代表序列长度。
第二行输入 n n n个整数 A 1 … A n A_1…A_n A1An,代表序列的具体数值,数值之间用空格隔开。

输出格式:
输出共 n − 1 n−1 n1行,每行输出两个整数,数值之间用空格隔开。分别表示当 i i i 2 ∼ n 2∼n 2n时,对应的 min ⁡ 1 ≤ j < i ∣ A i − A j ∣ \min_{1≤j<i}|A_i−A_j| min1j<iAiAj P i P_i Pi的值。

数据范围:
n ≤ 1 0 5 , ∣ A i ∣ ≤ 1 0 9 n≤10^5,|A_i|≤10^9 n105,Ai109

方法有很多,比如平衡树,单调栈,等等。这里用链表做。先将所有数做成一个pair,即 ( A [ i ] , i ) (A[i],i) (A[i],i),对其排序,然后将这些数从左到右依次插进一个链表里,同时要记录一下每个数在链表中的节点位置 p [ i ] p[i] p[i],即 p [ i ] p[i] p[i] A [ i ] A[i] A[i]对应的链表节点。接着 i i i n → 2 n\to 2 n2遍历,先找到 A [ i ] A[i] A[i]的位置 p [ i ] p[i] p[i],找到其前驱和后继,那么比它小的最大数和比它大的最小数就找到了(这里为了方便判断,链表的头尾各加一个哨兵,左边的哨兵赋值为 1 0 9 10^9 109,右边赋值为 − 1 0 9 -10^9 109,这样不需要特判 p [ i ] p[i] p[i]恰好在首尾的情况,哨兵的取值要使得哨兵不会存在于答案),找到了之后比较一下哪个更近,一样近则按照题目要求取值较小的,得到 A [ i ] A[i] A[i]的答案之后将其在链表中删去,然后继续求 A [ i − 1 ] A[i-1] A[i1]的答案。代码如下:

#include <iostream>
#include <algorithm>
#define x first
#define y second
using namespace std;
using PII = pair<int, int>;

// 数据里有1e9,INF要多取1个
const int N = 1e5 + 10, INF = 1e9 + 1;
int n;
int p[N], l[N], r[N];
PII a[N], res[N];

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &a[i].x);
    a[i].y = i;
  }

  sort(a + 1, a + 1 + n);
  a[0].x = INF, a[n + 1].x = -INF;
  r[0] = 1, l[n + 1] = n;
  for (int i = 1; i <= n; i++) {
    l[i] = i - 1, r[i] = i + 1;
    p[a[i].y] = i;
  }

  for (int i = n; i > 1; i--) {
    int pos = p[i], left = l[pos], right = r[pos];
    int lv = abs(a[pos].x - a[left].x), rv = abs(a[pos].x - a[right].x);
    if (lv < rv) res[i] = {lv, a[left].y};
    else if (lv > rv) res[i] = {rv, a[right].y};
    else res[i] = {lv, a[left].x < a[right].x ? a[left].y : a[right].y};
    r[left] = right, l[right] = left;
  }

  for (int i = 2; i <= n; i++) printf("%d %d\n", res[i].x, res[i].y);
}

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn),空间 O ( n ) O(n) O(n)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值