有时候需要用数组的值作为下标,有可能会是一个很大的值,那么就导致内存中保存不了,但实际上数组大小是有限的,那么此时就可以通过数组离散化进行处理。
简单来说,离散化关心的是相对大小,而不是某个数具体多大。
简介
离散化是程序设计中一个常用的技巧,它可以有效的降低时间和空间复杂度,例如有如下的一个数组。
1, 23424, 21472313246768, 6594, 95, 0, 65535313
如果将这些数作为数组的下标来保存对应的属性,那么就需要一个很大的数组,但有时只需要知道相对大小,那么就可以使用数组离散化。例如,上述的数据离散化之后就成了如下的值。
1, 4, 6, 3, 2, 0, 5
也就是只体现了相对大小。
实现
简单来说,就是先排个序,然后再重新编号,例如 {10000000, 10, 2000, 20, 300, 20}
重新编码为 {5, 0, 4, 1, 3, 1}
数组,也就是编码从 0
开始,最大为 N-1
其中 N
表示元素数。
这里介绍两种方法,注意上述重复元素的处理。
方法 1
先排序然后重新编号。
struct data_node {
int value;
int index;
};
static int cmp_inc(const void* a, const void* b)
{
const struct data_node *aa = a;
const struct data_node *bb = b;
return aa->value - bb->value;
}
void array_discrete(int *ret, int *arr, int size)
{
struct data_node *nodes;
nodes = malloc(sizeof(struct data_node) * size);
for (int i = 0; i < size; i++) {
nodes[i].index = i;
nodes[i].value = arr[i];
}
qsort(nodes, size, sizeof(struct data_node), cmp_inc);
ret[nodes[0].index] = 0;
for (int i = 1; i < size; i++) {
if (nodes[i].value == nodes[i - 1].value) {
ret[nodes[i].index] = ret[nodes[i - 1].index];
} else {
ret[nodes[i].index] = i;
}
}
free(nodes);
}
方法 2
先排序然后再使用二分查找计算。
// 返回有多少个元素小于target,例如 {1, 2, 3, 4, 4, 5} 4->3 3->2
static int lower_bound(const int *nums, int length, int target)
{
int left, right, mid;
left = 0;
right = length - 1;
while (left <= right) {
mid = left + (right - right) / 2;
if (nums[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return left;
}
static int cmp_int_inc(const void* a, const void* b)
{
return (*(int*)a - *(int*)b);
}
void array_discrete(int *ret, int *arr, int size)
{
memcpy(ret, arr, sizeof(int) * size);
qsort(arr, size, sizeof(int), cmp_int_inc);
for (int i = 0; i < size; i++) {
ret[i] = lower_bound(arr, size, ret[i]);
}
}