如果我使用散列对数组进行排序,在性能方面会有什么样的缺点?
What kind of drawbacks are there performance-wise , if I sort an array by using hashing?
我编写了一个简单的函数来使用散列对数组 int a[];
进行排序。
为此,我将每个元素的频率存储在新数组 hash1[]
中,然后在线性时间内放回原始数组。
#include<bits/stdc++.h>
using namespace std;
int hash1[10000];
void sah(int a[],int n)
{
int maxo=-1;
for(int i=0;i<n;i++)
{
hash1[a[i]]++;
if(maxo<a[i]){maxo=a[i];}
}
int i=0,freq=0,idx=0;
while(i<maxo+1)
{
freq=hash1[i];
if(freq>0)
{
while(freq>0)
{
a[idx++]=i;freq--;
}
}
i++;
}
}
int main()
{
int a[]={6,8,9,22,33,59,12,5,99,12,57,7};
int n=sizeof(a)/sizeof(a[0]);
sah(a,n);
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
}
该算法运行时间复杂度为 O(max_element)。仅考虑性能(时间和 space),我在这里面临什么样的缺点?
您可能考虑的问题:
- 输入验证。如果用户输入
-10
或一个非常大的值会怎样。
- 如果最大元素很大,当 L1 缓存耗尽时,您的性能有时会受到影响。
hash1
数组将与 a
数组竞争内存带宽。当我过去实现基数排序时,我发现每次迭代 8 位是最快的。
- 时间复杂度其实是O(max_element + number_of_elements)。例如。如果您对 200 万个 1 或 0 进行排序怎么办?它不如排序 2 个 1 或 0 快。
您实现的算法称为 counting sort。它的运行时间是 O(n + U),其中 n 是元素的总数,U 是数组中的最大值(假设数字从 0 到 U),它的 space 用法是 Θ(U ).您的特定实施假定 U = 10,000。尽管您将您的方法描述为 "hashing," 这实际上不是 散列 (计算元素的某些功能并使用它来将它们放入桶中)作为 distribution(根据元素的值分布元素)。
如果 U 是一个固定常量——就像你的情况一样——那么运行时间是 O(n) 并且 space 用法是 O(1),但请记住 big-O 谈论长-term 增长率,如果 U 很大,运行时间可能会非常高。如果您要对具有有限范围值的非常大的数组进行排序,这将使其具有吸引力。但是,如果值的范围可以很大,这不是一个特别好的方法。有趣的是,您可以将基数排序视为一种重复运行计数排序的算法,其中 U = 10(如果使用数字的 10 进制数字)或 U = 2(如果使用二进制)并且运行时间为 O(n log U),这对于大的 U 值是非常可取的。
您可以通过多种方式清理此代码。例如,您有一个 if
语句和一个具有相同条件的 while
循环,它们可以组合在一起成为一个 while
循环。您可能还想进行一些断言检查,以确保所有值都在 0 到 9,999 的范围内(含 0 到 9,999),否则会出现边界错误。此外,您可以考虑将全局数组设为局部变量(尽管注意您的堆栈使用情况)或 static
局部变量(以避免污染全局名称 space)。您也可以让用户传入指定最大大小的参数,或者可以自己计算。
我编写了一个简单的函数来使用散列对数组 int a[];
进行排序。
为此,我将每个元素的频率存储在新数组 hash1[]
中,然后在线性时间内放回原始数组。
#include<bits/stdc++.h>
using namespace std;
int hash1[10000];
void sah(int a[],int n)
{
int maxo=-1;
for(int i=0;i<n;i++)
{
hash1[a[i]]++;
if(maxo<a[i]){maxo=a[i];}
}
int i=0,freq=0,idx=0;
while(i<maxo+1)
{
freq=hash1[i];
if(freq>0)
{
while(freq>0)
{
a[idx++]=i;freq--;
}
}
i++;
}
}
int main()
{
int a[]={6,8,9,22,33,59,12,5,99,12,57,7};
int n=sizeof(a)/sizeof(a[0]);
sah(a,n);
for(int i=0;i<n;i++)
{
printf("%d ",a[i]);
}
}
该算法运行时间复杂度为 O(max_element)。仅考虑性能(时间和 space),我在这里面临什么样的缺点?
您可能考虑的问题:
- 输入验证。如果用户输入
-10
或一个非常大的值会怎样。 - 如果最大元素很大,当 L1 缓存耗尽时,您的性能有时会受到影响。
hash1
数组将与a
数组竞争内存带宽。当我过去实现基数排序时,我发现每次迭代 8 位是最快的。 - 时间复杂度其实是O(max_element + number_of_elements)。例如。如果您对 200 万个 1 或 0 进行排序怎么办?它不如排序 2 个 1 或 0 快。
您实现的算法称为 counting sort。它的运行时间是 O(n + U),其中 n 是元素的总数,U 是数组中的最大值(假设数字从 0 到 U),它的 space 用法是 Θ(U ).您的特定实施假定 U = 10,000。尽管您将您的方法描述为 "hashing," 这实际上不是 散列 (计算元素的某些功能并使用它来将它们放入桶中)作为 distribution(根据元素的值分布元素)。
如果 U 是一个固定常量——就像你的情况一样——那么运行时间是 O(n) 并且 space 用法是 O(1),但请记住 big-O 谈论长-term 增长率,如果 U 很大,运行时间可能会非常高。如果您要对具有有限范围值的非常大的数组进行排序,这将使其具有吸引力。但是,如果值的范围可以很大,这不是一个特别好的方法。有趣的是,您可以将基数排序视为一种重复运行计数排序的算法,其中 U = 10(如果使用数字的 10 进制数字)或 U = 2(如果使用二进制)并且运行时间为 O(n log U),这对于大的 U 值是非常可取的。
您可以通过多种方式清理此代码。例如,您有一个 if
语句和一个具有相同条件的 while
循环,它们可以组合在一起成为一个 while
循环。您可能还想进行一些断言检查,以确保所有值都在 0 到 9,999 的范围内(含 0 到 9,999),否则会出现边界错误。此外,您可以考虑将全局数组设为局部变量(尽管注意您的堆栈使用情况)或 static
局部变量(以避免污染全局名称 space)。您也可以让用户传入指定最大大小的参数,或者可以自己计算。