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.二叉树的几个特征:
中序遍历:将二叉树的结点投影到平面,从左到右就是中序遍历的结果
前序遍历:二叉树的前序遍历得到的序列中,第一个结点是根节点的值
后序遍历:二叉树的后序遍历得到的序列中,最后一个结点是根结点的值