C语言刷 堆(优先队列)

堆(优先队列):

讲的比较好的视频:https://www.bilibili.com/video/BV1AF411G7cA/?spm_id_from=333.1007.top_right_bar_window_history.content.click

定义

  堆必须是一个完全二叉树,为啥呢?因为:

  堆分为大顶堆(每个父节点都大于子节点)和小顶堆

堆的存储:

  联想层序遍历给堆中的节点进行编号:可以用一维数组存储堆的节点

节点下标规律:

  父节点下标i        左子节点i

  左子节点下标2i + 1    右子节点i + 1 

  右子节点下标2i + 2       父节点 (i - 1) / 2

基本操作

下沉 和 上浮:

建堆

堆排序

待做的两道题:

295. 数据流的中位数

480. 滑动窗口中位数

 

 

703. 数据流中的第 K 大元素

/* 小根堆 */
typedef struct {
    int heapCapacity;
    int heapSize;
    int *heap;
} KthLargest;

/* 堆顶下标: 0; parent: (k-1)/2; leftChild: 2*k + 1; rightChild: 2*k + 2 */
int ParentIndex(int i)
{
    return (i - 1) / 2;
}

int LeftChildIndex(int i)
{
    return 2 * i + 1;
}

int RightChildIndex(int i)
{
    return 2 * i + 2;
}

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

/************************************ 堆的有序性的维护 ************************************/
/*
* 上浮:
*     不符合规则的点(这里是小根堆,规则即父节点最小),与父节点交换(直至符合为止)        
*/
void Swim(int *heap, int i)
{
    while (i > 0 && heap[ParentIndex(i)] > heap[i]) {
        Swap(&heap[ParentIndex(i)], &heap[i]);
        i = ParentIndex(i); // 已经上浮一次,更新下次可能上浮的索引
    }
}

/*
* 下沉:
*     不符合规则的点(这里是小根堆,规则即父节点最小),与子节点中较小的(因为是小根堆)交换(直至符合为止)        
*/
void Sink(int *heap, int heapSize, int i)
{
    while (LeftChildIndex(i) < heapSize) {
        int smallOneIndex = LeftChildIndex(i);
        int leftVal = heap[LeftChildIndex(i)];
        if (RightChildIndex(i) < heapSize) {
            int rightVal = heap[RightChildIndex(i)];
            // 比较子节点中哪个更小
            smallOneIndex = leftVal < rightVal ? smallOneIndex : RightChildIndex(i);
        }
    
        if (heap[i] < heap[smallOneIndex]) {
            break;
        }

        Swap(&heap[i], &heap[smallOneIndex]);
        i = smallOneIndex; // 已经下沉一次,更新下次可能上浮的索引
    }
}

/*
* 出队:
*     1.最后一个点换到根(根即待出队的点)
*     2.下沉
*/
void Pop(int *heap, int *heapSize)
{
    Swap(&heap[0], &heap[*heapSize - 1]);
    // heap[*heapSize - 1] = 0;
    (*heapSize)--;
    Sink(heap, *heapSize, 0);
}

/*
* 入队:
*     1.待插入的点放在最后
*     2.上浮
*/
void Push(int *heap, int *heapSize, int val)
{
    heap[(*heapSize)++] = val;
    Swim(heap, *heapSize - 1);
}

/************************************ 答题 ************************************/
int kthLargestAdd(KthLargest* obj, int val) 
{
    if (obj->heapCapacity > obj->heapSize) {
        Push(obj->heap, &obj->heapSize, val);
    } else if (val > obj->heap[0]) {
        // 队列已经满了,并且头节点小于待插入的值
        Pop(obj->heap, &obj->heapSize);
        Push(obj->heap, &obj->heapSize, val);
    }

    // 小根堆,每次返回头节点
    return obj->heap[0];
}

KthLargest* kthLargestCreate(int k, int* nums, int numsSize) 
{
    if (k < 1) {
        return NULL;
    }

    KthLargest *obj = (KthLargest *)malloc(sizeof(KthLargest));
    obj->heapCapacity = k;
    obj->heapSize = 0;
    obj->heap = (int *)malloc(sizeof(int) * k);
    memset(obj->heap, 0, sizeof(int) * k);

    for (int i = 0; i < numsSize; i++) {
        if (obj->heapCapacity > obj->heapSize) {
            Push(obj->heap, &obj->heapSize, nums[i]);
        } else {
            // 堆已经满了,调用add接口
            int ret = kthLargestAdd(obj, nums[i]);
        }
    }

    return obj;
}

void kthLargestFree(KthLargest* obj) 
{
    if (obj != NULL) {
        free(obj->heap);
        free(obj);
    }

}

/**
 * Your KthLargest struct will be instantiated and called as such:
 * KthLargest* obj = kthLargestCreate(k, nums, numsSize);
 * int param_1 = kthLargestAdd(obj, val);
 
 * kthLargestFree(obj);
*/

 

347. 前 K 个高频元素

 

解法1:直接用UT_HASH中的排序,返回前k个元素

struct hashTable {
    int key;    // 数组元素
    int value;  // 数组元素出现的频率
    UT_hash_handle hh;
};

struct hashTable *g_hash;

void AddNode(int num)
{
  struct hashTable *tmp = NULL;
  HASH_FIND_INT(g_hash, &num, tmp);
  if (tmp == NULL) {
      tmp = (struct hashTable *)malloc(sizeof(struct hashTable));
      tmp->key = num;
      tmp->value = 1;
      HASH_ADD_INT(g_hash, key, tmp);
  } else {
      (tmp->value)++;
  }
}

/* 排序:逆序 */
int HashCmp(struct hashTable *a, struct hashTable *b)
{
    return b->value - a->value;
}

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
int* topKFrequent(int* nums, int numsSize, int k, int* returnSize)
{
    g_hash = NULL;
    for (int i = 0; i < numsSize; i++) {
        // 插入到hash表中
        AddNode(nums[i]);
    }

    // 根据数组元素出现的频次,对hash表进行降序
    HASH_SORT(g_hash, HashCmp);

    int *res = (int *)malloc(sizeof(int) * k);
    *returnSize = k;
    int cnt = 0;
    // 对hash表进行遍历
    struct hashTable *cur, *tmp;
    HASH_ITER(hh, g_hash, cur, tmp) {
        if (cnt == k) {
            break;
        }
        res[cnt++] = cur->key;
    }
    return res;
}

 

解法2:利用最大堆(并不适合做这题)

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

typedef struct {
    int num;
    int count;
    UT_hash_handle hh;
} HashTable;

HashTable *g_hash;

HashTable* FindNode(int key)
{
    HashTable *tmp = NULL;
    HASH_FIND_INT(g_hash, &key, tmp);
    return tmp;
}

void AddNode(int key)
{
    HashTable *tmp = FindNode(key);
    if (tmp == NULL) {
        tmp = malloc(sizeof(HashTable));
        tmp->num = key;
        tmp->count = 1;
        HASH_ADD_INT(g_hash, num, tmp);
    } else {
        tmp->count++;
    }
}

typedef struct {
    int num;
    int count;
} Pair;

int g_cur = 0;
Pair *g_heap;

int ParentId(int i)
{
    return (i - 1) / 2;
}

int LeftChildId(int i)
{
    return 2 * i + 1;
}

int RightChildId(int i)
{
    return 2 * i + 2;
}

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

/*大顶堆*/
void Push(int num, int count)
{
    // push到队尾
    g_heap[g_cur].num = num;
    g_heap[g_cur].count = count;
    int i = g_cur;
    g_cur++;
    // 判断是否上浮
    while (ParentId(i) >= 0 && g_heap[ParentId(i)].count < g_heap[i].count) {
        Swap(&g_heap[ParentId(i)], &g_heap[i]);
        i = ParentId(i);
    }
}

/*大顶堆*/
void Pop(int cap)
{
    // 交换队头和队尾
    Swap(&g_heap[0], &g_heap[g_cur - 1]);
    // 弹出队尾
    g_cur--;
    // 判断队头是否下沉
    int i = 0;
    int minId = LeftChildId(i);
    while (minId < cap && g_heap[minId].count < g_heap[i].count) {
        if (RightChildId(i) < cap) {
            minId = g_heap[LeftChildId(i)].count < g_heap[RightChildId(i)].count ? LeftChildId(i) : RightChildId(i);
        }
        Swap(&g_heap[i], &g_heap[minId]);
        i = minId;
    }
}

int* topKFrequent(int* nums, int numsSize, int k, int* returnSize)
{
    g_hash = NULL;
    for (int i = 0; i < numsSize; i++) {
        AddNode(nums[i]); // num -> 出现频次,存在hash中
    }

    g_heap = malloc(sizeof(Pair) * (k + 1));

    HashTable *cur, *next;
    HASH_ITER(hh, g_hash, cur, next) {
        if (g_cur < k) {
            // 堆没满
            Push(cur->num, cur->count); // g_cur++
        } else {
            // 堆满了
            if (cur->count > g_heap[g_cur - 1].count) {
                g_cur--;
                Push(cur->num, cur->count);
            } else {
                continue;
            }
        }
        HASH_DEL(g_hash, cur);
        free(cur);
    }

    *returnSize = 0;
    int *res = malloc(sizeof(int) * k);
    for (int i = 0; i < k; i++) {
        res[(*returnSize)++] = g_heap[0].num;
        Pop(k);
    }
    free(g_heap);
    g_cur = 0;
    return res;
}

 

posted @ 2022-01-11 11:01  胖白白  阅读(121)  评论(0编辑  收藏  举报