完全二叉树可视化

最近在看一些数据结构和算法,对二叉树接触比较多,写程序时要想知道树的状态只能通过监视窗口翻着看,比较费事而且不清晰,因此想写个将二叉树可视化的程序,不过笔者水平有限,暂时只实现了针对完全二叉树的,如果不是完全二叉树则可以用0或其他特征字符代替前部缺省的字符,以下分享给大家取用。
贴代码之前先放个原理图,应该也是大家写相关程序主要要解决的问题:树同一层节点数据的间隔长度:在这里插入图片描述
以下程序在visual stadio2017通过c++程序进行开发:

void showtree(T* arr,int len)
{
	if (len == 1)  //如果这个树总长为1直接输出
	{
		cout << arr[0] << endl; return;
	}
	if (len == 0)return;  //如果这个树总长为0,直接返回
	int* gap=new int[len+1]();   //存储树相邻节点距离
	int* pos = new int[len + 1];
	int* alen = new int[len+1];    //存储节点字符长度
	int cnt,tmp,wid=1,dep=log2(len);
	int dtmp = dep-1;
	int i;
	for ( i= 0; i < len; i++)
	{
		alen[i]= (ostringstream() << arr[i]).str().length();
		//提取树每个节点的字符长度
	}
	alen[len] = 1;
	for (int j = len / 2; j < len; j++)
	{
		gap[j] = 1;
		//没有子节点的各节点间隔设为1
	}
	gap[len] = 1;   //末尾再设置个为1的间隔
	int j = pow(2, dtmp) - 1;    //倒数第二行的起点
	tmp = (alen[2 * j + 1] + gap[2 * j + 2] + alen[2 * j + 2] - alen[j]) / 2;
	gap[j] = gap[2 * j + 1] + tmp+1;
	//该点(左侧起点)前部间隔为... 
	//2*j+1代表左子节点,2*j+2代表右子节点
	for (j++; j < len/2; j++)
	{
		gap[j] = tmp+gap[2*j+1];
		tmp= (alen[2 * j + 1] + gap[2 * j + 2] + alen[2 * j + 2] - alen[j]) / 2;
		gap[j] += tmp;
		//非左侧起点的其他点前部间隔计算公式
	}
	dtmp--;
	//以下为其余各层的前部间隔计算公式
	for (i = dtmp; i >= 0; i--)
	{
		int j = pow(2, dtmp) - 1;
		tmp = (alen[2 * j + 1] + gap[2 * j + 2] + alen[2 * j + 2] - alen[j]) / 2;   
		//根据子节点中心得出该节点位置
		gap[j] = gap[2 * j + 1] + tmp+1;
		//加上起点位置,再向右偏移一格
		for (j++; j < pow(2, dtmp+1) - 1; j++)
		{
			gap[j] = tmp + gap[2 * j + 1];
			tmp = (alen[2 * j + 1] + gap[2 * j + 2] + alen[2 * j + 2] - alen[j]) / 2;
			gap[j] += tmp;
			//除起点外,其他要加上前后两部分的位置偏差
		}
		dtmp--; //每次层数减一(向上)
	}
	int newdep = 1;
	for (cnt = 0; cnt < gap[0]; cnt++)cout << " ";
	cout << arr[0] << endl;   //将第一行打印出来,上方无斜杠,因此与以下循环分离
	while (newdep < dep)
	{
		i = pow(2, newdep) - 1;   //各行的第一位
		for (cnt = 0; cnt < gap[i] + alen[i] / 2; cnt++)cout << " ";   //打印前部间隔+半字符长度的空格
		cout << '/';                                                  //最左侧公式不同,因此分离
		for (i++; i < pow(2, newdep + 1) - 1; i++)
		{
			//同理求出各斜杠位置,-1为斜杠自身占位符
			for (cnt = 0; cnt < gap[i] + (alen[i - 1] + alen[i]) / 2-1; cnt++)cout << " ";
			cout << '\\';         
			if (++i == pow(2, newdep + 1) - 1)break;
			for (cnt = 0; cnt < gap[i] + (alen[i - 1] + alen[i]) / 2-1; cnt++)cout << " ";
			cout << '/';
		}
		cout << endl;   //每处理完一行则换行
		//打印字符
		for (i = pow(2, newdep) - 1; i < pow(2, newdep + 1) - 1; i++)
		{
			for (cnt = 0; cnt < gap[i]; cnt++)cout << " ";
			cout << arr[i];
		}
		cout << endl;
		newdep++;
	}
	//最后一层单独处理
	i = pow(2, newdep) - 1;
	for (cnt = 0; cnt < gap[i] + alen[i] / 2; cnt++)cout << " ";
	cout << '/';
	for (i++; i < len; i++)
	{
		for (cnt = 0; cnt < gap[i] + (alen[i - 1] + alen[i]) / 2 - 1; cnt++)cout << " ";
		cout << '\\';
		if (++i == pow(2, newdep + 1) - 1)break;
		for (cnt = 0; cnt < gap[i] + (alen[i - 1] + alen[i]) / 2 - 1; cnt++)cout << " ";
		cout << '/';
	}
	cout << endl;
	for (i = pow(2, newdep) - 1; i < len; i++)
	{
		for (cnt = 0; cnt < gap[i]; cnt++)cout << " ";
		cout << arr[i];
	}
	cout << endl;
	delete[]gap;
	delete[]pos;
	delete[]alen;  //最后还要记得内存回收
}

int main()
{
	double arr[10] = { 8.5,54.1,4.8,55.8,56485,15.789,123.55,78.885,1,2.551};
	showtree(arr, 10);
	system("pause");
}

里边注释比较多久不多介绍了,而笔者这里做的输入接口是二叉树的层序遍历,因此二叉树结构体需要先做层序遍历,这个有很多的例程,这里也提供一个:

vector<int> levelOrder(TreeNode* root) {
		vector<int> res;
		if (!root)return res;
		TreeNode* temp;
		queue<TreeNode*> myq;  //层序遍历通过队列实现
		myq.push(root);
		int len;
		while (!myq.empty())
		{
			len = myq.size();
			for (int i = 0; i <len ; i++)  //每次遍历一层
			{
				temp = myq.front();  //取出元素放入,再将子节点入队
				res.push_back(temp->val);
				myq.pop();
				if (temp->left)myq.push(temp->left);
				if (temp->right)myq.push(temp->right);
			}
		}
		return res;
    }

这里采用的返回值是vector容器,由于其迭代器就是指针,因此可以很容易将vector转为数组,例如:

	vector<int> ans;
	ans = newlevelOrder(tre);
	int* a = &ans[0];

这是所完成的可视化效果演示:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值