嘤嘤嘤,两个月没写博客了,由于有点忙,今天开始日更博客。
今天总结一下学习树的先根,中根,后根。每种两种方法,递归和非递归。
先根:
递归:
思路:先根遍历,即第一次遇到的结点就开始打印。先一直遍历左子树,直到未空,然后右子树,直到为空。递归下去。
过程:先将1进入方法,2,3执行方法,2先执行,在2中将4,5执行方法,直到发现4无子树,然后轮到5执行方法。直到发现5没有子树,此时2方法结束,轮到3执行方法。。。。。。懂我的意思吧。
private void preorder(BinaryNode<T> p) { //根节点
if (p != null) {
System.out.print(p.data.toString() + " ");//打印结点数值
preorder(p.left); //打印当前结点的左子树
preorder(p.right); //打印当前结点的右子树
}
}
非递归:
思路:还是先根遍历,遇到结点就压入栈中,每当发现某个结点无子树,将结点设置为从栈中弹出元素。如果不为空就打印。
过程:先将1压入栈中,然后让结点=1.left,1的左子树=2,发现2不为空压入栈中,继续。。。当发现4无子树时,从栈中弹出2,使得p=2.right=5.此时栈中只有1,等发现5没有子树时,p=弹栈=1.。。。。。你懂我意思吧。
上代码:
public void preorderTraverse() {
LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
BinaryNode<T> p = this.root;
while (p != null || !stack.isEmpty()) {
if (p != null) {
System.out.print(p.data + " "); // 先根遍历
stack.push(p);
p = p.left;
} else {
System.out.print("^ ");
p = stack.pop();
p = p.right;
}
}
}
中根:
递归:
思路:开始从跟结点遍历,当第二次遍历到该结点时打印。如何区分是第二次了,在递归中只需要在左右子树中间打印即可。因为但遍历完左子树时,回到父母结点时为第二次遍历到父母结点,第一次为通过父母接到遍历到孩子结点时。
过程:从1开始进入遍历方法,发现1不为空,将2,3进入遍历方法。由于2先开始,此时3一直在等待2的结束。2发现4.5不是空,将4,5进入遍历方法,此时没有打印2.当4结束时在通过2进入5之前打印此时结点,即为中根遍历。
上代码:
protected void inorder(BinaryNode<T> p) { //p为根结点
if (p != null) {
inorder(p.left);
System.out.print(p.data.toString() + " ");
inorder(p.right);
}
}
非递归:
思路:参考先根遍历中的非递归方法。先根遍历是在第一次遇到非空的时候打印。中根稍有区别,在第一次非空时入栈,不打印,当发现子树为空时,弹栈时回到空子树的父母结点时,打印,此时为第二次访问,即中根遍历。
过程:1开始进入while循环,发现1.left=2不为空入栈,此时不要打印,然后4,进入循环,入栈,发现4左子树为空,此时弹栈,弹出4,立即打印,达到中根遍历。其余相似。
上代码:
public void inorderTraverse() {
LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
BinaryNode<T> p = this.root;
BinaryNode<T> q1 = null;
BinaryNode<T> q2;
boolean flag = true;
while (p != null || !stack.isEmpty()) {
if (p != null) {
stack.push(p);
p = p.left;
} else {
System.out.print("^ ");
p = stack.pop();
System.out.print(p.data + " "); // 中跟遍历
p = p.right;
}
}
}
后跟:这是又臭又硬的一根
递归:
思路:现遍历子树,等左右子树都遍历完后,在打印结点。跟之前的类比一下很容易想到。
过程:1进入递归,发现2,3非空,然后2进入递归,3等待,2又发现4,5非空,此时4进入递归,5等待,然后4发现左右子树都为空,此时不再进行递归,打印4结点的值。懂我意思吧。
上代码:
public void inorderTraverse() {
LinkedStack<BinaryNode<T>> stack = new LinkedStack<BinaryNode<T>>();
BinaryNode<T> p = this.root;
BinaryNode<T> q1 = null;
BinaryNode<T> q2;
boolean flag = true;
while (p != null || !stack.isEmpty()) {
if (p != null) {
stack.push(p);
p = p.left;
} else {
System.out.print("^ ");
p = stack.pop();
System.out.print(p.data + " "); // 中跟遍历
p = p.right;
}
}
}
非递归:
思路:由于需要确定当前结点被访问过两次,此时需要额外一个结点来储存之前访问的结点。符合打印条件的就一共两种情况,一是无子结点,直接打印。二是当前访问的结点其右子树已被访问,或者右子树不存子,左子树被访问过。
过程:懒得写了,一定要注意,先将右子树入栈,再将左子树入栈,因为访问时是现访问左子树,在访问右子树,栈为后进现出结构!!!!!
上代码:
public void postorderTraverse() {
LinkedStack<BinaryNode> stack = new LinkedStack<BinaryNode>();
BinaryNode cur, pre = null; //pre为额外结点,记录前一个结点,用于打印
stack.push(this.root);
while (!stack.isEmpty()) {
cur = stack.peek();
if ((cur.left == null && cur.right == null)
|| (pre != null && (cur.left == pre || cur.right == pre))) { //判断是否为上述条件决定是否打印
BinaryNode temp = stack.pop();
System.out.print(temp.data + " ");
pre = temp;
} else {
if (cur.right != null) { //右结点入栈
stack.push(cur.right);
}
if (cur.left != null) {
stack.push(cur.left);
}
}
}
}