【数据结构】堆

1. Makefile

.PHONY: clean

CC = gcc
CFLAGS = -g -Wall -DDEBUG -std=c99
#EXTRA_FLAGS = -DUSE_RECURSION

# match *.c
SOURCE = $(wildcard ./*.c)
# replace * with *.c
BIN = $(patsubst %.c, %, $(notdir $(SOURCE)))

$(BIN): $(SOURCE)
	$(CC) $(CFLAGS) $(EXTRA_FLAGS) $(SOURCE) -o $@

clean:
	rm -rf $(BIN)

2. heap.h

/*
* File Name: heap.h
*
* Copyright (c) 2022, c code from sustzc.
* All rights reserved.
*/

#ifndef __HEAP_H__
#define __HEAP_H__

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdbool.h>

#ifdef __cplusplus
extern "C" {
#endif

#define MAX_HEAP_SIZE (20)

typedef int heap_data_t;
typedef struct {
    heap_data_t data[MAX_HEAP_SIZE];
    int size;
} heap_t;

int get_heap_size(const heap_t *heap);

int is_heap_empty(const heap_t *heap);

int is_heap_full(const heap_t *heap);

bool init_heap(heap_t *heap, heap_data_t data[], int size);

void print_heap_info(const heap_t *heap);

void print_array_info(const heap_data_t data[], int size);

#ifndef USE_RECURSION
void adjust_down_heap(heap_data_t data[], int size, int root);

void adjust_up_heap(heap_t *heap, int child);
#else
void adjust_down_heap_recursion(heap_data_t data[], int size, int root);

void adjust_up_heap_recursion(heap_t *heap, int child);
#endif /// USE_RECURSION

void build_heap(heap_t *heap);

void heap_push(heap_t *heap, heap_data_t data);

void heap_pop(heap_t *heap);

heap_data_t get_heap_top(const heap_t *heap);

heap_data_t *find_heap_top_minimum_k_data(heap_data_t data[], int size, int k);

heap_data_t *find_heap_top_maximum_k_data(heap_data_t data[], int size, int k);

void heap_sort(heap_data_t data[], int size);

void destroy_heap(heap_t *heap);

#ifdef __cplusplus
}
#endif

#endif // __HEAP_H__

3. heap.c

/*
* File Name: heap.c
*
* Copyright (c) 2022, c code from sustzc.
* All rights reserved.
*/

#include "heap.h"

#ifdef USE_RECURSION
// false 表示 小根堆
static bool s_is_big_root_heap = false;
#else
// true 表示 大根堆
static bool s_is_big_root_heap = true;
#endif /// USE_RECURSION

int get_heap_size(const heap_t *heap)
{
    return NULL == heap ? 0 : heap->size;
}

int is_heap_empty(const heap_t *heap)
{
    return 0 == get_heap_size(heap);
}

int is_heap_full(const heap_t *heap)
{
    return get_heap_size(heap) > MAX_HEAP_SIZE;
}

bool init_heap(heap_t *heap, heap_data_t data[], int size)
{
    if (NULL == heap || size > MAX_HEAP_SIZE) {
        printf("in %s, size: %d\n", __func__, size);
        return false;
    }

    memset(heap->data, 0x00, MAX_HEAP_SIZE * sizeof(heap_data_t));
    heap->size = size;
    memcpy(heap->data, data, size * sizeof(heap_data_t));

    return true;
}

static void swap_data(heap_data_t *a, heap_data_t *b)
{
    heap_data_t tmp = *a;
    *a = *b;
    *b = tmp;
}

static void print_info(const heap_data_t data[], int size)
{
    int i = 0;
    for (; i < size; ++i) {
        printf("[%-2d] ", data[i]);
    }

    printf("\n");
}

void print_heap_info(const heap_t *heap)
{
    printf("%s\n\t", __func__);

    if (NULL == heap) {
        return;
    }

    int size = get_heap_size(heap);
    print_info(heap->data, size);
}

void print_array_info(const heap_data_t data[], int size)
{
    printf("%s\n\t", __func__);
    print_info(data, size);
}

static int get_child_index(const heap_data_t data[], int size, int root)
{
    int child = root;
    int left = root * 2 + 1;
    int right = root * 2 + 2;

    if (s_is_big_root_heap) {
        if ((left < size) && (data[left] > data[child])) {
            child = left;
        }

        if ((right < size) && (data[right] > data[child])) {
            child = right;
        }
    } else {
        if ((left < size) && (data[left] < data[child])) {
            child = left;
        }

        if ((right < size) && (data[right] < data[child])) {
            child = right;
        }
    }

    return child;
}

#ifdef USE_RECURSION
void adjust_down_heap_recursion(heap_data_t data[], int size, int root)
{
    printf("in %s\n", __func__);

    int child = get_child_index(data, size, root);
    if (child != root) {
        swap_data(data + root, data + child);
        adjust_down_heap_recursion(data, size, child);
    }
}
#else
void adjust_down_heap(heap_data_t data[], int size, int root)
{
    printf("in %s\n", __func__);

    int parent = root;
    while (parent < size) {
        int child = get_child_index(data, size, parent);
        if (child != parent) {
            swap_data(data + child, data + parent);
            parent = child;
        } else {
            // 已经满足了堆的性质
            break;
        }
    }
}
#endif /// USE_RECURSION

static void heapify(heap_data_t data[], int size)
{
    // 最后一个结点的索引是 size - 1,
    // 那么最后一个结点的父亲结点的索引就是 ((size - 1) - 1) / 2
    int i = (size - 1 - 1) / 2;

    printf("Build %s root heap\n", s_is_big_root_heap ? "big" : "little");

    for (; i >= 0; --i) {
        if (i >= size) {
            printf("in %s, invaild parameter, size: %d, i: %d\n", __func__, size, i);
            continue;
        }

#ifdef USE_RECURSION
        adjust_down_heap_recursion(data, size, i);
#else
        adjust_down_heap(data, size, i);
#endif /// USE_RECURSION
    }
}

void build_heap(heap_t *heap)
{
    if (NULL == heap) {
        return;
    }

    heapify(heap->data, get_heap_size(heap));
}

#ifdef USE_RECURSION
void adjust_up_heap_recursion(heap_t *heap, int child)
{
    int parent = (child - 1) / 2;

    if (s_is_big_root_heap) {
        // 满足大根堆
        if (heap->data[child] < heap->data[parent]) {
            return;
        }
    } else {
        // 满足小根堆
        if (heap->data[child] > heap->data[parent]) {
            return;
        }
    }

    swap_data(heap->data + child, heap->data + parent);

    // 已经调整到根结点
    if (0 == parent) {
        return;
    } else {
        // 交换 父子节点后, 继续从新的父节点开始向上调整
        adjust_up_heap_recursion(heap, parent);
    }
}
#else
void adjust_up_heap(heap_t *heap, int child)
{
    int parent = 0;

    while (child > 0) {
        parent = (child - 1) / 2;

        if (s_is_big_root_heap) {
            // 满足大根堆
            if (heap->data[child] < heap->data[parent]) {
                break;
            }
        } else {
            // 满足小根堆
            if (heap->data[child] > heap->data[parent]) {
                break;
            }
        }

        swap_data(heap->data + child, heap->data + parent);
        child = parent;
    }
}
#endif /// USE_RECURSION

void heap_push(heap_t *heap, heap_data_t data)
{
    if (NULL == heap || is_heap_full(heap)) {
        printf("heap is NULL or heap full\n");
        return;
    }

    // 尾插
    heap->data[heap->size++] = data;

#ifdef USE_RECURSION
    adjust_up_heap_recursion(heap, heap->size - 1);
#else
    adjust_up_heap(heap, heap->size - 1);
#endif /// USE_RECURSION
}

void heap_pop(heap_t *heap)
{
    if (NULL == heap || is_heap_empty(heap)) {
        printf("heap is NULL or heap empty\n");
        return;
    }

    int size = get_heap_size(heap);
    // 用数组中的最后一个元素去替换首元素,
    // 相当于减少一个元素, 再向下调整
    heap->data[0] = heap->data[size - 1];

#ifdef USE_RECURSION
    adjust_down_heap_recursion(heap->data, size, 0);
#else
    adjust_down_heap(heap->data, size, 0);
#endif /// USE_RECURSION
}

void heap_sort(heap_data_t data[], int size)
{
    heapify(data, size);
    int i = size - 1;

    for (; i > 0; --i) {
        swap_data(data + i, data);

        /* 从堆顶开始向下调整 堆中的剩余元素 i */
#ifdef USE_RECURSION
        adjust_down_heap_recursion(data, i, 0);
#else
        adjust_down_heap(data, i, 0);
#endif /// USE_RECURSION
    }
}

heap_data_t get_heap_top(const heap_t *heap)
{
    return is_heap_empty(heap) ? INT_MIN : heap->data[0];
}

static heap_data_t *find_heap_top_k_data(heap_data_t data[], int size, int k)
{
    int i = 0;
    heap_data_t *top_k_data = NULL;

    do {
        top_k_data = (heap_data_t *)calloc(1, sizeof(heap_data_t) * k);
        if (NULL == top_k_data) {
            perror("calloc top_k_data fail");
            break;
        }

        memcpy(top_k_data, data, sizeof(heap_data_t) * k);

        heapify(top_k_data, k);

        for (i = k; i < size; ++i) {
            if (s_is_big_root_heap) {
                // 由于构造的是大堆,因此查找的原始数组中的数据要比 topK 堆中最大的数据小
                if (data[i] < top_k_data[0]) {
                    top_k_data[0] = data[i];
                }

#ifdef USE_RECURSION
                adjust_down_heap_recursion(top_k_data, k, 0);
#else
                adjust_down_heap(top_k_data, k, 0);
#endif /// USE_RECURSION
            } else {
                // 由于构造的是小堆,因此查找的原始数组中的数据要比 topK 堆中最小的数据大
                if (data[i] > top_k_data[0]) {
                    top_k_data[0] = data[i];
                }

#ifdef USE_RECURSION
                adjust_down_heap_recursion(top_k_data, k, 0);
#else
                adjust_down_heap(top_k_data, k, 0);
#endif /// USE_RECURSION
            }
        }
    } while (0);

    return top_k_data;
}

heap_data_t *find_heap_top_minimum_k_data(heap_data_t data[], int size, int k)
{
    // 构建 大根堆
    s_is_big_root_heap = true;
    heap_data_t *top_k_minimum_data = find_heap_top_k_data(data, size, k);

    return top_k_minimum_data;
}

heap_data_t *find_heap_top_maximum_k_data(heap_data_t data[], int size, int k)
{
    // 构建 小根堆
    s_is_big_root_heap = false;
    heap_data_t *top_k_maximum_data = find_heap_top_k_data(data, size, k);

    return top_k_maximum_data;
}

void destroy_heap(heap_t *heap)
{
    heap->size = 0;
}

4. test_heap.c

/*
* File Name: test_heap.c
*
* Copyright (c) 2022, c code from sustzc.
* All rights reserved.
*/

#include "heap.h"

void test()
{
    heap_t heap;
    int i = 0, data_size = 0, top_k = 5;
    int *found_data = NULL;

    heap_data_t data[] = {
        11, 12, 2, 9, 8, 10, 1
    };
    data_size = sizeof(data) / sizeof(heap_data_t);
    print_array_info(data, data_size);

    printf("init_heap\n");
    if (!init_heap(&heap, data, data_size)) {
        printf("init_heap failed\n");
        return;
    }

    build_heap(&heap);
    print_heap_info(&heap);

    printf("Before inserting the data, the top element of the heap is %d\n", get_heap_top(&heap));
    heap_push(&heap, 0);
    print_heap_info(&heap);
    printf("After inserting the data, the top element of the heap is %d\n", get_heap_top(&heap));
    heap_pop(&heap);
    printf("Pop the heap top data, the top element of the heap is %d\n", get_heap_top(&heap));

    found_data = find_heap_top_minimum_k_data(data, data_size, top_k);
    printf("Find top minimum %d in the data:\n\t", top_k);
    for (i = 0; i < top_k; ++i) {
        if (found_data) {
            printf("[%-2d] ", found_data[i]);
        }
    }
    free(found_data);
    found_data = NULL;

    printf("\n");
    found_data = find_heap_top_maximum_k_data(data, data_size, top_k);
    printf("Find top maximum %d in the data:\n\t", top_k);
    for (i = 0; i < top_k; ++i) {
        if (found_data) {
            printf("[%-2d] ", found_data[i]);
        }
    }
    free(found_data);
    found_data = NULL;

    printf("\nBefore heap sort, ");
    print_array_info(data, data_size);
    heap_sort(data, data_size);
    printf("After heap sort, ");
    print_array_info(data, data_size);

    printf("\ndestroy heap\n");
    destroy_heap(&heap);
    printf("heap size: %d\n", get_heap_size(&heap));
}

int main(void)
{
    test();

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值