堆排序-大小顶堆

leetcode 215. 数组中的第K个最大元素

大顶堆算法

  • 步骤1:将数组中所有的数据按照大顶堆排序
  • 步骤2:然后将堆顶元素删除,将堆尾数据放到堆顶,然后调整堆满足大顶堆的属性(调整的时候使用递归的方式,减少代码量)
  • 重复步骤2 k - 1次,堆顶数据就是想要的结果

时间复杂度分析

  • 在建堆的开始,针对n个节点,每一个节点的时间复杂度为log(k), k为二叉树的层数,总的就是nlog(k)
  • 后面删除k-1次,时间消耗为 (k-1)log(k)
  • 所以整个运行的时间复杂度为O(n)

void Swap(int *arr, int i, int j)
{
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}

void AdjustHeap(int *arr, int i, int arrSize)
{
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int largestIndex = i;

    /*********************大顶堆关键代码***************************/
    if (left < arrSize && arr[left] > arr[i]) {
        largestIndex = left;
    }
    if (right < arrSize && arr[right] > arr[largestIndex]) {
        largestIndex = right;
    }
    /**********************大顶堆关键代码**************************/
    if (i != largestIndex) {
        Swap(arr, i, largestIndex);
        AdjustHeap(arr, largestIndex, arrSize);
    }
}

int findKthLargest(int* nums, int numsSize, int k){

    // 使用堆排序(最大堆),堆顶就是最大的数,然后将堆顶的数删除,将堆尾的数放到堆顶,进行堆调整
    // 删除k - 1次后,堆顶数据就是结果

    // 首先构造大顶堆
    for (int i = numsSize / 2 - 1; i >= 0; i--) {
        AdjustHeap(nums, i, numsSize);
    }
    // 删除k - 1次堆顶元素(其实是将堆末尾和堆顶交换)
    int arrSize = numsSize;
    for (int i = 0; i < k - 1; i++) {
        Swap(nums, 0, numsSize - 1 - i);
        arrSize--;
        AdjustHeap(nums, 0, arrSize);
    }
    return nums[0];
}

小顶堆算法

  • 使用小顶堆算法。则要保持堆的个数为k个,将前k个大的数据保存到堆里。堆顶保存较小的,使用小顶堆就可以获取到第k大的数
void Swap(int *arr, int i, int j)
{
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
void SiftUp(int *arr, int i)
{
    if (i == 0) return;
    int parent = (i + 1) / 2 - 1;
    if (i != parent) {
        if (arr[i] < arr[parent]) {
            Swap(arr, i, parent);
            SiftUp(arr, parent);
        }
    }
}
void AdjustHeap(int *arr, int i, int arrSize)
{
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int min = i;
    if (left < arrSize && arr[left] < arr[i]) {
        min = left;
    }
    if (right < arrSize && arr[right] < arr[min]) {
        min = right;
    }
    if (i != min) {
        Swap(arr, i, min);
        AdjustHeap(arr, min, arrSize);
    }
}

int findKthLargest(int* arr, int numsSize, int k)
{
    // 首先将数组总的前k个数按照最小堆排序
    for (int i = 0; i < k; i++) {
        SiftUp(arr, i);
    }
    // 然后将剩下的元素,添加到最小堆里
    // 要求堆的个数保持k个不变,如果新的数值比堆顶还小,则直接跳过,反正,将堆顶替换之,然后调整堆的状态。
    for (int i = k; i < numsSize; i++) {
        if (arr[0] >= arr[i]) {
            continue;
        }
        arr[0] = arr[i];
        AdjustHeap(arr, 0, k);
    }
    return arr[0];
}

有必要总结一个大小顶堆的通用模版

void Swap(int *arr, int i, int j)
{
    int tmp = arr[i];
    arr[i] = arr[j];
    arr[j] = tmp;
}
typedef int (MyCmp)(const void *a, const void *b);
// 小顶堆
int HeapCmp(const void *a, const void *b)
{
    int left = *(int *)a;
    int right = *(int *)b;
    return left < right ? -1 : 1;
}
  • 上浮函数,入参cmp函数
void SiftUp(int *arr, int i, MyCmp cmp)
{
    if (i == 0) return;
    int parent = (i + 1) / 2 - 1;
    if (i != parent) {
        if (cmp(&arr[parent], &arr[i]) > 0) { //  if (arr[parent] > arr[i]) {
            Swap(arr, i, parent);
            SiftUp(arr, parent, cmp);
        }
    }
}
  • 重新调整堆属性,函数,入参cmp函数
void AdjustHeap(int *arr, int i, int arrSize, MyCmp cmp)
{
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    int min = i;
    // if (left < arrSize && arr[i] > arr[left]) {
    if (left < arrSize && cmp(&arr[i], &arr[left]) > 0) {
        min = left;
    }
    if (right < arrSize && cmp(&arr[min], &arr[right]) > 0) {
        min = right;
    }
    if (i != min) {
        Swap(arr, i, min);
        AdjustHeap(arr, min, arrSize, cmp);
    }
}
  • 主函数
int findKthLargest(int* arr, int numsSize, int k)
{

    // 首先将数组总的前k个数按照最小堆排序
    for (int i = 0; i < k; i++) {
        SiftUp(arr, i, HeapCmp);
    }

    // 然后将剩下的元素,添加到最小堆里
    // 要求堆的个数保持k个不变,如果新的数值比堆顶还小,则直接跳过,反正,将堆顶替换之,然后调整堆的状态。
    for (int i = k; i < numsSize; i++) {
        if (arr[0] >= arr[i]) {
            continue;
        }
        arr[0] = arr[i];
        AdjustHeap(arr, 0, k, HeapCmp);
    }
    return arr[0];
}
posted @ 2021-10-06 22:58  匠人小魏  阅读(157)  评论(0)    收藏  举报