邻值查找


题目

题目描述

给定一个长度为 nn 的序列 A,A 中的数各不相同。对于 A 中的每一个数 Ai,求:

min(1≤j<i) |Ai−Aj|min(1≤j<i) ⁡|Ai−Aj|

以及令上式取到最小值的 j(记为 Pi)。若最小值点不唯一,则选择使 Aj较小的那个j


输入

第一行一个整数 n,第二行 n 个数 A1至An。

输出

n−1 行,每行 2 个用空格隔开的整数。分别表示当 i∈[2,n]时,对应的 min(1≤j<i) |Ai−Aj| 和 Pi 的值。


样例输入

3
1 5 3

样例输出

4 1
2 1

数据规模与约定

时间限制:1 s

内存限制:256 M

100% 的数据保证 n≤105, Ai <= 1e9

思路

  • 首先读懂题目

  • 排序:升序

  • 从原始数列最后一个元素开始查找,查找完成把该元素剔除

题解 1:利用数组做的

自己写的3000ms左右
#include 
#include 
#include 
#include 
using namespace std;

typedef pair  PA;
typedef long long ll;

PA nums[100005];
PA da[100005]; //da[i].first = min|Ai - Aj|, da[i].second = Pj; i > j >= 1; 保存答案,方便输出; 
int key[100005];

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &nums[i].first);
        nums[i].second = i;
    }
    sort(nums + 1, nums + n + 1);
//    for (int i = 1; i <= n; i++) {
//        cout << nums[i].first << ' ' << nums[i].second << endl;
//    }
    for (int i = 1; i <= n; i++) {
        key[nums[i].second] = i; //排序后数列位置 <= 数列原位置 
    }
    int min = 1e9 + 10;
    int max = -1e9 - 10;
    nums[0] = {min, 0};
    nums[n + 1] = {max, n + 1};
    for (int i = n; i >= 2; i--) { //i指向原数列元素的位置 
        int key_i = key[i]; //key_i指向排序后元素位置;找到原数列元素在排序后数列的位置
        int i_y = key_i + 1; //指向排序后key_i右边的元素
        while (i_y < n + 1 && nums[i_y].first == max) i_y++;
        int i_z = key_i - 1; //指向排序后key_i左边的元素 
        while (i_z && nums[i_z].first == max) i_z--;
        ll abs1 = abs(nums[i_z].first - nums[key_i].first); //右边数 
        ll abs2 = abs(nums[i_y].first - nums[key_i].first); //左边数
        if (abs1 > abs2) {
            da[i].first = abs2, da[i].second = nums[i_y].second;
        } else {
            da[i].first = abs1, da[i].second = nums[i_z].second;
        }
        nums[key_i].first = max;
    }
    for (int i = 2; i <= n; i++) printf("%d %d\n", da[i].first, da[i].second);
    return 0;
}

题解 2:自己写的双向链表

自己用C写的2000ms左右
思路
  • 主要处理边界问题
#include 
#include 
#include 

typedef struct root {
    int key;
    int val;
} root;

//结构定义
typedef struct Node { //节点定义 
    root *val;
    struct Node *next; // 后驱指针 -> 
    struct Node *per;  //前驱指针 <-
} Node;

typedef struct DList {
    Node head;
} DList;

DList *init_DList() { //链表初始化 
    DList *l = (DList *)malloc(sizeof(DList));
    l->head.next = NULL;
    return l;
}

Node *getNewNode(root *val) { //节点初始化
    if (val == NULL) return NULL;
    Node *n = (Node *)malloc(sizeof(Node));
    n->val = val;
    n->next = n->per = NULL;
    return n;
}

void insert(DList *l, root *val) { // 头插法 节点插入
    if (l == NULL) return ;
    Node *p = &(l->head), *n = getNewNode(val);
    n->next = p->next;
    l->head.next = n;
    if (n->next) n->next->per = n;
    return ;
}

void delete_Node(DList *l, Node *n) { //节点删除
    if (n == NULL) return ;
    Node *p = n->next, *q = n->per;
    if (p == NULL) {
        q->next = NULL;
    } else if (q == NULL) {
        l->head.next = p;
        p->per = NULL;
    } else {
        p->per = q;
        q->next = p;
    }
    free(n);
    return ;
}

root nums[100005];
root da[100005];

void quick_sort(root *num, int l, int r) {//结构体快速排序 
    //数据越乱越好 
    if (l > r) return ;
    int x = l, y = r;
    root z = num[x];
    while (x < y) {
        while (x < y && num[y].val > z.val) y--;
        if (x < y) num[x++] = num[y];
        while (x < y && num[x].val < z.val) x++;
        if (x < y) num[y--] = num[x];
    }
    num[x] = z;
    quick_sort(num, l, x - 1);
    quick_sort(num, x + 1, r);
    return ;
}

int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &nums[i].val);
        nums[i].key = i;
    }
    quick_sort(nums, 1, n);
//    for (int i = 1; i <= n; i++) {
//        cout << nums[i].val << ' ' << nums[i].key << endl;
//    }
    DList *l = init_DList();
    for (int i = n; i >= 1; i--) {
        //vis[nums[i].key] = i;
        insert(l, &nums[i]);
    }
    Node *q = l->head.next; // 保存上一个位置
    for (int i = n; i >= 2; i--) {
        Node *p = q;
        int pos = i;
        while (p->val->key != pos && p->next) p = p->next;
        if (pos != p->val->key) {
            p = q;
            while (p->val->key != pos && p->per) p = p->per;
        }
        int data = p->val->val;
        int abs1 = 2e9 + 10, abs2 = 2e9 + 10;
        if (p->next) {
            int qian = p->next->val->val;
            q = p->next;
            abs1 = abs(qian - data);
        }
        
        if (p->per) {
            int hou= p->per->val->val;
            q = p->per;
            abs2 = abs(hou - data);
        }
        
        if (abs1 >= abs2) {
            da[i].val = abs2;
            da[i].key = p->per->val->key;
        } else {
            da[i].val = abs1, da[i].key = p->next->val->key;
        } 
        delete_Node(l, p);
    }
//    for (int i = 2; i <= n; i++) {
//        cout << da[i].first << ' ' << da[i].second << endl;
//    }
    for (int i = 2; i <= n; i++) printf("%d %d\n", da[i].val, da[i].key);
    free(l); 
    return 0;
}

题解3:利用数组实现双向链表

看了视频后自己写的,120ms左右
#include 
#include 
#include 

using namespace std;

#define N 100005 

typedef pair PII;

PII nums[N], ans[N];
int r[N], l[N], vis[N];
// 数组vis用来查找原始数列在排序后数列的位置
// 数组r为双向链表的右指针,l为左指针 




int main() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &nums[i].first);
        nums[i].second = i;
    }
    sort(nums + 1, nums + 1 + n);
    for (int i = 1; i <= n; i++) {
        vis[nums[i].second] = i; //查找原始数列在排序后数列的位置
        r[i] = i + 1, l[i] = i - 1; //链接链表
    }
    
    //边界处理 
    nums[0] = {1e9 + 10, 0};
    nums[n + 1] = {-(1e9 + 10), n + 1};
     
    for (int i = n; i > 1; i--) { //从n开始保证是最大的 
        int pos = vis[i]; 
        int right = r[pos], left = l[pos];
        int abs_r = abs(nums[pos].first - nums[right].first);
        int abs_l = abs(nums[pos].first - nums[left].first);
        if (abs_l <= abs_r) {
            ans[i].first = abs_l, ans[i].second = nums[left].second;
        } else {
            ans[i].first = abs_r, ans[i].second = nums[right].second;
        }
        
        //删除节点
        r[left] = right, l[right] = left;
    }
    for (int i = 2; i <= n; i++) printf("%d %d\n", ans[i].first, ans[i].second);
    return 0;
}
用了快读与快写,直接跑到112ms
#include 
#include 
#include 

using namespace std;

#define N 100005 

typedef pair PII;

PII nums[N], ans[N];
int r[N], l[N], vis[N];
// 数组vis用来查找原始数列在排序后数列的位置
// 数组r为双向链表的右指针,l为左指针 


inline int read() {
    int x = 0, w = 0;
    char ch = getchar();
    while (ch > '9' || ch < '0') {
        w |= ch == '-';
        ch = getchar();
    } 
    while (ch >= '0' && ch <= '9') {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    return w ? -x : x;
}


inline void write(int x) { //快写 
    
    char f[200];
    
    if (!x) {
        putchar('0');
        return ;
    }
    int tmp = x > 0 ? x : -x;
    int cnt = 0;
    (x < 0) && putchar('-');
    while (tmp > 0) {
        f[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) {
        putchar(f[--cnt]);
    }
} 
int main() {
    int n;
    n = read();
    for (int i = 1; i <= n; i++) {
        nums[i].first = read();
        nums[i].second = i;
    }
    sort(nums + 1, nums + 1 + n);
    for (int i = 1; i <= n; i++) {
        vis[nums[i].second] = i; //查找原始数列在排序后数列的位置
        r[i] = i + 1, l[i] = i - 1;
    }
    
    //边界处理 
    nums[0] = {1e9 + 10, 0};
    nums[n + 1] = {-(1e9 + 10), n + 1};
     
    for (int i = n; i > 1; i--) { //从n开始保证是最大的 
        int pos = vis[i]; 
        int right = r[pos], left = l[pos];
        int abs_r = abs(nums[pos].first - nums[right].first);
        int abs_l = abs(nums[pos].first - nums[left].first);
        if (abs_l <= abs_r) {
            ans[i].first = abs_l, ans[i].second = nums[left].second;
        } else {
            ans[i].first = abs_r, ans[i].second = nums[right].second;
        }
        //删除节点
        r[left] = right, l[right] = left;
    }
    
    
    for (int i = 2; i <= n; i++) 
        write(ans[i].first), putchar(' '), write(ans[i].second), putchar('\n');
    return 0;
}

总结

  • 通过这道题学习到了双向链表利用数组实现的方法

  • 利用两个方向数组实现了链表的双向功能,和删除节点的功能

  • 利用数组实现的双向链表查找和删除时间复杂度多少O(1),远远比利用结构体快得多,结构体查找时间复杂度为O(n),删除功能为O(1);


文章作者: Axieyun
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Axieyun !
评论
评论
  目录