二叉堆、堆排序

目录

一,二叉堆

二,堆的调整

三,堆的创建

四,堆排序

五,堆元素更新

六,堆的插入和删除

七,堆的按层打印

八,操作测试


一,二叉堆

二叉堆是用数组实现的完全二叉树,也就是说物理结构是数组,逻辑结构是完全二叉树。

二叉堆分2种,最大堆和最小堆。

最大堆指的是,任何一个结点都比它的两个子结点(或1个或0个)要大,最小堆同理。

本文以最大堆为例进行展示。

二,堆的调整

堆的调整指的是,给出一个完全二叉树,除了根结点之外,左子树是二叉堆,右子树是二叉堆,现在要调整元素位置,让整个树成为二叉堆。

其实就是把堆顶元素不停的往下塞,塞到合适的位置就停止了。

实现代码:

template<typename T>
bool cmp(T a, T b)
{
    return a < b; //最大堆
}

template<typename T>
void exchange(T* a, T* b)
{
    T tmp = *a;
    *a = *b;
    *b = tmp;
}

int LeftChild(int id)
{
	return id * 2 + 1;
}
int RightChild(int id)
{
	return id * 2 + 2;
}
int Parent(int id)
{
	return (id - 1) / 2;
}

template<typename T>
void AdjustHeap(T* arr, int rootId, int size)
{
    int largest = rootId, left = LeftChild(rootId), right = RightChild(rootId);
    if (left < size && cmp(arr[largest], arr[left]))largest = left;
    if (right < size && cmp(arr[largest], arr[right]))largest = right;
    if (largest == rootId)return;
    exchange(arr + rootId, arr + largest);
    AdjustHeap(arr, largest, size);
}

时间复杂度:O(h),其中h是二叉堆的高度,h=Θ(log n),其中n=size,表示所有结点数目

PS:如果要最小堆,只需要改cmp函数即可,其他(包括下文)所有代码都不用改

三,堆的创建

给出一个数组,现在要交换元素位置,使得它变成一颗二叉堆。

实现代码:

template<typename T>
void InitHeap(T* arr, int size)
{
    for (int i = size / 2; i >= 0; i--)AdjustHeap(arr, i, size);
}

原理:因为是完全二叉树,所以最后一层的结点数目小于总数的一半,也就是说,从size/2到0覆盖了除掉最后一层之外的所有结点。

时间复杂度:Θ(n),其中n=size,表示所有结点数目

四,堆排序

算法思路:

先构建二叉堆,于是数组第一个元素就是最大元素,把它和最后一个元素交换,

对于剩下的n-1个元素,直接调用AdjustHeap即可再次变成堆,于是得到了这n-1个元素里面的最大值,

依次类推,最后就变成增序的数组了。

template<typename T>
void HeapSort(T* arr, int size)
{
    InitHeap(arr, size);
    for (int i = size - 1; i > 0; i--) {
        exchange(arr + i, arr);
        AdjustHeap(arr, 0, i);
    }
}

时间复杂度:O(n log n)

五,堆元素更新

template<typename T>
void HeapIncrese(T* arr, int size, int id, T newValue)
{
	arr[id] = newValue;
	while (id > 0 && cmp(arr[Parent(id)], arr[id])) {
		exchange(arr + id, arr + Parent(id));
		id = Parent(id);
	}
}
template<typename T>
void HeapDecrese(T* arr, int size, int id, T newValue)
{
	arr[id] = newValue;
	AdjustHeap(arr, id, size);
}
template<typename T>
void HeapChange(T* arr, int size, int id, T newValue)
{
	if (cmp(arr[id], newValue))HeapIncrese(arr, size, id, newValue);
	else HeapDecrese(arr, size, id, newValue);
}

六,堆的插入和删除

template<typename T>
void HeapInsert(T* arr, int &size, T value)
{
	HeapIncrese(arr, size + 1, size, value);
	size++;
}
template<typename T>
void HeapDelete(T* arr, int &size, T value)
{
	for (int i = 0; i < size; i++) {
		if (arr[i] == value) {
			HeapChange(arr, size - 1, i, arr[size - 1]);
			size--;
			break;
		}
	}
}

七,堆的按层打印

template<typename T>
void HeapPrint(T* arr, int size)
{
	int mi = 2;
	for (int i = 0; i < size; i++) {
		cout << arr[i];
		if (i == mi - 2 || i == size - 1) {
			cout << endl;
			mi *= 2;
		}
		else cout << " ";
	}
	cout << endl;
}

八,操作测试

int arr[100000];

int main()
{
	int n, m;
	cin >> n >> m;
	for (int i = 0; i < n; i++)cin >> arr[i];
	InitHeap(arr, n);
	string opt;
	int num;
	while (cin >> opt >> num) {
		if (opt == "add") {
			HeapInsert(arr, n, num);
		}
		else {
			HeapDelete(arr, n, num);
		}
		HeapPrint(arr, n);
	}
	return 0;
}

输入:

5 4
1 2 3 4 5
add 6
delete 3
delete 2
add 2

输出:

1
2 3
4 5 6

1
2 6
4 5

1
4 6
5

1
2 6
5 4

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值