有序数值列表最近值无重复索引匹配的高效算法咨询
升序数组最近邻无重复匹配实现方案
核心优化逻辑
- 放弃暴力遍历的O(n*m)复杂度,利用输入数组已升序的特性,对每个待匹配元素仅需二分查找插入位置,单元素查找耗时O(log n)
- 引入并查集(DSU)维护可用索引的关联关系,遇到索引已被占用时,可近似O(1)快速定位左右最近的未占用索引,无需逐一遍历标记数组
- 前置长度校验:若待匹配数组长度大于候选索引数组,直接交换两数组,保证候选索引数量始终充足,无需额外占位处理溢出场景
性能对比
- 暴力搜索最坏情况需遍历所有候选索引,时间复杂度O(n*m),数据量较大时性能极差
- 本方案整体时间复杂度为O(m log n + n α(n)),其中α为阿克曼反函数,近似为常数,百万级数据下也可毫秒级完成
类C实现代码
#include <stdio.h> #include <stdlib.h> #include <math.h> // 并查集结构:维护可用索引的关联 typedef struct { int *parent; int size; } DSU; // 初始化并查集 DSU* dsu_init(int n) { DSU* dsu = (DSU*)malloc(sizeof(DSU)); dsu->size = n; dsu->parent = (int*)malloc(sizeof(int) * n); for (int i = 0; i < n; i++) { dsu->parent[i] = i; } return dsu; } // 查找根节点,带路径压缩 int dsu_find(DSU* dsu, int x) { if (x == -1) return -1; if (dsu->parent[x] != x) { dsu->parent[x] = dsu_find(dsu, dsu->parent[x]); } return dsu->parent[x]; } // 占用索引:将x与左邻居关联 void dsu_unset_left(DSU* left_dsu, int x) { left_dsu->parent[x] = (x > 0) ? dsu_find(left_dsu, x-1) : -1; } // 占用索引:将x与右邻居关联 void dsu_unset_right(DSU* right_dsu, int x) { right_dsu->parent[x] = (x < right_dsu->size - 1) ? dsu_find(right_dsu, x+1) : -1; } // 二分查找:返回第一个 >= target的插入位置 int binary_search(int* arr, int len, int target) { int left = 0, right = len; while (left < right) { int mid = left + (right - left)/2; if (arr[mid] < target) { left = mid + 1; } else { right = mid; } } return left; } // 核心匹配函数 // A: 候选索引数组(升序),B: 待匹配数组(升序) // res: 输出结果数组,长度为len(B) void match_nearest(int* A, int lenA, int* B, int lenB, int* res) { // 前置长度校验:若B更长,交换两数组(按需求规则) if (lenA < lenB) { int* temp_arr = A; A = B; B = temp_arr; int temp_len = lenA; lenA = lenB; lenB = temp_len; } // 初始化左右并查集 DSU* left_dsu = dsu_init(lenA); DSU* right_dsu = dsu_init(lenA); // 遍历顺序可调整:从右到左遍历即可适配数值簇优先匹配规则 for (int i = lenB - 1; i >= 0; i--) { int b = B[i]; int pos = binary_search(A, lenA, b); int left_candidate = -1, right_candidate = -1; int diff_left = 1e9, diff_right = 1e9; // 找pos右侧最近可用索引 if (pos < lenA) { right_candidate = dsu_find(right_dsu, pos); if (right_candidate != -1) { diff_right = abs(A[right_candidate] - b); } } // 找pos-1左侧最近可用索引 if (pos > 0) { left_candidate = dsu_find(left_dsu, pos-1); if (left_candidate != -1) { diff_left = abs(A[left_candidate] - b); } } // 选择差值更小的候选,差值相等时可调整优先级 int selected; if (diff_right < diff_left) { selected = right_candidate; } else if (diff_left < diff_right) { selected = left_candidate; } else { // 差值相等时默认优先选左,可根据需求调整 selected = left_candidate != -1 ? left_candidate : right_candidate; } res[i] = selected; // 标记索引为已占用,更新并查集 dsu_unset_left(left_dsu, selected); dsu_unset_right(right_dsu, selected); } // 释放资源 free(left_dsu->parent); free(left_dsu); free(right_dsu->parent); free(right_dsu); } // 测试用例 int main() { // 示例输入:A = [1,4,66,67,68,71], B = [68,68,68,82,82] int A[] = {1,4,66,67,68,71}; int B[] = {68,68,68,82,82}; int lenA = sizeof(A)/sizeof(A[0]); int lenB = sizeof(B)/sizeof(B[0]); int res[lenB]; match_nearest(A, lenA, B, lenB, res); // 输出优先结果:2 3 4 5 1 for (int i = 0; i < lenB; i++) { printf("%d ", res[i]); } printf("\n"); return 0; }
优先级适配说明
- 上述代码默认从右到左遍历待匹配数组,可直接输出你示例中的优先结果
[2,3,4,5,1] - 若需调整差值相等时的候选选择逻辑,仅需修改候选比较分支的优先级即可
- 长度不匹配场景下自动交换数组,输出结果长度始终与原待匹配数组一致
内容的提问来源于stack exchange,提问作者graphemecluster
相关产品推荐
相关产品推荐

