数据结构中的堆的实现和堆排序

堆的实现

堆的本质是一二叉树,并且是一颗完全二叉树。

堆分为大堆和小堆,大堆就是在堆顶的元素的值是这个完全二叉树中的最大值,小堆刚好相反,是最小值。在树这个数据结构定义的时候是递归定义的所以也就满足,一个树中子数的更节点是最大或者最小(取决与建立大堆还是小堆)。

堆的建立

堆的建立过程是一个进行比较的过程,也就是说,每次插入一个元素,就需要对堆进行调整,怎么调整?当然在插入的时候,我们就已经确定需要建立大堆还是小堆,所以我们在插入的时候,每次都插入到堆的尾部,应为堆是一个完全二叉树,所以,我们插入到尾部后就需要和父节点的值进行比较,再进行调换,如果发现不用调换就说明插入完成。

堆的建立是基于一个顺序表的数据。

具体是的实现:

void Swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

// 调整堆
void AdjustUp(int arr[], size_t size, size_t index)
{
    if (arr == NULL)
    {
        return;
    }
    size_t child = index;
    size_t parent = (size - 1 - 1) / 2;
    while (child > 0)
    {
        if (arr[child] < arr[parent])
        {
            Swap(&arr[child], &arr[parent]);
        }
        // 因为是尾部插入然后上移,当上移到parent < child时候,
        // 就需要停止,不然会继续向上判断。
        else 
        {
            break;
        }
        child = parent; 
        parent = (child-1) / 2; 
    }
}

void CreateHeap(int arr[], size_t size)
{
    for (int i = 0; i < size; ++i)
    {
        // 在堆尾部插入,每次堆增加1
        AdjustUp(arr, i,i);
    }
}

取堆顶元素

我们建堆的目的一般都是为了排序或者取最大或者最小的数值,那么就需要取堆的顶部元素。我们来实现取堆顶元素

// 因为我们用到是c语言,所以我们用了输出型参数value
int HeapFindRoot(int arr[], int *value)
{
    if (arr == NULL || value == NULL)
    {
        // 取失败
        return 0;
    }
    *value = arr[0];
    return 1;
}

取完堆顶元素,那么就要删除,在堆排序中,正是删除堆顶元素,然后在对堆进行调整就会得到新的堆顶元素。
代码:

删除堆顶元素

// 下沉
void AdjustDown(HeapType arr[],size_t size, size_t index)
{
    if (arr == NULL)
    {
        return;
    }
    size_t lchild = index * 2 + 1;
    while (lchild < size)
    {
        // 如果是右孩子小于左孩子,那么把下标为右孩子赋给左孩子下标。
        if ((lchild + 1 < size) && cmp(arr[lchild + 1], 
        \arr[lchild]))
        {
            lchild = lchild + 1;
        }
        // 把孩子中小的一个与父节点值比较,如果父值大,
        //子小,那么交换。否则跳出循环。
        if (cmp(arr[lchild], arr[index]))
        {
            Swap(&arr[lchild], &arr[index]);
        }
        else
        {
            break;
        }
        // 更新index和孩子节点下标。
        index = lchild;
        lchild = index * 2 + 1;
    }
}

// 删除堆顶元素。
void HeapErase(int arr[],size_t size)
{
    if (arr == NULL)
    {
        return;
    }
    Swap(&arr[0], &arr[size-1]);
    --size;
    size_t index = 0;
    AdjustDown(arr, size, index);
}

以上就是堆数据结构都是用int型。我们在实际中也可以是一个结构体,也可以是进程,进程的优先调度算法就是用一个堆的数据结构来实现。

堆排序

堆排序是一种时间复杂度为O(nlogn)算法。空间复杂度为O(n)的算法。

下沉
void AdjustDown(HeapType arr[],size_t size, size_t index)
{
    if (arr == NULL)
    {
        return;
    }
    size_t lchild = index * 2 + 1;
    while (lchild < size)
    {
        // 如果是右孩子小于左孩子,那么把下标为右孩子赋给左孩子下标。
        if ((lchild + 1 < size) && cmp(arr[lchild + 1], 
        \arr[lchild]))
        {
            lchild = lchild + 1;
        }
        // 把孩子中小的一个与父节点值比较,如果父值大,
        //子小,那么交换。否则跳出循环。
        if (cmp(arr[lchild], arr[index]))
        {
            Swap(&arr[lchild], &arr[index]);
        }
        else
        {
            break;
        }
        // 更新index和孩子节点下标。
        index = lchild;
        lchild = index * 2 + 1;
    }
}

// 时间复杂度为O(N * LogN)
void HeapSort1(HeapType arr[], size_t size)
{
    if (arr == NULL || size <= 1)
    {
        return;
    }
    // 先减一是计算时是从0开始,第二个是child推导parent公式
    size_t begen = (size - 1 - 1)/2;
    // 这里采用下沉建树
    for (; begen > 0; --begen)
    {
        AdjustDown(arr, size, begen);
    }
    AdjustDown(arr, size, 0); // 这是处理0这种情况
    size_t heap_size = size;
    while (heap_size > 0)
    {
        Swap(&arr[0], &arr[heap_size - 1]);
        --heap_size;
        AdjustDown(arr, heap_size, 0);
    }
}

猜你喜欢

转载自blog.csdn.net/gangstudyit/article/details/80543395