堆排序01

0.最大堆模板类的简单实现


template<typename Item>
class MaxHeap {

private:
	Item *data;
	int count;
	int capacity;
	void shiftUp(int k)//新加入元素后会破坏树的平衡,使用shitUp进行元素位置的调整
	{
		while (k > 1/*k>1避免越界*/ && data[k / 2] < data[k])//父节点小于当前元素
		{
			swap(data[k / 2], data[k]);
			k /= 2;//更新k将父节点设为当前元素进行考察
		}
	}


	void shiftDown(int k)
	{
		while (2*k <=count)//该节点有左孩子就说明有孩子
		{
			int j = 2 * k;//在此轮循环中,data[k]和data[j]交换位置
			if (j + 1 <= count && data[j + 1] > data[j])//说明有右孩子,并判断右孩子是否大于左孩子,如果是,则和右孩子交换位置
				j = j + 1;

			if(data[k]>=data[j])//该节点已经大于孩子节点,则无需交换
				break;

			swap(data[k], data[j]);
			k = j;//更新k值,继续考察
		}
	}


	void putNumberInLine(int num, string &line, int index_cur_level, int cur_tree_width, bool isLeft) {

		int sub_tree_width = (cur_tree_width - 1) / 2;
		int offset = index_cur_level * (cur_tree_width + 1) + sub_tree_width;
		assert(offset + 1 < line.size());
		if (num >= 10) {
			line[offset + 0] = '0' + num / 10;
			line[offset + 1] = '0' + num % 10;
		}
		else {
			if (isLeft)
				line[offset + 0] = '0' + num;
			else
				line[offset + 1] = '0' + num;
		}
	}

	void putBranchInLine(string &line, int index_cur_level, int cur_tree_width) {

		int sub_tree_width = (cur_tree_width - 1) / 2;
		int sub_sub_tree_width = (sub_tree_width - 1) / 2;
		int offset_left = index_cur_level * (cur_tree_width + 1) + sub_sub_tree_width;
		assert(offset_left + 1 < line.size());
		int offset_right = index_cur_level * (cur_tree_width + 1) + sub_tree_width + 1 + sub_sub_tree_width;
		assert(offset_right < line.size());

		line[offset_left + 1] = '/';
		line[offset_right + 0] = '\\';
	}

public:

	// 构造函数, 构造一个空堆, 可容纳capacity个元素
	MaxHeap(int capacity) {
		data = new Item[capacity + 1];//从索引1开始使用
		count = 0;
		this->capacity = capacity;
	}

	//Heapify
	//这种构造函数就避免了先insert(一个for循环),再extarct(一个for循环)
	//降低了算法复杂度
	MaxHeap(Item arr[], int n)
	{
		data = new Item[n + 1];
		capacity = n;
		for (int i = 0;i<n;i++)
			data[i + 1] = arr[i];
		count = n;

		//调整堆
		for (int i = count / 2; i >= 1; i--)//count/2第一个不是叶子节点的节点
			shiftDown(i);
	}

	~MaxHeap() {
		delete[] data;
	}

	// 返回堆中的元素个数
	int size() {
		return count;
	}

	// 返回一个布尔值, 表示堆中是否为空
	bool isEmpty() {
		return count == 0;
	}


	//插值
	void insertElement(Item item)
	{
		assert(count + 1 <= capacity);//检查是否超出前面开辟的内存
		data[count + 1] = item;
		count++;
		shiftUp(count);
	}


	//取值,取出最大值
	Item extractElement()
	{
		assert(count > 0);
		Item ret = data[1];
		swap(data[1], data[count]);
		count--;
		shiftDown(1);
		return ret;
	}


	// 以树状打印整个堆结构
	void testPrint() {

		// 我们的testPrint只能打印100个元素以内的堆的树状信息
		if (size() >= 100) {
			cout << "This print function can only work for less than 100 int";
			return;
		}

		// 我们的testPrint只能处理整数信息
		if (typeid(Item) != typeid(int)) {
			cout << "This print function can only work for int item";
			return;
		}

		cout << "The max heap size is: " << size() << endl;
		cout << "Data in the max heap: ";
		for (int i = 1; i <= size(); i++) {
			// 我们的testPrint要求堆中的所有整数在[0, 100)的范围内
			assert(data[i] >= 0 && data[i] < 100);
			cout << data[i] << " ";
		}
		cout << endl;
		cout << endl;

		int n = size();
		int max_level = 0;
		int number_per_level = 1;
		while (n > 0) {
			max_level += 1;
			n -= number_per_level;
			number_per_level *= 2;
		}

		int max_level_number = int(pow(2, max_level - 1));
		int cur_tree_max_level_number = max_level_number;
		int index = 1;
		for (int level = 0; level < max_level; level++) {
			string line1 = string(max_level_number * 3 - 1, ' ');

			int cur_level_number = min(count - int(pow(2, level)) + 1, int(pow(2, level)));
			bool isLeft = true;
			for (int index_cur_level = 0; index_cur_level < cur_level_number; index++, index_cur_level++) {
				putNumberInLine(data[index], line1, index_cur_level, cur_tree_max_level_number * 3 - 1, isLeft);
				isLeft = !isLeft;
			}
			cout << line1 << endl;

			if (level == max_level - 1)
				break;

			string line2 = string(max_level_number * 3 - 1, ' ');
			for (int index_cur_level = 0; index_cur_level < cur_level_number; index_cur_level++)
				putBranchInLine(line2, index_cur_level, cur_tree_max_level_number * 3 - 1);
			cout << line2 << endl;

			cur_tree_max_level_number /= 2;
		}
	}
};

1.最大堆排序

heapSort1, 将所有的元素依次添加到堆中, 在将所有元素从堆中依次取出来, 即完成了排序,无论是创建堆的过程, 还是从堆中依次取出元素的过程, 时间复杂度均为O(nlogn), 整个堆排序的整体时间复杂度为O(nlogn).

template<typename T>
void heapSort1(T arr[], int n) {

	MaxHeap<T> maxheap = MaxHeap<T>(n);//构造一个类,以类型T作为 模板类中的类型
	for (int i = 0; i < n; i++)
		maxheap.insertElement(arr[i]);

	for (int i = n - 1; i >= 0; i--)
		arr[i] = maxheap.extractElement();
}

heapSort2, 借助的heapify过程创建堆,此时, 创建堆的过程时间复杂度为O(n), 将所有元素依次从堆中取出来, 实践复杂度为O(nlogn).堆排序的总体时间复杂度依然是O(nlogn), 但是比上述heapSort1性能更优, 因为创建堆的性能更优.

//Heapify
//这种构造函数就避免了先insert再extarct,减少了一个shiftUp的过程.
//降低了算法复杂度
MaxHeap(Item arr[], int n)
{
	data = new Item[n + 1];
	capacity = n;
	for (int i = 0;i<n;i++)
		data[i + 1] = arr[i];
	count = n;

	//调整堆
	for (int i = count / 2; i >= 1; i--)//count/2第一个不是叶子节点的节点
		shiftDown(i);
}
	
///

template <typename T>
void heapSort2(T arr[], int n)
{
	MaxHeap<T> maxheap = MaxHeap<T>(arr, n);
	for (int i = n - 1; i >= 0; i--)
		arr[i] = maxheap.extractElement();
	SortTestHelper::printArray(arr, n);
}

2.原地排序

脱离上面的类,直接在数组上进行heapify操作。

template<typename T>
void __shiftDown_(T arr[],int n,int k)
{
	while (2 * k +1 < n)//该节点有左孩子就说明有孩子
	{
		int j = 2 * k +1;//在此轮循环中,data[k]和data[j]交换位置
		if (j + 1 < n && arr[j + 1] > arr[j])//说明有右孩子,并判断右孩子是否大于左孩子,如果是,则和右孩子交换位置
			j = j + 1;

		if (arr[k] >= arr[j])//该节点已经大于孩子节点,则无需交换
			break;

		swap(arr[k], arr[j]);
		k = j;//更新k值,继续考察
	}
}

template<typename T>
void heapSort(T arr[], int n)
{
	//首先进行heapify,索引改为从0开始
	for (int i = (n - 1) / 2; i >= 0; i--)
		__shiftDown_(arr, n, i);

	for (int i = n - 1; i > 0; i--)
	{
		swap(arr[0], arr[i]);
		__shiftDown_(arr, i, 0);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值