遍历树的两种方式的测试及比较

节点类:

/**
 * 节点类
 */
public class TreeNode {
	/**
	 * 节点编号
	 */
	public String id;
	/**
	 * 节点内容
	 */
	public String text;
	/**
	 * 父节点编号
	 */
	public String parentId;
	/**
	 * 孩子节点列表
	 */
	//private Children children = new Children();
	private List<TreeNode> children = new ArrayList<TreeNode>();

	// 添加孩子节点
	public void addChild(TreeNode node) {
		//this.children.addChild(node);
		this.children.add(node);
	}

	public List<TreeNode> getChildren() {
		return children;
	}
}

使用递归的方式遍历树并生成json字符串

//使用递归的方式来遍历树
	private void travelByRecursion(TreeNode node, StringBuilder builder) {
		//dealnode
		//System.out.print("{" + "\"id\" : \"" + node.id + "\"" + ", \"text\" : \"" + node.text + "\"");
		builder.append("{" + "\"id\" : \"" + node.id + "\"" + ", \"text\" : \"" + node.text + "\"");
		List<TreeNode> children = node.getChildren();
		if (children != null && !children.isEmpty()) {
			//System.out.print(", \"children\" : [");
			builder.append(", \"children\" : [");
			for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) {
				TreeNode n = it.next();
				travelByRecursion(n, builder);
				if (it.hasNext()) {
					//System.out.print(",");
					builder.append(',');
				}
				
			}
			//System.out.print("]");
			builder.append(']');
		} else {
			//System.out.print(", \"leaf\" : true");
			builder.append(", \"leaf\" : true");
		}
		//System.out.print("}");
		builder.append('}');
	}

使用循环的方式遍历树并生成json字符串

//使用循环的方式来遍历树
	private void travelByCycle(TreeNode node, StringBuilder builder) {
		//定义一个先入后出的栈存放节点信息
		Stack<TreeNode> stack = new Stack<TreeNode>();
		//定义一个先入后出的栈存放节点的闭合符号
		Stack<String> closeSymbolStack = new Stack<String>();
		//定义一个散列表存放同一层节点的最后一个节点的信息(用于判断是否输出逗号),key为node的id,value为上一节点的id
		Map<String, String> latestNode = new HashMap<String, String>();
		stack.push(node);
		TreeNode lpNode;
		//根节点肯定是同一层节点的最后一个节点,所以需要加入散列表
		latestNode.put(node.id, null);
		//int i=0;
		//closeSymbolStack.push("]}");
		//System.out.println(node.id);
		//i++;
		while (!stack.isEmpty()) {
			lpNode = stack.pop();
			//dealnode
			builder.append("{" + "\"id\" : \"" + lpNode.id + "\"" + ", \"text\" : \"" + lpNode.text + "\"");
			List<TreeNode> children = lpNode.getChildren();
			if (children != null && !children.isEmpty()) {
				builder.append(", \"children\" : [");
				boolean firstNode = true;
				TreeNode currentNode = null;
				for (Iterator<TreeNode> it = children.iterator(); it.hasNext();) {
					currentNode = it.next();
					if (firstNode) {
						//栈是先进后出的,所以在同一层级中第一个入栈的节点实际上是最后一个被读取的节点也就是最后一个节点
						//System.out.println(currentNode.id + "," + lpNode.id);
						latestNode.put(currentNode.id, lpNode.id);
						firstNode = false;
					}
					stack.push(currentNode);
				}
				//往闭合符号栈中压入该节点对应的闭合符号
				//判断该节点是否为同一层节点的最后一个节点
				//如果不是同一层节点的最后一个节点则需要多输出一个逗号
				if (latestNode.containsKey(lpNode.id)) {
					//System.out.println(lpNode.id);
					closeSymbolStack.push("]}");
					//i++;
				} else {
					closeSymbolStack.push("]},");
				}
			} else {
				builder.append(", \"leaf\" : true}");
				//判断是滞是否是同一层节点的最后一个叶子节点
				
				if (!latestNode.containsKey(lpNode.id)) {
					//如果不是是同一层节点的最后一个叶子节点则输出逗号
					builder.append(',');
				} else {
					//如果是是同一层节点的最后一个叶子节点则输出闭合符号
					if (!closeSymbolStack.isEmpty()) {
						builder.append(closeSymbolStack.pop());
					}
					//如果是同一层节点的最后一个叶子节点则判断上一节点是否是最后一个节点,如果是则输出闭合符号,循环处理直到上一节点不是同层次的最后一个节点或上一节点为空
					String tempId = lpNode.id;
					String parendNodeId = latestNode.get(tempId);
					while (parendNodeId != null && latestNode.containsKey(parendNodeId)) {
						//System.out.println(tempId + "," + parendNodeId);
						if (!closeSymbolStack.isEmpty()) {
							builder.append(closeSymbolStack.pop());
						}
						tempId = parendNodeId;
						parendNodeId = latestNode.get(tempId);
					}
				}
			}
			
		}
		if (!closeSymbolStack.isEmpty()) {
			for (Iterator<String> it = closeSymbolStack.iterator(); it.hasNext();) {
				builder.append(it.next());
			}
		}
		//System.out.println("i is " + i);
	}
  1. 实现难度的比较

从以上代码可以看出,递归实现的代码非常简洁,天然就是为生成json或xml这种格式的字符串设计的,而通过循环的方式遍历的代码显得比较烦琐,特别是为了实现生成json字符串的功能,使用了两个辅助变量closeSymbolStack和latestNode后显得更加烦琐,说实话为了使用循环的方式来生成json我花了大概一天的时间,而递归只有了几分钟而已。

2. 性能比较

我分别对深度只有4的树形结构进行了几次循环测试,测试结果如下

循环次数递归实现耗时(毫秒)循环实时耗时(毫秒)
50004070
1000070101
100000306394
100000012871638
100000001096313621

然后又分别不同的树的深度进行了测试,测试结果如下

树的深度递归实现耗时(毫秒)循环实时耗时(毫秒)
100025
20001314
3000714
40001219
5000java.lang.StackOverflowError24
10000java.lang.StackOverflowError36
100000java.lang.StackOverflowError475

可以看出,如果树的深度不深的话,使用递归的性能会比使用循环的好一些,但如果树的深度比较深,树的深度超过5000后,使用递归的方式由于调用栈太深导致jvm抛出异常,而使用循环的方式依然正常。

转载于:https://my.oschina.net/u/914897/blog/489149

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值