树状图广度优先和深度优先

552 阅读2分钟

这是我参与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;

}