关于求树中每层的节点总数

关于求树中每层的节点总数

我们用链表来表示树,树也是一种简单的图,同样也可以用二维矩阵来表示。

方案1:基于树的深度优先遍历,进行递归,推荐

方案2:基于树的深度优先遍历,进行迭代。

方案3:基于树的广度优先遍历,进行迭代。


示例树如下图



代码如下,附带详细注释:

package com.collonn.algorithm.tree;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.Set;
import java.util.Stack;

/**
 * 树节点定义,每个节点可以有多个子节点
 */
class TreeLevelNode {
	// 节点值
	private String value;
	// 子节点列表,为了简单,直接用LinkedList
	// 当然,你也可以自定义一个TreeLevelNode链表
	private List<TreeLevelNode> subNodeList = new LinkedList<TreeLevelNode>();

	public TreeLevelNode() {

	}

	public TreeLevelNode(String value) {
		this.value = value;
	}

	public TreeLevelNode(String value, List<TreeLevelNode> subNodeList) {
		this.value = value;
		this.subNodeList = subNodeList;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public List<TreeLevelNode> getSubNodeList() {
		return subNodeList;
	}

	public void setSubNodeList(List<TreeLevelNode> subNodeList) {
		this.subNodeList = subNodeList;
	}

	public void addSub(TreeLevelNode node) {
		this.subNodeList.add(node);
	}

	public void addSub(TreeLevelNode[] nodes) {
		for (TreeLevelNode node : nodes) {
			this.subNodeList.add(node);
		}
	}
}

/**
 * <pre>
 * 示例树如下
 * ***********************************************
 * 						a
 * 			b			c			d
 * 		e			f		g		h
 * 								i		j
 * 	***********************************************
 * </pre>
 */
public class TreeLevelCount {
	// 树的根节点
	private TreeLevelNode root;
	// 每层的节点总数
	private Map<Integer, Integer> levelMap = new HashMap<Integer, Integer>();

	private void initTree() {
		// init all nodes
		TreeLevelNode a = new TreeLevelNode("a");
		TreeLevelNode b = new TreeLevelNode("b");
		TreeLevelNode c = new TreeLevelNode("c");
		TreeLevelNode d = new TreeLevelNode("d");
		TreeLevelNode e = new TreeLevelNode("e");
		TreeLevelNode f = new TreeLevelNode("f");
		TreeLevelNode g = new TreeLevelNode("g");
		TreeLevelNode h = new TreeLevelNode("h");
		TreeLevelNode i = new TreeLevelNode("i");
		TreeLevelNode j = new TreeLevelNode("j");

		// set root
		this.root = a;

		// set subNodes
		a.addSub(new TreeLevelNode[] { b, c, d });

		b.addSub(new TreeLevelNode[] { e });
		c.addSub(new TreeLevelNode[] { f, g });
		d.addSub(new TreeLevelNode[] { h });

		h.addSub(new TreeLevelNode[] { i, j });
	}

	// 打印结果
	private void printLevelCount() {
		System.out.printf("------------------- level count\n");
		for (Entry<Integer, Integer> entry : this.levelMap.entrySet()) {
			System.out.printf("level:%d, count:%d\n", entry.getKey(), entry.getValue());
		}
	}

	// 深度优先遍历,递归方式,统计每层的节点数
	// 简单,高效
	private void dfsRecursiveLevelCount(TreeLevelNode node, int level) {
		System.out.printf("visit, node:%s, level:%d\n", node.getValue(), level);

		// 更新计数
		Integer levelCount = this.levelMap.get(level);
		if (levelCount == null) {
			levelCount = 1;
		} else {
			levelCount++;
		}
		this.levelMap.put(level, levelCount);

		// 对每个子节点进行递归调用
		for (TreeLevelNode subNode : node.getSubNodeList()) {
			dfsRecursiveLevelCount(subNode, level + 1);
		}
	}

	// 深度优先遍历,借用栈(非递归),统计每层的节点数
	// 稍微有点麻烦
	private void dfsStackLevelCount() {
		// 方案1,存储已访问过的节点
		// 方案2,也可以存储某节点已访问过的子节点数,这样可以直接从子节点列表中取出下一个未访问的子节点,这种情况下,用ArrayList存储子节点更高效
		// 方案3,TreeLevelNode中维护一个指针,直接指向同辈中的下一个节点
		Set<String> nodeValueSet = new HashSet<String>();
		// 用来保存当前路径的栈
		Stack<TreeLevelNode> stack = new Stack<TreeLevelNode>();
		// 初始加入root节点
		stack.add(this.root);

		// 根节点的level永远是1,levelCount永远是1
		this.levelMap.put(1, 1);
		// 将root节点加入已访问
		nodeValueSet.add(this.root.getValue());
		System.out.printf("visit, node:%s, level:%d\n", this.root.getValue(), 1);

		out: while (!stack.isEmpty()) {
			// 访问栈顶元素,但不弹出
			TreeLevelNode topNode = stack.peek();

			// 当其没有子节点时,弹出栈顶元素
			if (topNode.getSubNodeList().size() == 0) {
				stack.pop();
				continue;
			}

			// 寻找一个该节点的未访问的子节点
			for (TreeLevelNode nod : topNode.getSubNodeList()) {
				if (!nodeValueSet.contains(nod.getValue())) {
					// 如果子节点没有被访问过,则加入栈顶
					stack.push(nod);
					// 将该节点已访问
					nodeValueSet.add(nod.getValue());
					System.out.printf("visit, node:%s, level:%d\n", nod.getValue(), stack.size());

					// 注意,栈的当前大小,就是栈顶元素在树中的层次
					// 更新level的统计总数
					Integer count = this.levelMap.get(stack.size());
					if (count == null) {
						count = 1;
					} else {
						count++;
					}
					this.levelMap.put(stack.size(), count);

					// 断续深度遍历,相当于调用了递归方法
					continue out;
				}
			}

			// 如果该节点的所有孩子节点都访问过,则弹出栈顶元素
			stack.pop();
		}
	}

	// 广度优先遍历,借用队列,统计每层的节点数
	// 稍微有点麻烦
	private void bfsLevelCount() {
		// 存储元素的层级数
		Map<String, Integer> nodeLevelMap = new HashMap<String, Integer>();
		// 队列
		Queue<TreeLevelNode> queue = new LinkedList<TreeLevelNode>();

		// 初始加入root节点
		queue.offer(this.root);
		// 根节点的level永远是1,levelCount永远是1
		this.levelMap.put(1, 1);
		nodeLevelMap.put(this.root.getValue(), 1);
		System.out.printf("visit, node:%s, level:%d\n", this.root.getValue(), 1);

		while (!queue.isEmpty()) {
			// 访问队列头,并删除
			TreeLevelNode head = queue.poll();

			// 将子节点依次加入到队列尾
			for (TreeLevelNode nd : head.getSubNodeList()) {
				queue.offer(nd);

				// 子节点所在的层次为:父节点层级加1
				int levelCurrent = nodeLevelMap.get(head.getValue()) + 1;
				// 记录节点的层级
				nodeLevelMap.put(nd.getValue(), levelCurrent);
				System.out.printf("visit, node:%s, level:%d\n", nd.getValue(), levelCurrent);

				// 更新level的统计总数
				Integer count = this.levelMap.get(levelCurrent);
				if (count == null) {
					count = 1;
				} else {
					count++;
				}
				this.levelMap.put(levelCurrent, count);
			}
		}
	}

	public static void main(String[] args) {
		TreeLevelCount tlc = new TreeLevelCount();
		tlc.initTree();

		System.out.printf("------------------- 深度优先遍历,递归求解 -------------------\n");
		tlc.levelMap.clear();
		tlc.dfsRecursiveLevelCount(tlc.root, 1);
		tlc.printLevelCount();

		System.out.printf("------------------- 深度优先遍历,迭代求解 -------------------\n");
		tlc.levelMap.clear();
		tlc.dfsStackLevelCount();
		tlc.printLevelCount();

		System.out.printf("------------------- 广度优先遍历 -------------------\n");
		tlc.levelMap.clear();
		tlc.bfsLevelCount();
		tlc.printLevelCount();
	}

}



代码执行结果如下:

------------------- 深度优先遍历,递归求解 -------------------
visit, node:a, level:1
visit, node:b, level:2
visit, node:e, level:3
visit, node:c, level:2
visit, node:f, level:3
visit, node:g, level:3
visit, node:d, level:2
visit, node:h, level:3
visit, node:i, level:4
visit, node:j, level:4
------------------- level count
level:1, count:1
level:2, count:3
level:3, count:4
level:4, count:2
------------------- 深度优先遍历,迭代求解 -------------------
visit, node:a, level:1
visit, node:b, level:2
visit, node:e, level:3
visit, node:c, level:2
visit, node:f, level:3
visit, node:g, level:3
visit, node:d, level:2
visit, node:h, level:3
visit, node:i, level:4
visit, node:j, level:4
------------------- level count
level:1, count:1
level:2, count:3
level:3, count:4
level:4, count:2
------------------- 广度优先遍历 -------------------
visit, node:a, level:1
visit, node:b, level:2
visit, node:c, level:2
visit, node:d, level:2
visit, node:e, level:3
visit, node:f, level:3
visit, node:g, level:3
visit, node:h, level:3
visit, node:i, level:4
visit, node:j, level:4
------------------- level count
level:1, count:1
level:2, count:3
level:3, count:4
level:4, count:2


原创博文,转载请注明出处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值