这是我参与8月更文挑战的第21天,活动详情查看:8月更文挑战
树状图广度优先和深度优先
本来想取名字叫
树状图的遍历,你们肯定会想,这么简单的问题还需要拿出来研究吗?这遍文章用多种角度讲解树状图的遍历,深度优先、广度优先、递归转循环等,你大概率不会。解析树状图的时候,递归算是比较好理解的方式,但是递归在遍历大量的树状图数据时,反而会引起栈溢出,此时就需要转循环了。
一、什么是广度、深度优先
字面意思,广度优先就是先走树状图的宽度,横着的数据;深度优先就是优先走树状图的深度,上下最低部的数据。看看流程图会更好理解。
广度优先
按顺序输出:1,2,3,4,5,6,7,8,9
深度优先
按顺序输出:1,2,4,5,6,3,7,8,9
graph TD
1[1] -->2[2]
1[1] -->3[3]
2[2] -->4[4]
2[2] -->5[5]
2[2] -->6[6]
3[3] -->7[7]
3[3] -->8[8]
3[3] -->9[9]
二、遍历
2.1 模拟数据
通过上面的流程图,我们可以模拟出以下Json,然后用Gson转换便可以得到这个树状图对象。
{
"name": "1",
"child": [
{
"name": "2",
"child": [
{
"name": "4",
"child": [
]
},
{
"name": "5",
"child": [
]
},
{
"name": "6",
"child": [
]
}
]
},
{
"name": "3",
"child": [
{
"name": "7",
"child": [
]
},
{
"name": "8",
"child": [
]
},
{
"name": "9",
"child": [
]
}
]
}
]
}
2.3 转对象
我们把上诉的Json字符串用Gson转化成对象,类名叫Person,它是这样的结构。
public class Person {
private String name;
private List<Person> child;
}
public class Human {
private String name;
private int depth;
private List<Human> child;
}
public class DepthPerson extends Person {
private int depth;
}
2.2 普通递归遍历(深度优先)
拿到这样的数据,大部分人第一反应就是递归,递归需要深度,也是为什麽深度优先用递归实现最容易,因为你不需要自己用循环维护一个栈。
按照递归思想,可以一层走到底,再回来将另一层走到底,比较好理解。
那么看看递归遍历怎么实现呢。
private void printPerson(Person person) {
Log.d(TAG, person.getName());
if (person.getChild() != null && person.getChild().size() != 0) {
for (int i = 0; i < person.getChild().size(); i++) {
printPerson(person.getChild().get(i));
}
}
}
// 输出:1、2、4、5、6、3、7、8、9
2.3 深度优先-循环
说实话,以前我并不知道递归可以转循环,而是遇到了一个递归爆栈的问题,不得已才转循环。
那么递归转循环是怎么实现呢?
我们需要用到栈结构+while循环,创建出来的栈是需要保存临时的对象,栈结构的特性是先进先出,将最左侧的孩子先放入栈,就能达到先输出的目的。
看代码
private void printPerson(Person rooPerson) {
Stack<Person> stack = new Stack<>();
stack.push(rooPerson);
while (!stack.isEmpty()) {
Person person = stack.pop();
Log.d(TAG, person.getName());
if (person.getChild() != null && person.getChild().size() != 0) {
for (int i = person.getChild().size() - 1; i >= 0; i--) {
stack.push(person.getChild().get(i));
}
}
}
}
// 输出:1、2、4、5、6、3、7、8、9
2.4 广度优先-循环
来看看广度优先,用List作为临时集合,用while作为循环,在循环中添加该组件的孩子们。
private void printPerson(Person rooPerson) {
List<Person> personList = new ArrayList<>();
personList.add(rooPerson);
while (personList.size() != 0) {
Person person = personList.remove(0);
if (person == null) continue;
Log.d(TAG, person.getName());
if (person.getChild() != null && person.getChild().size() != 0) {
personList.addAll(person.getChild());
}
}
}
// 输出:1、2、3、4、5、6、7、8、9
2.5 创建新树并加入深度属性
private void printPerson6(Person rooPerson) {
DepthPerson rootDepthPerson = new DepthPerson();
rootDepthPerson.setChild(rooPerson.getChild());
rootDepthPerson.setIndex(rooPerson.getIndex());
rootDepthPerson.setName(rooPerson.getName());
rootDepthPerson.setDepth(1); // 初始 1
List<DepthPerson> depthPersonList = new ArrayList<>();
depthPersonList.add(rootDepthPerson);
while (depthPersonList.size() != 0) {
DepthPerson depthPerson = depthPersonList.remove(0);
if (depthPerson == null) continue;
Log.d(TAG, depthPerson.getName() + " 深度:" + depthPerson.getDepth());
if (depthPerson.getChild() != null && depthPerson.getChild().size() != 0) {
for (int i = 0; i < depthPerson.getChild().size(); i++) {
Person person = depthPerson.getChild().get(i);
DepthPerson temp = new DepthPerson();
temp.setChild(person.getChild());
temp.setIndex(person.getIndex());
temp.setName(person.getName());
temp.setDepth(depthPerson.getDepth() + 1);
depthPersonList.add(temp);
}
}
}
}
2.6 生成新的树状图对象(递归)
private Human printPerson4(Person rooPerson) {
Human rootHuman = new Human();
rootHuman.setName(rooPerson.getName());
if (rooPerson.getChild() != null && rooPerson.getChild().size() != 0) {
List<Human> humanList = new ArrayList<>();
for (int i = 0; i < rooPerson.getChild().size(); i++) {
humanList.add(printPerson4(rooPerson.getChild().get(i)));
}
rootHuman.setChild(humanList);
}
return rootHuman;
}
2.7 生成新的树状图对象(循环)
private static Human printPerson5(Person rooPerson) {
List<Person> personList = new ArrayList<>();
List<Human> humanList = new ArrayList<>();
Human rootHuman = new Human();
personList.add(rooPerson);
humanList.add(rootHuman);
while (personList.size() != 0) {
Person person = personList.remove(0);
Human human = humanList.remove(0);
if (person == null) continue;
human.setName(person.getName());
//System.out.println(person.getName());
if (person.getChild() != null && person.getChild().size() != 0) {
personList.addAll(person.getChild());
List<Human> list = new ArrayList<>();
for (int i = 0; i < person.getChild().size(); i++) {
Human h = new Human();
list.add(h);
}
humanList.addAll(list);
human.setChild(list);
}
}
return rootHuman;
}