【问题标题】:How can I check in which array a value is in C?如何检查 C 中的值在哪个数组中?
【发布时间】:2016-12-15 12:37:50
【问题描述】:

假设我有三个数组:

const uint8_t arr_1[] = {1, 4, 53 , 209};
const uint8_t arr_2[] = {54, 56, 90, 100};
const uint8_t arr_3[] = {6, 7, 8, 9, 10};

如何有效地检查给定值uint8_t val 是否在其中一个数组中,如果是,则在哪个数组中。一种方法是遍历所有数组直到找到值,但这不是很有效。该解决方案需要扩展到更多阵列。如果需要,可以对每个数组中的值进行排序。

【问题讨论】:

标签: c arrays algorithm


【解决方案1】:

我将在您的帖子中总结我的 cmets 在一个答案中。希望它对某人有用。

如果您为每个数组保存一个指针及其值范围,可以这样说:

struct arr_meta_data {
  const uint8_t *p_arr;
  uint8_t min;
  uint8_t max;
};

然后您可以根据范围定义的偏序对struct arr_meta_data 的数组进行排序。

查找一个值是一个两步过程

  1. struct arr_meta_data的数组中进行二分查找,找到第一个数组可以所在的位置。 O(log M) 其中M 是您拥有的数组数量。

  2. 在值在范围内的每个数组中向前移动和二进制搜索。一旦到达一个值小于最小值的数组,您可以停止并返回未找到该值。

【讨论】:

  • "根据范围的偏序对struct arr_meta_data的数组进行排序" --> 是模糊的。在min max 上如何完成一维排序?
  • @chux 理想情况下,按 min 排序,然后按 max 进行稳定排序应该给出所需的顺序。我正在设想一种遍历,它将以“自然”顺序访问真实线上的每个线段。恐怕现在我无法准确定义它
  • 嗯。考虑在min 值上对数组进行排序的输入:{1, 50}, { 2, 5}, {10, 10}; --> 即使所有 3 个都是包含10 的候选数组,基于min 值,“一旦你到达一个数组,其中的值再次超出范围”,搜索将在第二个数组{ 2, 5} 之后停止,因为 10 超出了数组的范围。 OTOH,如果您的意思是“一旦达到值大于最小值的数组”,我认为您的算法将起作用。
  • @chux 是的,我明白你的意思。虽然我认为你的意思是“低于最低限度”
【解决方案2】:

受到@StoryTeller 的启发,我尝试使用他的方法。

我只考虑给定的 3 个数组来构建代码:

const uint8_t arr_1[] = {1, 4, 53 , 209};
const uint8_t arr_2[] = {54, 56, 90, 100};
const uint8_t arr_3[] = {6, 7, 8, 9, 10};

因为我不知道数据是如何给出的。这个想法是正确的,我假设数组总是排序的。如果将来不是这种情况,那么您可以事先使用qsort 对数组进行排序,平均运行时间为 O(nlogn)

在下面的代码中,我使用了:

  1. stdlib.h提供的内置bsearch函数,省去你自己写的麻烦。使用这个二分搜索工具仍然允许 O(logn) 搜索时间,这比线性搜索消耗的 O(n) 时间要好得多。

  2. 结构数组设置,收集有关每个数组的特定信息,例如检查[min, max] 范围,并跳过与搜索键值不在同一范围内的数组

    设置:

    typedef struct {
        const uint8_t *p_arr;
        uint8_t currsize; * size of each array */
        uint8_t max; /* Ranges */
        uint8_t min;
    } array_t;
    
    typedef struct {
        array_t *A; /* pointer to array information */
        uint8_t numarrays; /* just a buddy vairable to keep track of n arrays */
    } data_t;
    
  3. mallocfree 的必要调用,为堆上的指针变量分配和释放空间。

这是我想出的:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#define INITSIZE 3

typedef struct {
    const uint8_t *p_arr;
    uint8_t currsize;
    uint8_t max;
    uint8_t min;
} array_t;

typedef struct {
    array_t *A;
    uint8_t numarrays;
} data_t;

data_t *initialize_meta_data(void);
int check_arrays(const uint8_t arr[], const size_t n, uint8_t *key);
void set_arrays(data_t *data, const uint8_t arr[], const size_t n, int index);
void search_arrays(data_t *data, uint8_t key);
int cmpfunc(const void *a, const void *b);

int
main(void) {
    const uint8_t arr_1[] = {1, 4, 53 , 209};
    const uint8_t arr_2[] = {54, 56, 90, 100};
    const uint8_t arr_3[] = {6, 7, 8, 9, 10};

    const size_t size1 = sizeof(arr_1)/sizeof(*arr_1);
    const size_t size2 = sizeof(arr_2)/sizeof(*arr_2);
    const size_t size3 = sizeof(arr_3)/sizeof(*arr_3);

    data_t *data = initialize_meta_data();

    data->A = malloc(data->numarrays * sizeof(*(data->A)));

    set_arrays(data, arr_1, size1, 0);
    set_arrays(data, arr_2, size2, 1);
    set_arrays(data, arr_3, size3, 2);

    /* Number to search for */
    uint8_t key = 10;

    search_arrays(data, key);

    free(data->A);
    free(data);

    return 0;
}

void
search_arrays(data_t *data, uint8_t key) {
    int i;

    for (i = 0; i < data->numarrays; i++) {
        if ((key <= data->A[i].max) && (key >= data->A[i].min)) {
            if (check_arrays(data->A[i].p_arr, data->A[i].currsize, &key)) {
                printf("%d found in array number: %d\n", key, i+1);
            }
        } else {
            printf("array number: %d skipped\n", i+1);
        }
    }
}

void
set_arrays(data_t *data, const uint8_t arr[], const size_t n, int index) {
    data->A[index].p_arr = arr;
    data->A[index].currsize = n;
    data->A[index].max = data->A[index].p_arr[data->A[index].currsize-1];
    data->A[index].min = data->A[index].p_arr[0];
}

data_t
*initialize_meta_data(void) {
    data_t *data = malloc(sizeof(*data));
    data->A = NULL;
    data->numarrays = INITSIZE;
    return data;
}

int
check_arrays(const uint8_t arr[], const size_t n, uint8_t *key) {
    uint8_t *item;

    item = bsearch(key, arr, n, sizeof(*arr), cmpfunc);

    if (item != NULL) {
        return 1;
    }

    return 0;
}

int
cmpfunc(const void *a, const void *b) {
    return (*(uint8_t*)a > *(uint8_t*)b) - (*(uint8_t*)a < *(uint8_t*)b);
}

总体而言,此代码可以进行大量优化,您可能会为您的项目使用非常不同的方法,但想法会相似。

希望对你有所帮助:)

【讨论】:

    【解决方案3】:

    因为它是 uint8_t 你可以使用一个位数组。没有排序没有循环。 这段代码没有测试,但是思路可以看出来。

    uint arrEle(uint8_t val) { return val/32; }
    uint arrBit(uint8_t val) { return 1<<(val%32); }
    
    void setBit(uint32_t *arr, uint8_t val) {
        arr[arrEle(val)]|=arrBit(val);
    }
    uint testBit(uint32_t *arr, uint8_t val) {
        return (arr[arrEle(val)]&arrBit(val)) ? 1:0;
    }
    void clrBit(uint32_t *arr, uint8_t val) {
        arr[arrEle(val)]&=~arrBit(val);
    }
    
    // sorry not static initialize
    const uint32_t arr_all[4][8] = {0};
    const uint32_t arr_1[][8] = arr_all[0];
    const uint32_t arr_2[][8] = arr_all[1];
    
    setBit(arr_1, 1);
    setBit(arr_1, 4);
    setBit(arr_1, 53);
    setBit(arr_1, 209);
    

    现在你的测试很简单:

    for(i=0;i<sizeof(arr_all)/sizeof(*arr_all);i++) 
      if(testBit(arr_all[i], val)) return i;  // return the array number 0..3
    

    【讨论】:

    • 你说没有排序没有循环,但是你循环遍历所有元素进行查找?
    • 它是对 n 个数组的循环,而不是更多。
    • 谢谢,我明白了,我的错。
    【解决方案4】:
    #include <stdio.h>
    #include <stdint.h>
    
    const uint8_t arr_1[] = {1, 4, 53 , 209};
    const uint8_t arr_2[] = {54, 56, 90, 100};
    const uint8_t arr_3[] = {6, 7, 8, 9, 10};
    
    uint8_t lookup[256] = {0,};
    
    void add_one(const uint8_t *arr, unsigned count, unsigned num)
    {
    unsigned uu, val;
    
    for (uu=0; uu < count; uu++) {
            val = arr[uu];
            if (lookup[val]) fprintf(stderr
                    , "Add_one(num = %u): OMG!1111! value=%u also present in array_%u\n"
                    , num, val, (unsigned) lookup[val] );
            lookup[val] = num;
            }
    }
    
    void do_init(void)
    {
    add_one(arr_1, 4, 1);
    add_one(arr_2, 4, 2);
    add_one(arr_3, 6, 3);
    }
    
    int main(void)
    {
    unsigned idx,val;
    
    do_init();
    
    for (val = 0; val < 256; val++) {
            idx = lookup[val];
            if (!idx) continue;
            printf("Value %u is in arr_%u\n", val, idx);
            }
    
    return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-26
      • 1970-01-01
      • 2021-05-28
      • 1970-01-01
      相关资源
      最近更新 更多