二叉树的主要性质
性质1:非空二叉树上叶子节点数等于双分支节点数加1。(使用总分支数=总节点数-1证明)
性质2:二叉树的第i层上最多有2^(i-1)个节点(i>=1)。
性质3:高度为k的二叉树最多有2^(i-1)个节点。
性质4:高度为k的二叉树最多有2^k-1个节点。
二叉树的存储结构
(1)顺序存储结构:就是采用数组来存储一个二叉树。
(2)链式存储结构:就是采用链表来存储一个二叉树。
二叉树的遍历
(1)先序遍历:根左右。
(2)中序遍历:左根右。
(3)后序遍历:左右根。
(4)层序遍历:从上到下,从左到右,开始遍历。
二叉树的实现
二叉树节点的结构类:
class Node{
private Object data =;
private Node lchild =;
private Node rchild =;
public Node(){}
public Node(Object data) {
super();
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getLchild() {
return lchild;
}
public void setLchild(Node lchild) {
this.lchild = lchild;
}
public Node getRchild() {
return rchild;
}
public void setRchild(Node rchild) {
this.rchild = rchild;
}
}
使用输入的数组来构造二叉树:
思想:
首先将输入的数组数据转换成Node节点,并使用List存储。然后构造完了一堆节点,需要对节点之间建立关系。而关系可以根据自身定义,我的定义是输入数组中的数据下标为奇数做为左孩子节点,偶数为右孩子节点。然后对边界的定义如2i+2,无论当总节点为偶数时,二叉树本身的结构是没有最右边的叶子节点的,也就是说此树非满二叉树,那么我们使用2i+2来计算不存在的最右边叶子节点的右孩子节点时会报下标越界的异常。
实现:
/*构造树
* 使用数组构造二叉树
* 定义的规则:奇数作为左孩子,偶数的时候作为右孩子
*
* */
public static List<Node> createBt(Object[] objArr){
List<Node> retList =new ArrayList<Node>();
for(Object obj :objArr){
retList.add(new Node(obj));
}
if(retList.size() !=0){
for(int i=0;i<retList.size()/2;i++){
//奇数情况下
if(retList.get(2*i+1) !=null){
retList.get(i).setLchild(retList.get(2*i+1));
}
//偶数情况下
if(2*i+2 <=retList.size()-1 && retList.get(2*i+2) !=null){
retList.get(i).setRchild(retList.get(2*i+2));
}
}
}
return retList;
}
先序遍历,递归方式遍历二叉树
/* 先序遍历
* 根左右
* 递归方式
* */
public static void preBtByRecursion(Node n){
if(n !=null){
System.out.println("值:"+n.getData());
preBtByRecursion(n.getLchild());
preBtByRecursion(n.getRchild());
}else{
return;
}
}
中序遍历,递归方式遍历二叉树:
public static void orderBtByRecursion(Node n){
if(n !=null){
orderBtByRecursion(n.getLchild());
System.out.println("值:"+n.getData());
orderBtByRecursion(n.getRchild());
}else{
return;
}
}
后序遍历,递归方式遍历二叉树:
/* 后序遍历
* 左右根
* 递归方式
* */
public static void postBtByRecursion(Node n){
if(n !=null){
postBtByRecursion(n.getLchild());
postBtByRecursion(n.getRchild());
System.out.println("值:"+n.getData());
}else{
return;
}
}
先序遍历,非递归方式遍历二叉树:
思想:
遍历二叉树采用当前遍历路径尽可能深的去遍历二叉树,如果当前路径走到了尽头就需要往回走,那么就需要回溯了。为了对之前走过的路径进行回溯,那么就需要使用一个辅助的结构来记录之前走过的节点。而我们从头到尾的存入遍历过的节点,然后从尾到头的弹出节点。这种结构恰好符合栈的结构。所以就采用辅助栈来存储遍历过的节点。从而达到回溯的目得。
实现:
/* 先序遍历
* 根左右
* 非递归方式
* */
public static void preBt(List<Node> list){
Stack<Node> stack =new Stack<Node>();
Node root =list.get(0);
while(root !=null || !stack.isEmpty()){
System.out.println("值:"+root.getData());
stack.push(root);
root =root.getLchild();
while(root ==null && !stack.isEmpty()){
root =stack.pop();
root =root.getRchild();
}
}
}
中序遍历,非递归方式遍历二叉树:
思想:
和前面的前序遍历二叉树的思想差不多,都是采用辅助栈来实现回溯。
实现:
/* 中序遍历
* 左根右
* 非递归方式
* */
public static void orderBt(List<Node> list){
Stack<Node> stack =new Stack<Node>();
Node n =list.get(0);
while(n !=null || !stack.isEmpty()){
stack.push(n);
n =n.getLchild();
while(n ==null && !stack.isEmpty()){
n =stack.pop();
System.out.println("值:"+n.getData());
n =n.getRchild();
}
}
}
后序遍历,非递归方式遍历二叉树:
思想:
采用双辅助栈的方式来实现遍历的。后序遍历是左右根,为了调整遍历出来的节点的顺序,其实除了树最顶上的节点作为根外,剩下的节点,对于孩子节点父节点,对于同层的点来说就是兄弟节点。所以剩下的点都可以当作左右节点来看待了。树最顶上的点在进行操作前,先将其存入栈s1中,在从栈s1中弹出存入栈s2中。(肯定有人会问,直接放入栈s2中不就完事了,这样做,当栈s1都没有根节点,那么它从哪开始遍历都不知道)。其他节点就是同样的套路了。压入栈s1的顺序左右,弹出栈的顺序是右左,在压入到栈s2的顺序是右左,那么弹出栈的顺序就是左右了,加上之前特殊处理的根在s2最底下,s2的整体顺序就是左右根,符合后序遍历的规则了。
实现:
/* 后序遍历
* 左右根
* 非递归方式
* */
public static void postBt(List<Node> list){
Stack<Node> stack1 =new Stack<Node>();
Stack<Node> stack2 =new Stack<Node>();
Node n =list.get(0);
stack1.push(n);
while(!stack1.isEmpty()){
n =stack1.pop();
stack2.push(n);
if(n.getLchild() !=null){
stack1.push(n.getLchild());
}
if(n.getRchild() !=null){
stack1.push(n.getRchild());
}
}
while (!stack2.isEmpty()) {
System.out.println(stack2.pop().getData());
}
}
测试
public static void main(String[] args) {
String[] str ={"1","2","3","4","5","6"};
List<Node> list =createBt(str);
postBt(list);
}