C 从数组构建二进制堆,哪种方法更有效?为什么

C 从数组构建二进制堆,哪种方法更有效?为什么,c,performance,algorithm,binary-heap,C,Performance,Algorithm,Binary Heap,我正在学习堆,我发现了两种从给定阵列构建堆的方法: 我正试图建立一个最大的堆 1.自上而下的方法 这里我只是检查每个元素是否在正确的位置。使用名为restoreUp的方法,将每个键与其父键进行比较,如果父键小于父键,则向下移动。此过程将继续,直到父键大于。我从索引位置2开始检查每个键 我的代码是: void restoreUp(int arr[],int i) { int k=arr[i]; int par=i/2; while(arr[par]<k) {

我正在学习堆,我发现了两种从给定阵列构建堆的方法: 我正试图建立一个最大的堆

1.自上而下的方法

这里我只是检查每个元素是否在正确的位置。使用名为restoreUp的方法,将每个键与其父键进行比较,如果父键小于父键,则向下移动。此过程将继续,直到父键大于。我从索引位置2开始检查每个键

我的代码是:

void restoreUp(int arr[],int i)
{
    int k=arr[i];
    int par=i/2;
    while(arr[par]<k)
    {
        arr[i]=arr[par];
        i=par;
        par=i/2;
    }
    arr[i]=k;
}
void buildheap1(int arr[],int size)
{
    int i;
    for(i=2;i<=size;i++)
       restoreUp(arr,i);
} 
void restoreDown(int arr[],int i,int size)
{
    int left=2*i;
    int right=2*i+1;
    int num=arr[i];
    while(right<=size)
    {
        if(num>arr[left] && num>arr[right])
        {
            arr[i]=num;
            return;
        }
        else if(arr[left]>arr[right])
        {
            arr[i]=arr[left];
            i=left;
        }
        else
        {
            arr[i]=arr[right];
            i=right;
        }
        left=2*i;
        right=2*i+1;
    }
    if(left==size && arr[left]>num)
    {
        arr[i]=arr[left];
        i=left;
    }
    arr[i]=num;
}
void buildheap2(int arr[],int size)
{
    int i;
    for(i=size/2;i>=1;i--)
       restoreDown(arr,i,size);
}
void restoreUp(int-arr[],int-i)
{
int k=arr[i];
int par=i/2;
而(arr[par]arr[right])
{
arr[i]=arr[左];
i=左;
}
其他的
{
arr[i]=arr[右];
i=对;
}
左=2*i;
右=2*i+1;
}
if(left==大小&arr[left]>num)
{
arr[i]=arr[左];
i=左;
}
arr[i]=num;
}
void buildheap2(整数arr[],整数大小)
{
int i;
对于(i=size/2;i>=1;i--)
恢复向下(arr、i、尺寸);
}
这两种方法对我都有效。
我只是想知道哪种方法更有效,为什么?

一般来说,对于现代CPU(有缓存)。反向读取和数组通常是一个非常糟糕的主意,因为它会导致大量缓存未命中。当然,除非阵列已经在缓存中


因此,从这个角度来看,第一种方法似乎更好。

有关渐近复杂性,请参见heapify的“siftDown”版本。为了获得真实的性能,可以使用分析器。但是第一种方法是O(n logn),而第二种方法是O(logn)。第一种方法增加的算法复杂性将使第二种方法增加的缓存未命中率相形见绌。此外,尽管第一种方法确实正向迭代数组,但在检查父级时也会反向引用数组。同样,第二种方法向后读取数组,但在访问子级时也向前读取。我怀疑在这两种方法中,每次迭代的缓存未命中数都非常接近。