最大堆(C++)

        最大堆的性质就是每个非叶子节点都要比它的两个儿子(或是一个)要大,因此我们在插入与删除时必定会对插入点的祖先或是后代进行访问,这种访问方式不宜使用顺序遍历来实现,显然通过层序遍历直接访问下标的方式更加容易实现,对于节点i,i/2即为它的父节点,2*i,2*i+1,即为它的子节点,所以这里使用数组实现而不是链表。

基本功能插入,删除,遍历

class MaxTree
{
public:
	vector<int>tree;

	int getHight();//获取树高

	void Insert(int val);

    void put_in(int *arr,int n);

	void Erase(int val);

	void printTree();
};

为了方便直接使用vector容器装数据了,就不额外写那些接口了

树的高度即为log2(n+1)

int MaxTree::getHight()
{
	int n = tree.size();
	return log(n + 1) / log(2) + 1;
}

不要忘了包含math.h头文件

由于最大堆是完全二叉树,新数据一律插在最后,然后再向上与其父节点对比,比父节点大了就与父节点换位,然后继续向上重复这个操作直到比父节点小

void MaxTree::Insert(int val)
{
	vector<int>::iterator it = find(tree.begin(), tree.end(), val);
	if (it != tree.end())
	{
		cout << "插入失败" << endl;
		return;
	}

	tree.push_back(val);
	int n = tree.size();
	while (n / 2 != 0)
	{
		if (tree[n / 2 - 1] > tree[n - 1])
		{
			return;
		}
		else
		{
			int temp = tree[n - 1];
			tree[n - 1] = tree[n / 2 - 1];
			tree[n / 2 - 1] = temp;
			n = n / 2;
		}
	}
}

要使用find函数先包含头文件algorithm,如果找到与插入数据相同的数据,就插入失败,如果没有就先尾插这个数据,然后获得这个容器的元素个数,即为新元素的下标,然后循环的与其父节点对比,直到它比父节点小或是它成为了根。

还有一种插入方法是先传入一个数组,然后把这个数组变成最大堆,由于要求每个子树都是最大堆,那么就从倒数第二层开始,从后向前的依次将每个堆变成最大堆,这样,从最后一个非叶子结点到根,都进行了算法处理,使得每个结点都比它的儿子大

void MaxTree:: put_in(int *arr,int n)
{
	for (int i = 0; i < n; i++)
	{
		this->tree.push_back(arr[i]);
	}

	int pos = n / 2;//这个位置是最后一个非叶子结点

	while (pos > 0)//对每个非叶子结点做一遍算法
	{
		int m = pos;
		while (m * 2 <= n)
		{
			int _m;
			if (m * 2 + 1 > n)
				_m = m * 2;
			else
				_m = tree[m * 2 - 1] > tree[m * 2] ? (m * 2) : (m * 2 + 1);

			if (tree[m - 1] < tree[_m - 1])
			{
				int temp = tree[m - 1];
				tree[m - 1] = tree[_m - 1];
				tree[_m - 1] = temp;
			}
			m = _m;
		}
		pos--;
	}

}

对于删除操作,我们只能对根进行删除,因为我们只对根进行了维护(即根为最大值),其他元素没有顺序可言,而删除一个不确定位置的元素对我们来说没有任何意义,因此不能随意的删除元素。

void MaxTree::Erase()
{
	int n = tree.size();
	if (n - 1 == 0)
	{
		tree.pop_back();
		return;
	}

	int temp = tree[n - 1];
	tree[n - 1] = tree[0];
	tree[0] = temp;
	tree.pop_back();
	n--;
	int m = 1;
	while (m * 2 <= n)
	{
		int _m;
		if (m * 2 + 1 > n)
		{
			_m = m * 2;
		}
		else
		{
			_m = tree[m * 2 - 1] > tree[m * 2] ? (m * 2) : (m * 2 + 1);
		}

		if (tree[m - 1] < tree[_m - 1])
		{
			temp = tree[m - 1];
			tree[m - 1] = tree[_m - 1];
			tree[_m - 1] = temp;
			m = _m;
		}
		else
		{
			return;
		}
	}

}

与插入操作相反,删除操作需要将根与最后一个元素互换,再尾删它,最后一个元素就成为了新的根,但是新的根并不是最大值,那就要将它与两个儿子对比,与其中大的一个交换,再循环往复直到它比两个儿子都大

在主函数中进行若干插入删除操作后,打印输出来看一下是否满足要求,为了打印出的数据更加直观,这里使用iomanip头文件中的setw()函数调整输出格式,这个函数限定了输出位数,不足位数的以空格代替

void MaxTree::printTree()
{
	int n = 0;
	int p = 1;
	int h = getHight();
	for (auto it = tree.begin(); it != tree.end(); it++)
	{
		cout << setw(pow(2,h + 1 - p)) << *it <<setw(pow(2, h + 1 - p))<< " ";
		n++;
		if (n == pow(2, p) - 1)
		{
			p++;
			cout << endl;
		}
	}
	cout << endl;

输出效果如图,形象的展示了树的结构。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值