java二叉树的遍历,递归与非递归方法
2017-09-22 15:41
453 查看
定义:
1.二叉树是n(n >= 0)个节点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。
2.所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树,线性表结构可以理解为斜树。
3.在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子都在同一层上,称为满二叉树。
4.对一棵具有n个节点的二叉树按层序编号,如果编号为i(1 <= i <= n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
二叉树的相关性质:
性质1:在二叉树的第i层上至多有2i-1个结点(i>=1)。
性质2:深度为k的二叉树至多有2k-1个结点(k>=1)。
性质3:对任何一颗二叉树T,如果其终端结点数为n0,度为2的 结点 数为n2,则n0 = n2+1.
性质4:具有n个结点的完全二叉树深度为[log2(n+1)]([x]表示不 大于 x的最大整数)。
性质5:如果对一颗有n个结点的完全二叉树(其深度为[log2(n+1)]) 的结点按层序编号(从第1层到第[log2(n+1)]层,每层从左到 右),对任意一个结点i(1<=i<=n)有:
1).如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结 点[i/2]
2).如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩 子是结点2i。
3).如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。
好了不废话了,接下来看算法吧,二叉树的遍历分为深度遍历和广度遍历,其中深度遍历又分为前序、中序、后序遍历,而这三种遍历我们熟知的实现方法是两种一种是递归方式一种是非递归方式,接下来我们一个一个说明思路。
递归方式:
1.前序遍历:
思路:先传入树的根节点,如果被遍历的节点为空则返回,否则打印该节点,然后遍历左节点,递归调用该方法,直到节点为空返回,遍历右节点,再递归调用该方法,直到节点为空返回。
代码:
2.中序遍历:
思路:先传入树的根节点,如果该节点为空则返回,否则递归调用该方法遍历左节点,直到为空返回,打印该节点内容,递归调用该方法遍历右节点,直到为空返回。
3.后序遍历:
思路:传入树的根节点,如果该节点为空则返回,否则递归调用该方法遍历左孩子,直到为空返回,递归调用该方法遍历右孩子,直到为空返回,打印该节点内容。
非递归方式(利用栈):
1.前序遍历:
思路:创建空栈,先把根节点压栈,然后抛栈打印当前节点,如果当前节点的右孩子不为空,则压栈,左孩子不为空则压栈,while循环以上步骤,直到栈为空(因为栈是后进先出,所以先压右孩子,再压左孩子实现左孩子先出栈)。
2.中序遍历:
思路:首先创建空栈,根节点压栈,然后抛栈获得一个节点,如果该节点的右孩子不为空则压栈,该节点自己再次压栈,如果该节点的左孩子不为空则压栈,否则抛栈(左孩子为空则打印当前节点),判断栈是不为空并且栈顶节点没有孩子,则抛栈,判断栈不为空且栈中包含当前节点的右孩子则抛栈,循环上面的方法直到栈为空。
代码:
3.后序遍历:
思路:创建两个栈,一个用来操作节点,一个用来记录节点轨迹,根节点入栈,然后判断当前栈顶节点的左孩子被轨迹栈包含,或者当前节点的右孩子被轨迹栈包含,或者当前节点没有孩子,则抛栈,否则如果右孩子不为空则,右孩子压栈,如果左孩子不为空则左孩子压栈,循环以上方法,直到栈为空。
代码:
接下来是完整的java代码,如果树节点的结构体不会建立可以看看:
1.二叉树是n(n >= 0)个节点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根节点和两棵互不相交的、分别称为根节点的左子树和右子树的二叉树组成。
2.所有的结点都只有左子树的二叉树叫左斜树。所有结点都是只有右子树的二叉树叫右斜树。这两者统称为斜树,线性表结构可以理解为斜树。
3.在一棵二叉树中,如果所有分支节点都存在左子树和右子树,并且所有叶子都在同一层上,称为满二叉树。
4.对一棵具有n个节点的二叉树按层序编号,如果编号为i(1 <= i <= n)的节点与同样深度的满二叉树中编号为i的节点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
二叉树的相关性质:
性质1:在二叉树的第i层上至多有2i-1个结点(i>=1)。
性质2:深度为k的二叉树至多有2k-1个结点(k>=1)。
性质3:对任何一颗二叉树T,如果其终端结点数为n0,度为2的 结点 数为n2,则n0 = n2+1.
性质4:具有n个结点的完全二叉树深度为[log2(n+1)]([x]表示不 大于 x的最大整数)。
性质5:如果对一颗有n个结点的完全二叉树(其深度为[log2(n+1)]) 的结点按层序编号(从第1层到第[log2(n+1)]层,每层从左到 右),对任意一个结点i(1<=i<=n)有:
1).如果i=1,则结点i是二叉树的根,无双亲;如果i>1,则其双亲是结 点[i/2]
2).如果2i>n,则结点i无左孩子(结点i为叶子结点);否则其左孩 子是结点2i。
3).如果2i+1>n,则结点i无右孩子;否则其右孩子是结点2i+1。
好了不废话了,接下来看算法吧,二叉树的遍历分为深度遍历和广度遍历,其中深度遍历又分为前序、中序、后序遍历,而这三种遍历我们熟知的实现方法是两种一种是递归方式一种是非递归方式,接下来我们一个一个说明思路。
递归方式:
1.前序遍历:
思路:先传入树的根节点,如果被遍历的节点为空则返回,否则打印该节点,然后遍历左节点,递归调用该方法,直到节点为空返回,遍历右节点,再递归调用该方法,直到节点为空返回。
代码:
/**递归方法,前序遍历树,前序遍历是根左右*/ public static void preOrder(TreeNode node) { if(node == null) { return; } System.out.print(node.data+"-->"); preOrder(node.leftChild); preOrder(node.rightChild); }
2.中序遍历:
思路:先传入树的根节点,如果该节点为空则返回,否则递归调用该方法遍历左节点,直到为空返回,打印该节点内容,递归调用该方法遍历右节点,直到为空返回。
/**递归方法,中序遍历树, 左根右*/ public static void midOrder(TreeNode node) { if(node == null) return; midOrder(node.leftChild); System.out.print(node.data+"-->"); midOrder(node.rightChild); }
3.后序遍历:
思路:传入树的根节点,如果该节点为空则返回,否则递归调用该方法遍历左孩子,直到为空返回,递归调用该方法遍历右孩子,直到为空返回,打印该节点内容。
/**递归方法,后序遍历树,左右根*/ public static void postOrder(TreeNode node) { if(node == null) return; postOrder(node.leftChild); postOrder(node.rightChild); System.out.print(node.data+"-->"); }
非递归方式(利用栈):
1.前序遍历:
思路:创建空栈,先把根节点压栈,然后抛栈打印当前节点,如果当前节点的右孩子不为空,则压栈,左孩子不为空则压栈,while循环以上步骤,直到栈为空(因为栈是后进先出,所以先压右孩子,再压左孩子实现左孩子先出栈)。
/**非递归方法利用栈,前序遍历树,根左右*/ public static void stackPreOrder(TreeNode node) { if(node == null) return; Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); stack.push(node); while(!stack.isEmpty()) { TreeNode n = stack.pop(); System.out.print(n.data+"-->"); if(n.rightChild != null) { stack.push(n.rightChild); } if(n.leftChild != null) { stack.push(n.leftChild); } } }
2.中序遍历:
思路:首先创建空栈,根节点压栈,然后抛栈获得一个节点,如果该节点的右孩子不为空则压栈,该节点自己再次压栈,如果该节点的左孩子不为空则压栈,否则抛栈(左孩子为空则打印当前节点),判断栈是不为空并且栈顶节点没有孩子,则抛栈,判断栈不为空且栈中包含当前节点的右孩子则抛栈,循环上面的方法直到栈为空。
代码:
/**非递归方法利用栈,中序遍历树,左根右*/ public static void stackMidOrder(TreeNode node) { if(node == null) return; Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); stack.push(node); while(!stack.isEmpty()) { TreeNode n = stack.pop(); if(n.rightChild != null) { stack.push(n.rightChild); } stack.push(n); if(n.leftChild != null) { stack.push(n.leftChild); } else { System.out.print(stack.pop().data+"-->"); } if(!stack.isEmpty() && stack.peek().leftChild == null && stack.peek().rightChild == null) { System.out.print(stack.pop().data+"-->"); if(!stack.isEmpty()) { System.out.print(stack.pop().data+"-->"); } } if(!stack.isEmpty() && stack.contains(stack.peek().rightChild)) { System.out.print(stack.pop().data+"-->"); } } }
3.后序遍历:
思路:创建两个栈,一个用来操作节点,一个用来记录节点轨迹,根节点入栈,然后判断当前栈顶节点的左孩子被轨迹栈包含,或者当前节点的右孩子被轨迹栈包含,或者当前节点没有孩子,则抛栈,否则如果右孩子不为空则,右孩子压栈,如果左孩子不为空则左孩子压栈,循环以上方法,直到栈为空。
代码:
/**非递归方法利用栈,后序遍历树,左右根*/ public static void stackPostOrder(TreeNode node) { if(node == null) { return; } Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); Stack<TreeNode> full = new Stack<BinaryTree.TreeNode>(); stack.push(node); full.push(node); while(!stack.isEmpty()) { TreeNode n = stack.peek(); if(full.contains(n.leftChild) || full.contains(n.rightChild) || (n.leftChild == null && n.rightChild == null)) { System.out.print(stack.pop().data+"-->"); } else { if(n.rightChild != null) { stack.push(n.rightChild); full.push(n.rightChild); } if(n.leftChild != null) { stack.push(n.leftChild); full.push(n.leftChild); } } } }
接下来是完整的java代码,如果树节点的结构体不会建立可以看看:
public class BinaryTree {
private static TreeNode root;
//创建树节点的类对象
public static class TreeNode {
//树节点的index
private int index;
//树节点的内容
private String data;
//树节点的左孩子
private TreeNode leftChild;
//树节点的右孩子
private TreeNode rightChild;
public TreeNode(int indext, String data) {
this.index = index;
this.data = data;
this.leftChild = null;
this.rightChild = null;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
/**
* 创建一棵二叉树:
*
* A
* B C
* D E F
G H I J K*/
public static void createTree(TreeNode node) {
TreeNode nodeB = new TreeNode(2, "B");
TreeNode nodeC = new TreeNode(3, "C");
TreeNode nodeD = new TreeNode(4, "D");
TreeNode nodeE = new TreeNode(5, "E");
TreeNode nodeF = new TreeNode(6, "F");
TreeNode nodeG = new TreeNode(7, "G");
TreeNode nodeH = new TreeNode(8, "H");
TreeNode nodeI = new TreeNode(9, "I");
TreeNode nodeJ = new TreeNode(10, "J");
TreeNode nodeK = new TreeNode(11, "K");
root.leftChild = nodeB;
root.rightChild = nodeC;
nodeB.leftChild = nodeD;
nodeB.rightChild = nodeE;
nodeC.rightChild = nodeF;
nodeD.leftChild = nodeG;
nodeD.rightChild = nodeH;
nodeE.leftChild = nodeI;
nodeF.leftChild = nodeJ;
nodeF.rightChild = nodeK;
}
/**获取树的深度*/
public static int getTreeDeep(TreeNode node) {
if(node == null) {
return 0;
} else {
int i = getTreeDeep(node.leftChild);
int j = getTreeDeep(node.rightChild);
return i>j ? i+1 : j+1;
}
}
/**获取树的节点数*/
public static int getTreeSize(TreeNode node) {
if(node == null) {
return 0;
} else {
return 1+getTreeSize(node.leftChild)+getTreeSize(node.rightChild);
}
}
/**递归方法,前序遍历树,前序遍历是根左右*/ public static void preOrder(TreeNode node) { if(node == null) { return; } System.out.print(node.data+"-->"); preOrder(node.leftChild); preOrder(node.rightChild); }
/**递归方法,中序遍历树, 左根右*/ public static void midOrder(TreeNode node) { if(node == null) return; midOrder(node.leftChild); System.out.print(node.data+"-->"); midOrder(node.rightChild); }
/**递归方法,后序遍历树,左右根*/ public static void postOrder(TreeNode node) { if(node == null) return; postOrder(node.leftChild); postOrder(node.rightChild); System.out.print(node.data+"-->"); }
/**非递归方法利用栈,前序遍历树,根左右*/ public static void stackPreOrder(TreeNode node) { if(node == null) return; Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); stack.push(node); while(!stack.isEmpty()) { TreeNode n = stack.pop(); System.out.print(n.data+"-->"); if(n.rightChild != null) { stack.push(n.rightChild); } if(n.leftChild != null) { stack.push(n.leftChild); } } }
/**非递归方法利用栈,中序遍历树,左根右*/ public static void stackMidOrder(TreeNode node) { if(node == null) return; Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); stack.push(node); while(!stack.isEmpty()) { TreeNode n = stack.pop(); if(n.rightChild != null) { stack.push(n.rightChild); } stack.push(n); if(n.leftChild != null) { stack.push(n.leftChild); } else { System.out.print(stack.pop().data+"-->"); } if(!stack.isEmpty() && stack.peek().leftChild == null && stack.peek().rightChild == null) { System.out.print(stack.pop().data+"-->"); if(!stack.isEmpty()) { System.out.print(stack.pop().data+"-->"); } } if(!stack.isEmpty() && stack.contains(stack.peek().rightChild)) { System.out.print(stack.pop().data+"-->"); } } }
/**非递归方法利用栈,后序遍历树,左右根*/ public static void stackPostOrder(TreeNode node) { if(node == null) { return; } Stack<TreeNode> stack = new Stack<BinaryTree.TreeNode>(); Stack<TreeNode> full = new Stack<BinaryTree.TreeNode>(); stack.push(node); full.push(node); while(!stack.isEmpty()) { TreeNode n = stack.peek(); if(full.contains(n.leftChild) || full.contains(n.rightChild) || (n.leftChild == null && n.rightChild == null)) { System.out.print(stack.pop().data+"-->"); } else { if(n.rightChild != null) { stack.push(n.rightChild); full.push(n.rightChild); } if(n.leftChild != null) { stack.push(n.leftChild); full.push(n.leftChild); } } } }
}
public BinaryTree() {
root = new TreeNode(1, "A");
}
public static void main(String[] args) {
BinaryTree tree = new BinaryTree();
TreeNode.createTree(root);
int deep = TreeNode.getTreeDeep(root);
System.out.println("Tree Deep:"+deep);
int size = TreeNode.getTreeSize(root);
System.out.println("Tree Size:"+size);
System.out.print("preOrder : ");
TreeNode.preOrder(root);
System.out.println("");
System.out.print("stackPreOrder : ");
TreeNode.stackPreOrder(root);
System.out.println("");
System.out.print("midOrder : ");
TreeNode.midOrder(root);
System.out.println("");
System.out.print("stackMidOrder : ");
TreeNode.stackMidOrder(root);
System.out.println("");
System.out.print("postOrder : ");
TreeNode.postOrder(root);
System.out.println("");
System.out.print("stackPostOrder : ");
TreeNode.stackPostOrder(root);
}
}
相关文章推荐
- Java实现二叉树的前序、中序、后序、层序遍历(递归方法)
- Java实现二叉树的前序、中序、后序遍历(递归方法)
- 二叉树的先序、中序、后序遍历方法(递归与非递归方法)——《数据结构》
- Java实现二叉树的前序、中序、后序遍历(非递归方法)
- 二叉树的高度 java 利用递归和层次遍历两种方法
- Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)
- 数据结构二叉树的递归与非递归遍历之java,javascript,php实现可编译(1)java
- Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)
- 二叉树先中后序遍历(递归、非递归方法)、层序遍历 Java实现
- 【C++】二叉树的创建方法及其遍历的递归与非递归方法总结
- 二叉树的高度 java 利用递归和层次遍历两种方法
- 【算法设计-二叉树遍历】二叉树的递归与非递归遍历方法
- 二叉树的先序、中序以及后序遍历(递归与非递归方法)
- 二叉树叶子节点遍历---递归与非递归方法求取树深度
- 二叉树的四种遍历 (六个方法递归 非递归都有 包含二叉树的创建java方法)
- Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)
- Java实现二叉树的前序、中序、后序、层序遍历(递归方法)
- 递归方法实现二叉树的创建,遍历
- 二叉树的遍历(递归方法)
- 二叉树的三种遍历方法(递归和非递归)(转载)