数据结构 - 优先队列与堆


使用场景

某些情况下要让高优先级的元素先出队,优先队列实际上是用堆实现的。

C 实现

priority_queue 优先队列

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define swap(a, b) { \
    __typeof(a) __temp = a; \
    a = b; b = __temp; \
}

typedef struct priority_queue {
	int *data;
	int cnt, size;
} priority_queue;

priority_queue *init(int n) {
	priority_queue *q = (priority_queue *) malloc(sizeof(priority_queue));
	q->data = (int *) malloc(sizeof(int) * (n + 1));
	q->cnt = 0;
	q->size = n;
	return q;
}

int empty(priority_queue *q) {
	return q->cnt == 0;
}

// 堆一般使用下标 1 作为头节点
int top(priority_queue *q) {
	return q->data[1];
}

// 此处优先队列实现为小顶堆,小的元素在堆顶
// O(lgN)
int push(priority_queue *q, int val) {
	if (q == NULL) return 0;
	if (q->cnt == q->size) return 0;
	q->data[++(q->cnt)] = val;
	// 自底向上调整
	int ind = q->cnt;
	while (ind / 2 && q->data[ind] > q->data[ind / 2]) {
		swap(q->data[ind], q->data[ind / 2]);
		ind /= 2;
	}
	return 1;
}

// O(lgN)
int pop(priority_queue *q) {
	if (q == NULL) return 0;
	if (empty(q)) return 0;
	// 头节点出队,最后一个节点交换后自顶向下调整
	q->data[1] = q->data[q->cnt--];
	int ind = 1;
	while ((ind * 2) <= q->cnt) {
		int temp = ind, l = ind * 2, r = ind * 2 + 1;
		// 选择左右节点较大向下调整
		if (q->data[l] > q->data[temp]) temp = l;
		if (r <= q->cnt && q->data[r] > q->data[temp]) temp = r;
		// 无法继续向下调整
		if (temp == ind) break;
		swap(q->data[ind], q->data[temp]);
		ind = temp;
	}
	return 1;
}

void clear(priority_queue *q) {
	if (q == NULL) return;
	free(q->data);
	free(q);
	return;
}

int main() {
	// 模拟
	srand(time(0));
    #define max_op 20
    priority_queue *q = init(max_op);
    for (int i = 0; i < max_op; ++i) {
        int val = rand() % 100;
        push(q, val);
        printf("insert %d to the priority_queue!\n", val);
    }
    for (int i = 0; i < max_op; ++i) {
        printf("%d ", top(q));
        pop(q);
    }
    #undef max_op
    printf("\n");
    clear(q);
    return 0;
}

heap_sort 利用堆的性质,建立初始堆,利用堆的性质完成高效的交换排序

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define swap(a, b) {\
    __typeof(a) __temp = a;\
    a = b; b = __temp;\ 
}

// O(lgN) 向下调整
void downUpdate(int *arr, int n, int ind) {
	while (ind * 2 <= n) {
		int temp = ind, l = ind * 2, r = ind * 2 + 1;
		if (arr[l] > arr[temp]) temp = l;
		if (r <= n && arr[r] > arr[temp]) temp = r;
		if (ind == temp) break;
		swap(arr[temp], arr[ind]);
		ind = temp;
	}
	return ;
}

// O(NlgN) 还需要一个先维护堆的时间
void heap_sort(int *arr, int n) {
	// 技巧:数组内存位置 -1,实际以 arr[0] 进行操作 arr[1-n] 排序掉
	arr -= 1;
	// O(N) 先维护一个堆结构
	// S=2^(i-1)*(k-i)  (S为时间,i为当前层数,k为堆高度)
	// S=2^k-k-1=n-lgN-1(数学公式化解,k=lgN) 
	for (int i = n / 2; i >= 1; --i) {
		downUpdate(arr, n, i);
	}
	// O(NlgN) 已经具有堆的性质,接下来的每次交换 O(lgN)
	for (int i = n; i > 1; --i) {
		swap(arr[i], arr[1]);
		downUpdate(arr, i - 1, 1);
	}
}

void output(int *arr, int n) {
    printf("[");
    for (int i = 0; i < n; ++i) {
        printf("%d ", arr[i]);
    }
    printf("]\n");
}

int main() {
	// 排序模拟
    srand(time(0));
    #define max_n 20
    int *arr = (int *) malloc(sizeof(int) * max_n);
    for (int i = 0; i < max_n; ++i) {
        arr[i] = rand() % 100;
    }
    output(arr, max_n);
    heap_sort(arr, max_n);
    output(arr, max_n);
    free(arr);
    #undef max_n
    return 0;
}

C++ 实现

#include <bits/stdc++.h>

using namespace std;

class Heap {
private:
    int *data;
    int size;
    // 递归方式向下调整
    // 以 0 下标为头节点的操作
    void update(int pos, int n) {
        int lchild = 2 * pos + 1, rchild = 2 * pos + 2;
        int max_value = pos;
        if (lchild < n && data[lchild] > data[max_value]) {
            max_value = lchild;
        }
        if (rchild < n && data[rchild] > data[max_value]) {
            max_value = rchild;
        }
        if (max_value != pos) {
            swap(data[pos], data[max_value]);
            update(max_value, n);
        }
    }
public:
    Heap(int length_input) {
        data = new int[length_input];
        size = 0;
    }
    ~Heap() {
        delete[] data;
    }
    int top() {
        return data[0];
    }
    // 大顶堆实现
    void push(int value) {
        data[size] = value;
        int current = size;
        // 0 下标取 father
        int father = (current - 1) / 2;
        while (data[current] > data[father]) {
            swap(data[current], data[father]);
            current = father;
            father = (current - 1) / 2;
        }
        size++;
    }
    void pop() {
        swap(data[0], data[size - 1]);
        size--;
        update(0, size);
    }
    void output() {
        for (int i = 0; i < size; i++) {
            cout << data[i] << " ";
        }
        cout << endl;
    }
    // 使用该数据结构对数组进行堆排序时,需要将数组元素先 push 进去
    void heap_sort() {
        for (int i = size - 1; i >= 1; --i) {
            swap(data[i], data[0]);
            update(0, i);
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值