前面在回顾设计模式和数据结构时,遇到二叉树不同打印方式需求,于是想着能不能把二叉树的不同遍历方法独立出来,作为多个单独的打印方法来方便使用,然后想到了策略模式,于是有了下面的内容,以作记录和分享。若有不足,还请多多指正。
定义接口
第一个接口:定义策略接口。用于二叉树不同遍历方法的实现,作为不同的策略,例如:前序、中序、后序、层序遍历。
public interface Shower {
//解决:如果Node节点是在数据结构内部,不对外暴露怎么办?
void show(NodeInter node);
}
第二个接口:用于解决二叉树中Node是在二叉树中内部定义,不对外暴露的情况。只是这里有个要求就是内部的Node去实现下这个接口。
public interface NodeInter<T, V> {
NodeInter<T, V> getRight();
NodeInter<T, V> getLeft();
}
第三个接口:定义一个二叉树打印的接口
public interface STInterf {
void show(Shower shower);
}
定义使用到的普通的二叉树,如下:
public class ST<Key extends Comparable, Value> implements STInterf {
private Node root;
private class Node implements NodeInter<Key, Value> {
private Key key;
private Value value;
private Node left, right;
public Node(Key key, Value value) {
this.key = key;
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
'}';
}
@Override
public NodeInter<Key, Value> getRight() {
return right;
}
@Override
public NodeInter<Key, Value> getLeft() {
return left;
}
}
@Override
public void show(Shower shower) {
shower.show(root);
}
public void put(Key key, Value value) {
root = put(root, key, value);
}
private Node put(Node node, Key key, Value value) {
if (node == null) return new Node(key, value);
int cmp = key.compareTo(node.key);
if (cmp < 0) node.left = put(node.left, key, value);
else if (cmp > 0) node.right = put(node.right, key, value);
//存在键,则更新值
else node.value = value;
return node;
}
}
二叉树的遍历
下面是策略接口的具体实现。
前序
public class PreShowerImpl implements Shower {
@Override
public void show(NodeInter node) {
if (null ==node) return;
System.out.println(node);
show(node.getLeft());
show(node.getRight());
}
}
中序
public class MidShowerImpl implements Shower {
@Override
public void show(NodeInter node) {
if (null ==node) return;
show(node.getLeft());
System.out.println(node);
show(node.getRight());
}
}
后续
public class AfterShowerImpl implements Shower {
@Override
public void show(NodeInter node) {
if (null ==node) return;
show(node.getLeft());
show(node.getRight());
System.out.println(node);
}
}
层序
public class LevelShowerImpl implements Shower {
/**
* 使用了两个队列
*/
@Override
public void show(NodeInter root) {
if (root == null) return;
//使用队列
Queue<NodeInter> queue = new LinkedQueue<>();
//层队列
Queue<NodeInter> levelQueue = new LinkedQueue<>();
//判断是否是同一个节点下的右节点,否则是左节点
boolean isRight = false;
//判断是否是在同一个节点先的左右节点中间
boolean inner = false;
//根节点入队列
queue.enqueue(root);
while (!queue.isEmpty() || !levelQueue.isEmpty()) {
//打印一层节点,并加他们的下一层节点入队到层队列
while (!queue.isEmpty()) {
//节点出队列
NodeInter tmp = queue.dequeue();
//辨别节点间的分隔符
if (tmp == null) {
System.out.printf("%s ", isRight ? "], " : "[");
isRight = isRight ? false : true;
if (isRight) {
inner = true;
}
continue;
}
System.out.printf("%s ", tmp);
//根据是否是同一个节点的左右子树,来将左右子树分开
if (inner) {
//并不能很好区分左右
System.out.print("\t");
inner = false;
}
if (tmp.getLeft() != null || tmp.getRight() != null) {
levelQueue.enqueue(null);
}
//将左右子节点入队
if (tmp.getLeft() != null) levelQueue.enqueue(tmp.getLeft());
if (tmp.getRight() != null) levelQueue.enqueue(tmp.getRight());
//对于叶节点,不再对其左右子树进行划分
//在兄弟节点的子节点间,添加分割符,以用于打印分割
if (tmp.getLeft() != null || tmp.getRight() != null) {
levelQueue.enqueue(null);
}
}
System.out.println("\n----------分层------------");
//将同一层节点放到总队列中,以便出队打印
while (!levelQueue.isEmpty()) {
//出栈
NodeInter pop = levelQueue.dequeue();
//入队
queue.enqueue(pop);
}
}
}
}
测试
public class Client {
ST<Integer, Integer> st = new ST();
@Before
public void init() {
int num = 10;
Random random = new Random();
for (int i = 0; i < num; i++) {
st.put(random.nextInt(20), random.nextInt(100));
}
}
@Test
public void test() {
//st.show();
Shower shower = new PreShowerImpl();
Shower shower1 = new MidShowerImpl();
Shower shower2 = new AfterShowerImpl();
st.show(shower);
System.out.println("----------分割线------------");
st.show(shower1);
System.out.println("----------分割线------------");
st.show(shower2);
System.out.println("----------分割线------------");
st.show(new LevelShowerImpl());
}
}
结果:
Node{key=18, value=63}
Node{key=7, value=41}
Node{key=2, value=17}
Node{key=0, value=68}
Node{key=6, value=57}
Node{key=3, value=89}
Node{key=8, value=95}
Node{key=12, value=52}
----------分割线------------
Node{key=0, value=68}
Node{key=2, value=17}
Node{key=3, value=89}
Node{key=6, value=57}
Node{key=7, value=41}
Node{key=8, value=95}
Node{key=12, value=52}
Node{key=18, value=63}
----------分割线------------
Node{key=0, value=68}
Node{key=3, value=89}
Node{key=6, value=57}
Node{key=2, value=17}
Node{key=12, value=52}
Node{key=8, value=95}
Node{key=7, value=41}
Node{key=18, value=63}
----------分割线------------
Node{key=18, value=63}
----------分层------------
[ Node{key=7, value=41} ],
----------分层------------
[ Node{key=2, value=17} Node{key=8, value=95} ],
----------分层------------
[ Node{key=0, value=68} Node{key=6, value=57} ], [ Node{key=12, value=52} ],
----------分层------------
[ Node{key=3, value=89} ],
----------分层------------
Process finished with exit code 0