1.二叉树的创建
二叉树的创建,这里使用的"#"作为停止符(创建的时候也是前序的方式创建的)
public void createBiTree() { root = createBiTree(root); } private Node createBiTree(Node x) { Scanner in = new Scanner(System.in); String str = in.next(); if(str.equals("#")) return null; else { x = new Node(str); x.left = createBiTree(x.left); x.right = createBiTree(x.right); } return x; //这行代码好好理解 }
二叉树的创建和二叉排序树的创建,在C语言中因为有指针,所以送入参数的是结点的左右子树的地址,而Java中没有指针,所以采用的是添加一个结点后,将该结点返回的方式,返回给它上一级结点的左右子树。类似的情况,还发生在在没有头结点的链表中采用头插法,此时将头结点复制给形参,对形参操作后,头结点已经被后移动了一位,此时需要重新赋值头结点。
这里使用的Scanner输入的,其实使用BufferedReader也是可以的
代码实现:
private Node createBiTree(Node x){ //Scanner in = new Scanner(System.in); BufferedReader bis = new BufferedReader(new InputStreamReader(System.in)); try{ String str = bis.readLine(); //String str = in.nextLine(); if(str.equals("#")){ return null; }else{ x = new Node(str); x.left = createBiTree(x.left); x.right = createBiTree(x.right); } }catch(Exception e){ } return x; }
Sanner和BufferedReader的区别:https://blog.csdn.net/zengxiantao1994/article/details/78056243
这个createBiTree函数中的返回return x,要理解,创建完二叉树后,还会返回根结点。
2.二叉树的前序、中序、后续遍历
1)前序遍历的两种方式:
前序递归方式:
public void preOrder(Node x) { if(x==null) return; System.out.print(x.str+" "); preOrder(x.left); preOrder(x.right); }前序非递归方式:
public void preOrder(Node root){ Stack<Node> stack = new Stack<>(); while(root!=null||!stack.isEmpty()){ while(root!=null){ //将左边的都遍历完 System.out.print(root.str+" "); stack.push(root); root = root.left; } if(!stack.isEmpty()){ //如果栈不为空,则从栈中取出元素 root = stack.pop(); root = root.right; } } }
使用非递归方式
代码过程说明:
1)访问任意结点p,并将结点p压入栈;
2)判断结点p的左孩子是否为空,如果为空,就从栈中取出栈顶结点并进行出栈操作,并将出去的栈顶结点的右孩子置为当前的结点p,循环至1),如果不为空,则将结点p的左孩子置为当前结点p;
3)知道栈为空且p为NULL,则遍历结束;
使用栈的目的是为了以后能通过栈中的结点找到该结点的右孩子。
前序非递归遍历参考资料:
https://www.bilibili.com/video/av15550091?from=search&seid=4678713895339943928
http://www.cnblogs.com/dolphin0520/archive/2011/08/25/2153720.html#!comments
2)中序遍历的两种方式:
中序遍历递归方式:
public void inOrder(Node x){ if(x==null) return; inOrder(x.left); System.out.print(x.str+" "); inOrder(x.right); }中序遍历非递归方式:
public void inOrder(Node root){ Stack<Node> stack = new Stack<>(); while(root!=null||!stack.isEmpty()){ while(root!=null){ stack.push(root); root = root.left; } if(!stack.isEmpty()){ root = stack.pop(); System.out.print(root.str+" "); //这一点和前序不同,每次从栈中取出的时候打印 root = root.right; } } }
对于任意结点P
1)若其左孩子不为空,则将其左孩子入栈并将p的左孩子设置为当前的p,然后对当前结点p再进行相同的处理
2)若其左孩子为空,则取出栈顶元素并进行出栈操作,打印出该栈顶结点,然后将栈顶结点的右孩子设置为p,重复1)
3)直到p为空且栈为空则遍历结束。
3)后序遍历的两种方式:
public void inOrder(Node x){ if(x==null) return; inOrder(x.left); inOrder(x.right); System.out.println(x.str+" "); }
递归方式:
3.二叉树的镜像(按照前序遍历的过程来操作)
代码示例:
public void postOrder(Node x) { if(x==null) return; postOrder(x.left); postOrder(x.right); System.out.print(x.str+" "); }通过前序遍历的顺序逐个交换一个结点的左子树和右子树。
4.二叉树的深度(按照后序遍历的过程来操作)
代码实现:
public int treeDepth(Node x){ if(x==null){ return 0; } int n1 = treeDepth(x.left); int n2 = treeDepth(x.right); if(n1>n2){ n1++; }else{ n2++; } return Math.max(n1, n2); }
说明:二叉树的最大深度可以看成是二叉树的后续遍历,后续遍历是自底向上,自左向右的移动,可以理解为动态规划问题。
5.二叉树的层序遍历
public void levelOrder(Node x) { if(x==null) return; Queue<Node> q = new LinkedList<>(); //链表也可以实现队列的功能 q.add(x); while(!q.isEmpty()) { Node n = q.poll(); System.out.print(n.data+" "); if(n.left!=null) //这边的判断要加入,因为队列中不能放入空 q.add(n.left); if(n.right!=null) q.add(n.right); } }
1)将头结点入队列
2)将头结点出队列,获取头结点,并判断头结点的左子树和右子树是否为空,如果不为空,将它们压入队列
3)重复上面的操作,每次一个结点出队列,都会将它的左右结点压入队列(在左右结点不为空的情况下)
记住一点:每次从队列尾部取出一结点的同时,都要将给结点的左孩子(如果存在的情况下)和右孩子(如果存在的情况下)插入到队列中。
思想类同图中的广度搜索优先
6.二叉树的几个特征:
中序遍历:将二叉树的结点投影到平面,从左到右就是中序遍历的结果
前序遍历:二叉树的前序遍历得到的序列中,第一个结点是根节点的值
后序遍历:二叉树的后序遍历得到的序列中,最后一个结点是根结点的值