1 树
树,顾名思义,长得像一棵树,不过通常我们画成一棵倒过来的树,根在上,叶在下。
1.1 树的一些概念
树还可以表示成下面这种方式:
从上图可以看出:树可以用来描述包含关系,但包含关系不得出现交叉重叠区域,否则就不能用树描述。
2 树的遍历
2.1 广度优先遍历
对于树的广度优先遍历,可以使用队列来完成。
示例:
树节点类:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public
class
Node {
private
String name;
private
List<Node> childs;
public
Node(String name) {
super ();
this .name = name;
}
public
String getName() {
return
name;
}
public
List<Node> getChilds() {
return
childs;
}
public
void
setChilds(List<Node> childs) {
this .childs = childs;
}
}
|
测试代码:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
public
class
Test {
public
static
void
main(String[] args) {
// 构造节点
Node a = new
Node( "A" );
Node b = new
Node( "B" );
Node c = new
Node( "C" );
Node d = new
Node( "D" );
Node e = new
Node( "E" );
Node f = new
Node( "F" );
Node g = new
Node( "G" );
Node h = new
Node( "H" );
Node i = new
Node( "I" );
Node j = new
Node( "J" );
Node k = new
Node( "K" );
Node l = new
Node( "L" );
// 构造树
a.setChilds(Arrays.asList(b, c, d));
b.setChilds(Arrays.asList(e, f));
d.setChilds(Arrays.asList(g, h, i));
f.setChilds(Arrays.asList(j, k));
h.setChilds(Arrays.asList(l));
// 创建队列
Queue<Node> que = new
ArrayDeque<Node>();
que.offer(a);
// 操作队列
while
(!que.isEmpty()) {
Node temp = que.poll();
System.out.print(temp.getName());
List<Node> childs = temp.getChilds();
if
(childs != null ) {
for
( int
iter = 0 ; iter < childs.size(); iter++) {
que.offer(childs.get(iter));
}
}
}
}
}
|
输出结果
?
1
|
ABCDEFGHIJKL
|
队列的状况如下图:
2.2 深度优先遍历
深度有限遍历分为:前序遍历(Preorder Traversal),后序遍历(Postorder Traversal)和中序遍历(Inorder Traversal)。
其中中序遍历只有对二叉树才有意义。
树:
2.2.1 前序遍历:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
package
com.demo2;
import
java.util.List;
//树节点
public
class
Node {
private
String name;
private
List<Node> childs;
public
Node(String name) {
super ();
this .name = name;
}
public
String getName() {
return
name;
}
public
List<Node> getChilds() {
return
childs;
}
public
void
setChilds(List<Node> childs) {
this .childs = childs;
}
@Override
public
String toString() {
return
this .name;
}
}
|
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package
com.demo2;
import
java.util.ArrayDeque;
import
java.util.Arrays;
import
java.util.Deque;
import
java.util.List;
//测试类
public
class
Test {
public
static
void
main(String[] args) {
// 构造节点
Node a = new
Node( "A" );
Node b = new
Node( "B" );
Node c = new
Node( "C" );
Node d = new
Node( "D" );
Node e = new
Node( "E" );
Node f = new
Node( "F" );
Node g = new
Node( "G" );
Node h = new
Node( "H" );
Node i = new
Node( "I" );
Node j = new
Node( "J" );
Node k = new
Node( "K" );
Node l = new
Node( "L" );
// 构造树
a.setChilds(Arrays.asList(b, c, d));
b.setChilds(Arrays.asList(e, f));
d.setChilds(Arrays.asList(g, h, i));
f.setChilds(Arrays.asList(j, k));
h.setChilds(Arrays.asList(l));
// 创建栈
Deque<Node> que = new
ArrayDeque<Node>();
que.push(a);
// 前序遍历
System.out.println( "前序遍历" );
while
(!que.isEmpty()) {
Node nodeTemp = que.pop();
System.out.print(nodeTemp.getName());
List<Node> childs = nodeTemp.getChilds();
if
(childs != null ) {
for
( int
iter = childs.size() - 1 ; iter >= 0 ; iter--) {
que.push(childs.get(iter));
}
}
}
}
}
|
输出结果:
?
1
2
|
前序遍历
ABEFJKCDGHLI
|
2.2.2 后序遍历:
后序遍历需要标识“节点是否已经将它的子节点入栈”。因此,在树节点类Node中添加了一个属性boolean mark,mark为true时,表示该节点已经将它的子节点入栈;false表示还没有将该节点的子节点入栈。
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
package
com.demo3;
import
java.util.List;
//树节点类
public
class
Node {
private
String name;
private
List<Node> childs;
private
boolean
mark;
public
Node(String name) {
super ();
this .name = name;
this .mark = false ;
}
public
String getName() {
return
name;
}
public
List<Node> getChilds() {
return
childs;
}
public
void
setChilds(List<Node> childs) {
this .childs = childs;
}
public
boolean
isMark() {
return
mark;
}
public
void
setMark( boolean
mark) {
this .mark = mark;
}
@Override
public
String toString() {
return
this .name;
}
}
|
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
package
com.demo3;
import
java.util.ArrayDeque;
import
java.util.Arrays;
import
java.util.Deque;
import
java.util.List;
//测试
public
class
Test {
public
static
void
main(String[] args) {
// 构造节点
Node a = new
Node( "A" );
Node b = new
Node( "B" );
Node c = new
Node( "C" );
Node d = new
Node( "D" );
Node e = new
Node( "E" );
Node f = new
Node( "F" );
Node g = new
Node( "G" );
Node h = new
Node( "H" );
Node i = new
Node( "I" );
Node j = new
Node( "J" );
Node k = new
Node( "K" );
Node l = new
Node( "L" );
// 构造树
a.setChilds(Arrays.asList(b, c, d));
b.setChilds(Arrays.asList(e, f));
d.setChilds(Arrays.asList(g, h, i));
f.setChilds(Arrays.asList(j, k));
h.setChilds(Arrays.asList(l));
// 创建栈
Deque<Node> que = new
ArrayDeque<Node>();
que.push(a);
// 后序遍历
System.out.println( "后序遍历" );
while
(!que.isEmpty()) {
Node nodeTemp = que.peek();
if
(nodeTemp.isMark()) {
Node node = que.pop();
System.out.print(node.getName());
} else
{
List<Node> childs = nodeTemp.getChilds();
if
(childs != null ) {
for
( int
iter = childs.size() - 1 ; iter >= 0 ; iter--) {
que.push(childs.get(iter));
}
}
nodeTemp.setMark( true );
}
}
}
}
|
输出结果:
?
1
2
|
后序遍历
EJKFBCGLHIDA
|
2.2.3 二叉树的中序遍历
根据中序遍历的顺序,对于任一结点,优先访问其左孩子,而左孩子结点又可以看做一根结点,然后继续访问其左孩子结点,直到遇到左孩子结点为空的结点才进行访问,然后按相同的规则访问其右子树。因此其处理过程如下:
对于任一结点P,
1)若其左孩子不为空,则将P入栈并将P的左孩子置为当前的P,然后对当前结点P再进行相同的处理;
2)若其左孩子为空,则取栈顶元素并进行出栈操作,访问该栈顶结点,然后将当前的P置为栈顶结点的右孩子;
3)直到P为NULL并且栈为空则遍历结束
二叉树
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
//二叉树节点类
public
class
Node {
private
String name;
private
Node left;
private
Node right;
public
Node(String name) {
super ();
this .name = name;
}
public
String getName() {
return
name;
}
public
Node getLeft() {
return
left;
}
public
void
setLeft(Node left) {
this .left = left;
}
public
Node getRight() {
return
right;
}
public
void
setRight(Node right) {
this .right = right;
}
@Override
public
String toString() {
return
this .name;
}
}
|
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
import
java.util.ArrayDeque;
import
java.util.Deque;
//测试
public
class
Test {
public
static
void
main(String[] args) {
// 构造节点
Node a = new
Node( "A" );
Node b = new
Node( "B" );
Node c = new
Node( "C" );
Node d = new
Node( "D" );
Node e = new
Node( "E" );
Node f = new
Node( "F" );
Node g = new
Node( "G" );
Node h = new
Node( "H" );
Node i = new
Node( "I" );
Node j = new
Node( "J" );
// 构造树
a.setLeft(b);
a.setRight(c);
b.setLeft(d);
b.setRight(e);
c.setLeft(f);
c.setRight(g);
e.setLeft(h);
e.setRight(i);
g.setLeft(j);
// 创建栈
Deque<Node> que = new
ArrayDeque<Node>();
Node currNode = a;
// 中序遍历算法
System.out.println( "中序遍历:" );
while
(currNode != null
|| !que.isEmpty()) {
while
(currNode != null ) {
que.push(currNode);
currNode = currNode.getLeft();
}
currNode = que.pop();
System.out.print(currNode.getName());
currNode = currNode.getRight();
}
}
}
|
输出结果:
?
1
2
|
中序遍历:
DBHEIAFCJG
|