Print binary tree

本文介绍了一个使用队列实现的二叉树数据结构及其可视化方法。通过递归创建平衡二叉树,并采用先序遍历填充节点信息到队列中,最终实现对二叉树结构的打印。文章详细展示了如何计算节点之间的距离、确定每个节点的位置,以及如何逐层打印二叉树。
#include 
#include 

#define ARRAY_SIZE (63)
#define NODE_DISTANCE (4)
#define PRINT_OFFSET (2)

typedef enum _CHILD_DIRECTION
{
	LEFT_CHILD = 1,
	RIGHT_CHILD,
	NOT_CHILD,
}CHILD_DIRECTION;

typedef struct _Node {
	int value;
	struct _Node *left;
	struct _Node *right;
}Node;

typedef struct _NodeInfo {
	struct _Node *node;
	struct _NodeInfo *parent;
	int height;
	int offset;
	int distance_between_nodes;
	CHILD_DIRECTION direction;
}NodeInfo;

static int queue_head_idx = 0, queue_tail_idx = 0;
static NodeInfo node_queue[ARRAY_SIZE+1];

static void queue_reset()
{
	queue_head_idx = queue_tail_idx = 0;
}

static int queue_empty()
{
	return queue_head_idx == queue_tail_idx;
}

static int queue_full()
{
	return ((queue_head_idx+1)%(ARRAY_SIZE+1)) == queue_tail_idx;
}

static void queue_insert(NodeInfo node_info)
{
	if (queue_full() == 0)
	{
		node_queue[queue_head_idx] = node_info;
		queue_head_idx++;
		queue_head_idx = queue_head_idx%(ARRAY_SIZE+1);
	}
}

static NodeInfo queue_offer()
{
	NodeInfo ret_node_info = {0};
	if (queue_empty() == 0)
	{
		ret_node_info = node_queue[queue_tail_idx];
		queue_tail_idx++;
		queue_tail_idx = queue_tail_idx%(ARRAY_SIZE+1);
	}
	return ret_node_info;
}

static Node * create_sub_tree(int *src, int start, int end)
{
	Node *node;
	int mid;
	
	if (start > end) return NULL;

	mid = (start + end)/2;

	node = malloc(sizeof(Node));
	if (node)
	{
		node->value = src[mid];
		node->left = create_sub_tree(src, start, mid -1);
		node->right= create_sub_tree(src, mid+1, end);
	}

	return node;
}

static void create_binary_tree(Node** root, int *src, int size)
{
	printf("create_binary_tree begin\n");

	if (root == NULL) return;
 
	*root = create_sub_tree(src, 0, size-1);
	printf("create_binary_tree end\n");
}

static void print_node(int height, NodeInfo *node_info_ordered, int size)
{
	int i, j, node_idx = 0, last_node_level = height;
	NodeInfo *node_info;
	NodeInfo *parent_node;
	int target_offset, current_offset;
	int current_line_width, node_num_in_current_line;
	
	current_offset = 0;
	target_offset = node_info_ordered[node_idx].distance_between_nodes/2 + PRINT_OFFSET;
	node_info_ordered[node_idx].offset = target_offset;
	node_num_in_current_line = 1;

	while(current_offset < target_offset) {
		printf(" ");
		current_offset++;
	}
	
	while (node_idx < size)
	{
		node_info = &node_info_ordered[node_idx];
		
		if(node_info->height != last_node_level)
		{
			printf("\n");

			parent_node = node_info->parent;
			current_line_width = parent_node->distance_between_nodes/2;
			
			current_offset = 0;
			target_offset = parent_node->offset - (current_line_width/2);
			while(current_offset < target_offset) {
				printf(" ");
				current_offset++;
			}

			for(i=0; iheight;
			node_num_in_current_line *= 2;

			current_offset = 0;
		}
		
		parent_node = node_info->parent;
		if(parent_node != NULL)
		{
			if (node_info->direction == LEFT_CHILD)
			{
				node_info->offset = parent_node->offset - (parent_node->distance_between_nodes/4);
			}
			else
			{
				node_info->offset = parent_node->offset + (parent_node->distance_between_nodes/4);
			}
		}

		target_offset = node_info->offset;
		while(current_offset < target_offset) {
			printf(" ");
			current_offset++;
		}
		current_offset += printf("%d", node_info->node->value);

		node_idx++;
	}

	printf("\n");
}



static int preordering_traverse(int height, Node* root, NodeInfo *node_info_ordered)
{
	NodeInfo node_info = {0};
	Node* node;
	int idx = 0, h;
	int i=1, distance_between_nodes = NODE_DISTANCE;
	while(ileft)
		{
			node_info.node = node->left;
			node_info.height = h-1;
			node_info.parent = &node_info_ordered[idx-1];
			node_info.distance_between_nodes = node_info.parent->distance_between_nodes/2;
			node_info.direction = LEFT_CHILD;
			queue_insert(node_info);
		}

		if (node->right)
		{
			node_info.node = node->right;
			node_info.height = h-1;
			node_info.parent = &node_info_ordered[idx-1];;
			node_info.distance_between_nodes = node_info.parent->distance_between_nodes/2;
			node_info.direction = RIGHT_CHILD;
			queue_insert(node_info);
		}
	}
	return idx;
}

#define TREE_MAX(a,b) (((a)>(b))?(a):(b))
static int calc_tree_height(Node* root)
{
	if(root != NULL)
		return TREE_MAX(1+calc_tree_height(root->left), 1+calc_tree_height(root->right));
	else
		return 0;
}

static void print_tree(Node* root)
{
	int height, offset, size;
	Node *node = root;
	NodeInfo node_queue_ordered[ARRAY_SIZE+1];
	
	height = calc_tree_height(root);

	printf("print_tree height = %d\n", height);

	size = preordering_traverse(height, root, node_queue_ordered);

	print_node(height, node_queue_ordered, size);

	queue_reset();
}

int main()
{
	int a[ARRAY_SIZE] = {3,5,5,1,2,6,8,4,7,9, \
		3,5,5,1,2,6,8,4,7,9, \
		3,5,5,1,3,5,5,1,2,6, \
		8,4,7,9,3,5,5,1,2,6, \
		3,5,5,1,3,5,5,1,2,6, \
		8,4,7,9,3,5,5,1,8,4, \
		7,9,3};
	Node* root;

	printf("print binary tree begin\n");

	create_binary_tree(&root, a, ARRAY_SIZE);

	print_tree(root);
	
	printf("print binary tree end\n");

	return 0;
}
<think>我们被要求详细介绍二叉树(binary tree)。结合用户提供的引用内容,我们可以从以下几个方面展开: 1. 二叉树的定义和基本概念(包括节点、根、子树等) 2. 二叉树的性质(如第i层最多有2^(i-1)个节点,深度为k的二叉树最多有2^k-1个节点等) 3. 特殊类型的二叉树(满二叉树、完全二叉树、完美二叉树等,注意引用[3]中提到的术语区别) 4. 二叉树的存储结构(通常用链式存储,如引用[1][2][4]中的节点结构) 5. 二叉树的遍历(深度优先:前序、中序、后序;广度优先:层次遍历) 6. 二叉树的构建(如引用[1]中的手动构建和引用[4]中的插入构建) 7. 二叉树的应用 由于用户要求详细,我们将每个部分都进行适当的展开。 注意:用户提供的引用中,引用[3]提到了满二叉树和完全二叉树的区别,并引入了完美二叉树的概念,我们需要明确这些术语。 下面开始组织内容: </think>### 二叉树(Binary Tree)详解 二叉树是一种重要的非线性数据结构,其特点是每个节点最多有两个子节点:**左子节点**和**右子节点**。这种结构天然支持递归定义,在算法设计和数据处理中具有广泛应用。 --- #### 一、二叉树核心概念 1. **节点结构** 每个节点包含三个部分: ```java class TreeNode { int data; // 节点存储的数据 TreeNode left; // 左子节点指针 TreeNode right; // 右子节点指针 } ``` 引用示例代码:[^4] 2. **基本术语** - **根节点(Root)**:树的起始节点(如`tree.root`)[^1] - **叶子节点(Leaf)**:左右子节点均为空的节点 - **深度(Depth)**:从根节点到当前节点的路径长度 - **高度(Height)**:从当前节点到最深叶子节点的路径长度 - **度(Degree)**:节点的子节点数量(二叉树中最大为2) 3. **特殊二叉树类型** | 类型 | 定义 | 示例 | |------|------|------| | **满二叉树(Full Binary Tree)** | 每个节点有0或2个子节点 | `A(B,C)` | | **完全二叉树(Complete Binary Tree)** | 除最后一层外全满,最后一层从左向右填充 | `A(B(D,E),C(F))` | | **完美二叉树(Perfect Binary Tree)** | 所有叶子节点在同一层,且每层都被填满 | `A(B(D,E),C(F,G))` [^3] | --- #### 二、二叉树存储结构 1. **链式存储(主流方式)** - 节点通过指针链接(如上文Java代码实现)[^4] - 优点:动态内存分配,插入删除灵活 - 缺点:指针占用额外空间 2. **顺序存储(数组实现)** - 根节点索引:`i=0` - 左子节点索引:`2i+1` - 右子节点索引:`2i+2` - 适用场景:完全二叉树存储优化 --- #### 三、二叉树遍历算法 1. **深度优先遍历(DFS)** ```java // 前序遍历:根 → 左 → 右 void preOrder(TreeNode node) { if (node == null) return; System.out.print(node.data + " "); // 访问根 preOrder(node.left); // 遍历左子树 preOrder(node.right); // 遍历右子树 } ``` - **中序遍历**:左 → 根 → 右(二叉搜索树可输出有序序列)[^1] - **后序遍历**:左 → 右 → 根(常用于表达式树求值) 2. **广度优先遍历(BFS)** 使用队列实现层次遍历: ```java void levelOrder(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); queue.add(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); System.out.print(node.data + " "); if (node.left != null) queue.add(node.left); if (node.right != null) queue.add(node.right); } } ``` --- #### 四、二叉树构建方法 1. **手动构建(静态)** 直接连接节点(适用于固定结构): ```java BinaryTree tree = new BinaryTree(); tree.root = new Node(1); tree.root.left = new Node(2); tree.root.right = new Node(3); // 引用[1]示例 ``` 2. **插入构建(动态)** 根据规则递归插入(如二叉搜索树): ```java void insert(TreeNode node) { if (root == null) root = node; else if (node.data < current.data) current.left = insert(current.left, node); else current.right = insert(current.right, node); } // 引用[4]逻辑简化版 ``` --- #### 五、二叉树关键性质 1. **节点数量关系** - 第 $i$ 层最多有 $2^{i-1}$ 个节点 - 深度为 $k$ 的树最多有 $2^k - 1$ 个节点 2. **高度与节点关系** 对于 $n$ 个节点的二叉树: - 最小高度:$\lfloor \log_2n \rfloor + 1$ - 最大高度:$n$(退化为链表) 3. **特殊性质** 在二叉搜索树中(BST): - 左子树所有节点值 $<$ 根节点值 - 右子树所有节点值 $>$ 根节点值 --- #### 六、二叉树应用场景 1. **数据检索**:二叉搜索树实现$O(\log n)$查找 2. **表达式解析**:算术表达式的语法树表示 3. **文件系统**:目录树结构建模 4. **游戏AI**:决策树实现行为选择 5. **压缩算法**:哈夫曼编码构建最优前缀树 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值