【问题标题】:Faster way to count smaller or equal element in an very big array?在非常大的数组中计算较小或相等元素的更快方法?
【发布时间】:2019-06-05 00:59:14
【问题描述】:

我需要降低代码的时间复杂度,到目前为止,我尝试对其进行预排序并使用二进制搜索来查找所需元素的索引,并使用索引来查找键号之前的多少个元素,但我可以' t 通过,因为我的代码很慢。有没有其他方法可以让这段代码更快?

重要细节:

  • 我需要使用一个大数组和多个查询(数组保持不变)运行这段代码,最多 100k。
  • 每个元素的值最多为 10 亿。
  • 这个问题的时间限制是0.1s

例子:

假设我有一个包含 15 个元素的数组,我有 5 个查询要做。

数组:1002 19 3 8 22 123 14 5234 123 657 41 829 34 2314 15

查询:100 1000 78 3 1

输出:8 12 8 1 0

我尝试对它进行预排序并使用二进制搜索,但仍然无法通过 0,1s 的时间限制。

#include<stdio.h>

void swap(int *a,int *b){
    int temp=*a;
    *a=*b;
    *b=temp;
}

int partition(int a[],int p,int r){
    int temp, i,x;
    x=a[r];
    i=p-1;
    for(int j=p;j<=r;j++){
        if(a[j]<x){
            i++;
            swap(&a[i],&a[j]);
        }
    }
    i++;
    swap(&a[i],&a[r]);;
    return i;
}

void qsort(int a[],int p,int r){
    int q;
    if(p<r){
        q=partition(a,p,r);
        qsort(a,p,q-1);
        qsort(a,q+1,r);
    }
}

int binarySearch(int arr[], int n, int key){ 
    int left = 0, right = n; 
    int mid; 
    while (left < right){ 
        mid = left + (right-left)/2; 
        if (arr[mid] == key){ 
            while (arr[mid+1] == key && mid+1<n) 
                 mid++; 
            break; 
        }else if (arr[mid] > key) 
            right = mid; 
        else
            left = mid + 1; 
    } 
    while (arr[mid] > key) 
        mid--; 
    return mid + 1; 
} 

int main(){
    int a, b;
    scanf("%d %d",&a, &b);
    int data[a],d;
    for(int i=0;i<a;i++){
        scanf("%d", &data[i]);
    }   
    qsort(c, 0, a-1);
    for(int i=0;i<b;i++){
        scanf("%d", &d);
        printf("%d\n", binarySearch(c, a, d));
    }
    return 0;
}

【问题讨论】:

  • 您可以使用 stdlib 中已经定义的 qsort,如果您可以使用 c++,您可以使用 sort 和 lower_bound/upper_bound 函数来替换 qsort 和二分查找。
  • 这里有一个错误:while (arr[mid+1] == key &amp;&amp; mid+1&lt;n)。您需要确保索引检查发生在项目检查之前。如果您在进行边界检查之前访问arr[mid+1],您可能会遇到访问冲突。
  • 看起来也像 with (arr[mid] &gt; key) 中的一个错误。如果key 小于数组中的任何项目,这看起来mid 可能会变为负数。

标签: arrays algorithm search


【解决方案1】:

一般来说,您的方法是正确的,应该提供平均时间 NlogN + MlogN 其中 N 是数组大小,M 是查询数。

但是qsort 的实现还不够好 - 它总是选择正确的元素作为枢轴,并且对于某些数组(已经排序或包含重复元素)给出二次排序时间!

如果您不能使用标准库中的排序例程,只需更改您的实现 - 在随机索引处获取枢轴或使用“median of three”方法

附:还可以考虑用 Hoare 的方法替换使用过的 Lomuto 的分区方法(它不是那么简单但更快)

【讨论】:

    【解决方案2】:

    首先,您能否告诉我们,对于给定的迭代列表,您选择的固定 100k 元素数组的当前执行时间是多少?这将让我们了解您离极限还有多远。

    其次,执行时间不仅取决于复杂度,还取决于每个循环的执行时间。这就是为什么您可以拥有两种具有不同复杂度的算法,但具有较高复杂度的一种算法比另一种运行得更快,因为它运行在足够少的元素上。 说,在您的情况下,复杂性肯定会起作用。然而,每个循环的运行时间很重要,这取决于实现。因此,我可以提出以下建议:

    • 提高编译器的优化级别(-O 选项)
    • 更改一些代码。例如mid = left + (right-left)/2; 可以简化为mid = (left + right) &gt;&gt; 1;。这将已经提高您的代码速度。 (我已编辑这部分以将右移更改为 1)
    • 更改您的循环检查。将它们转换为针对 0 进行测试,您的代码会快很多。
    • 此外,每次测试都会花费您的时间,因此最好是阅读和写作,而不是测试您是否必须阅读或写作。

    注意:

    您或许可以尝试另一种方法,根据每个数字将数字组织成一棵树。例如 12 123 1237 15 24 26 1256 将构建一棵树,就像最后一个数字将被标记以指示它是叶子(以数字结尾)。当然,这棵树也可以从二进制值构建

    1 - 2 - 3 - 7 - 5 - 6 - 5 2 - 4 - 6

    之后,只需沿着树遍历并比较每个数字的长度和值。

    只是一个想法!

    【讨论】:

      猜你喜欢
      • 2018-06-26
      • 2023-03-09
      • 1970-01-01
      • 1970-01-01
      • 2016-12-20
      • 2020-08-12
      • 2021-05-26
      • 1970-01-01
      • 2019-11-04
      相关资源
      最近更新 更多