堆排序的二三事

1.可以用做外排序。

比如查找N个数据(假设数据量内存放不下)中最大的前K个

取这N个数据中的前K个建一个小堆,

因为是小堆,所以,堆顶数据必然是最小的

此时,读取剩余数据,只要是大于堆顶数据,就交换堆顶数据和读到的数据。

然后调整堆,让堆顶继续是最小数据。

(永远保证堆里面保存着读之前所有数据中最大的前K个)

一直读取,直到所有数据读结束。 此时堆里就保存着这N个数据中最大的前K个。

如果是最小的前K个,同理,建大堆。


2.堆也可以直接对一组数据排序。

如果是排成升序建大堆

大堆保证了堆顶数据是最大的,且每个节点都是大堆

堆顶既然已经是最大的数据,堆顶的数据已经有序,所以把堆顶的数据放在数组最后面(因为是升序)

再用原堆底的数据来填补堆顶。(即交换 堆顶和堆低)。

此时再对除堆低以外的数据继续建大堆,然后交换堆低和堆顶,直到只剩最后一个数据,

此时数组有序。

同理,排降序,则建小堆





1.最大前K个数据如下:

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <windows.h>
#include <vector>
#include <assert.h>
#include <utility>

using namespace std;

template<class T>

class Less
{
public:
	bool operator()(const T& left, const T& right)  const
	{
		return left < right;
	}
};

template<class T>
class Great
{
public:
	bool operator()(const T& left, const T& right)  const
	{
		return left > right;
	}
};

template<class T, class Compare = Great<T>>
class Heap
{
public:
	Heap()
	{}
	Heap(T* a, size_t n)
	{
		_a.reserve(n);

		for (size_t i = 0; i < n; i++)
		{
			_a.push_back(a[i]);
		}

		//建堆
		for (int i = (_a.size() - 2) / 2; i >= 0; --i)
		{
			AdjustDown(i);
		}
	}


	void AdjustDown(int root)   //向下调整
	{
		Compare com;
		int parent = root;
		int child = parent * 2 + 1;

		while (child < _a.size())
		{
			if (child + 1 < _a.size() && com(_a[child + 1], _a[child]))   //选出 最大的一个孩子
			{
				++child;
			}

			if (com(_a[child], _a[parent]))     //让父亲是最大的
			{
				swap(_a[child], _a[parent]);
				parent = child;
				child = child * 2 + 1;
			}
			else
			{
				break;
			}
		}
	}

	void AdjustUp(int child)              //向上调整
	{
		Compare com;
		int parent = (child - 1) / 2;

		while ()
		{
			if (com(_a[parent], _a[child]))
			{
				swap(_a[parent], _a[child]);
				child = patent;
				parent = (child - 1) / 2;
			}
			else
			{
				break;
			}
		}
	}
protected:
	vector<T> _a;
};


void  AdjustDown(int* heap, int n, int root)
{
	assert(heap);
	int parent = root;
	int child = parent * 2 + 1;

	while (child < n)
	{
		if (child + 1 < n && heap[child + 1] < heap[child])
		{
			++child;
		}

		if (heap[child] < heap[parent])
		{
			swap(heap[child], heap[parent]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

void TopK()
{
	const size_t N = 10000;
	const size_t K = 10;
	int a[N] = { 0 };

	for (size_t i = 0; i < N; i++)
	{
		a[i] = rand() % N;
	}


	a[0] = N + 100;
	a[100] = N + 101;
	a[2] = N + 102;
	a[50] = N + 105;
	a[1000] = N + 100;
	a[1005] = N + 1550;
	a[888] = N + 130;
	a[9998] = N + 100;
	a[9999] = N + 100;
	a[9444] = N + 100;


	int heap[K] = { 0 };

	for (size_t i = 0; i < K; i++)
	{
		heap[i] = a[i];
	}

	// 建小堆   堆顶为最小数据
	for (int i = (K - 2) / 2; i >= 0; i--)
	{
		AdjustDown(heap, K, i);
	}


	//大于堆顶的数据,放入堆  调整一次
	for (int i = K; i < N; i++)
	{
		if (a[i] > heap[0])
		{
			heap[0] = a[i];
			AdjustDown(heap, K, 0);
		}
	}


	for (size_t i = 0; i < K; i++)
	{
		cout << heap[i] << " ";
	}
	cout << endl;
}


int main()
{
	int a[] = { 10,11, 13, 12, 16, 18, 15, 17, 14, 19 };
	Heap<int>hp(a, sizeof(a) / sizeof(a[0]));
	TopK();

	system("pause");
	return 0;
}




2.最大前K个

void AdjustDown(int a[], int root, int len)        //向下调整   //
{
	assert(a);

	int parent = root;
	int child = parent * 2 + 1;
	while (child < len)                  //检查孩子节点是否存在
	{
		if (child + 1 < len && a[child] < a[child + 1])
		{
			child++;
		}
		if (a[parent] < a[child])         //孩子中最大的那个大于父节点则交换 (大堆,必须保证父节点是最大的)
		{
			swap(a[child], a[parent]);
			parent = child;            //检查孩子节点 是否是大堆
			child = parent * 2 + 1;
		}
		else
			break;
	}
}

void HeapSort(int a[], int len) //升序,建大堆
{
	assert(a);

	for (int i = (len - 1) / 2; i >= 0; i--)
	{
		AdjustDown(a, i, len);       //建大堆,必须保证根节点(堆顶)是最大的。
	}

	int end = len;
	while (end > 0)               
	{
		swap(a[end], a[0]);       //用最后一个节点替换根节点。
		AdjustDown(a, 0, end);
		end--;                    //此时最后一个节点已经是最大节点(已经有序),去掉最后一个节点。
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值