算法与数据结构-二叉树 讲解与java代码实现

1. 三种遍历方式和宽度优先遍历

二叉树遍历方式

  • 递归实现先序,中序,后序遍历
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class TreeToSequence {
    public int[][] convert(TreeNode root) {
        // write code here

        ArrayList<Integer> temp1=new ArrayList<Integer>();
        ArrayList<Integer> temp2=new ArrayList<Integer>();
        ArrayList<Integer> temp3=new ArrayList<Integer>();

        pre(root,temp1);
        mid(root,temp2);
        post(root,temp3);
        int[][] result=new int[3][temp1.size()];
        for(int i=0;i<temp1.size();i++){
            result[0][i]=temp1.get(i);
            result[1][i]=temp2.get(i);
            result[2][i]=temp3.get(i);
        }

        return result;
    }

    public void pre(TreeNode root,ArrayList<Integer> result){
        if(root==null){
            return;
        }

        result.add(root.val);
        pre(root.left,result);
        pre(root.right,result);
    }

    public void mid(TreeNode root,ArrayList<Integer> result){
        if(root==null){
            return;
        }

        mid(root.left,result);
        result.add(root.val);
        mid(root.right,result);
    }

    public void post(TreeNode root,ArrayList<Integer> result){
     if(root==null){
         return;

     }   
        post(root.left,result);
        post(root.right,result);
        result.add(root.val);
    }
}
  • 非递归实现先序,中序,后序遍历
    非递归先序
    非递归中序
    非递归后序2个栈
    非递归后序1个栈
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class TreeToSequence {
    public int[][] convert(TreeNode root) {
        // write code here

        ArrayList<Integer> preRes=pre(root);
        ArrayList<Integer> midRes=mid(root);
        //ArrayList<Integer> postRes=post(root);
        ArrayList<Integer> postRes=post_oneStack(root);

        int[][] result=new int[3][preRes.size()];

        for(int i=0;i<preRes.size();i++){
            result[0][i]=preRes.get(i);
            result[1][i]=midRes.get(i);
            result[2][i]=postRes.get(i);
        }

        return result;
    }
    //非递归的先序遍历
    public ArrayList<Integer> pre(TreeNode root){
           Stack<TreeNode> stack=new Stack<TreeNode>();
        ArrayList<Integer> result=new ArrayList<Integer>();
        stack.push(root);
        TreeNode cur;
        while(!stack.isEmpty()){
            cur=stack.pop();
            result.add(cur.val);
            if (cur.right!=null){
               stack.push(cur.right);
            }
            if(cur.left!=null){
                stack.push(cur.left);
            }
        }
        return result;
    }

    public ArrayList<Integer> mid(TreeNode root){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        ArrayList<Integer> result=new ArrayList<Integer>();
        TreeNode cur;
        cur=root;
        while(cur!=null||!stack.isEmpty()){
            //将左子树依次压入栈
            while(cur!=null){
                stack.push(cur);
                cur=cur.left;
            }
            //当左子树全部压入栈后,开始从栈顶取元素并指定cur等于右子树
            cur=stack.pop();
            result.add(cur.val);
            cur=cur.right;
        }
        return result;
    }

    public ArrayList<Integer> post(TreeNode root){
        Stack<TreeNode> stack1=new Stack<TreeNode>();
        Stack<TreeNode> stack2=new Stack<TreeNode>();
        ArrayList<Integer> result=new ArrayList<Integer>();
        TreeNode cur;
        stack1.push(root);
        while(!stack1.isEmpty()){
            cur=stack1.pop();
            stack2.push(cur);
            if(cur.left!=null){
                stack1.push(cur.left);
            }
            if(cur.right!=null){
                stack1.push(cur.right);
            }
        }

        while(!stack2.isEmpty()){
            cur=stack2.pop();
            result.add(cur.val);
        }

        return result;

    }

    public ArrayList<Integer> post_oneStack(TreeNode root){
        Stack<TreeNode> stack=new Stack<TreeNode>();
        stack.push(root);
        ArrayList<Integer> result=new ArrayList<Integer>();
        TreeNode lastNode=root,cur=null;//分别指向上次打印的节点以及当前节点

        while(!stack.isEmpty()){
            cur=stack.peek();
            if(cur.left!=null&&cur.left!=lastNode&&cur.right!=lastNode){
                stack.push(cur.left);
            }else if(cur.right!=null&&cur.right!=lastNode){
                stack.push(cur.right);
            }else{
                cur=stack.pop();
                result.add(cur.val);
                lastNode=cur;
            }
        }

              return result;


    }
}

宽度优先遍历(按层从左到右遍历)

宽度优先遍历

import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class TreePrinter {
    public int[][] printTree(TreeNode root) {
        //保存每一行的值
        ArrayList<Integer> temp=new ArrayList<Integer>();
        //保存总的结果的list
        ArrayList<ArrayList<Integer>> resList=new ArrayList<ArrayList<Integer>>();
        Queue<TreeNode> queue=new LinkedList<TreeNode>();//队列,因为是按层从左到右遍历,先进先出
        queue.offer(root);
        TreeNode cur=root,last=root,nlast=null;//分别是当前节点,本层最后一个节点,下一层最后一个节点
        while(!queue.isEmpty()){
            cur=queue.poll();
            temp.add(cur.val);
            if(cur.left!=null){
                queue.offer(cur.left);
                nlast=cur.left;
            }
            if(cur.right!=null){
                queue.offer(cur.right);
                nlast=cur.right;
            }
            //如果当前节点为本层最后一个节点,则要把现在的节点加入结果,并令last等于nlast
            if(cur==last){
                resList.add(temp);
                temp=new ArrayList<Integer>();//不能使用Clear清空,否则存进入的就变成了空,因为保存的是引用
                last=nlast;
            }
        }
        int layers=resList.size();
        int[][] result=new int[layers][];
        for(int i=0;i<layers;i++){
            result[i]=new int[resList.get(i).size()];
            for(int j=0;j<result[i].length;j++){
                result[i][j]=resList.get(i).get(j);
            }
        }

        return result;


    }
}

二叉树的序列化打印:先序遍历

import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class TreeToString {
    public String toString(TreeNode root) {
        // write code here
        Stack<TreeNode> stack=new Stack<TreeNode>();
        String result=new String();
        stack.push(root);
        TreeNode cur;
        while(!stack.isEmpty()){
            cur=stack.pop();
            if(cur==null){
                result+="#!";
            }else{
                result+=cur.val+"!";
                stack.push(cur.right);
                stack.push(cur.left);
            }
        }
        return result;
    }
}

纸张对折n次,求折痕方向:使用中序逆序遍历,此处的逆序可以省去,因为是题目规定哪个方向为正
这里写图片描述
折纸方向打印 实现

import java.util.*;
public class FoldPaper {
    public String[] foldPaper(int n) {
        // write code here
           ArrayList<String> temp=new ArrayList<String>();
        //dir false表示"down",true表示"up"
        printDir(n,false,temp);
        int nodeSize=temp.size();
        String[] result=new String[nodeSize];
        for(int i=0;i<nodeSize;i++){
            result[i]=temp.get(i);
        }
        return result;
    }
    //中序逆序遍历递归打印
    public void printDir(int n,boolean dir,ArrayList<String> result){
        if(n<1){
            return;
        }
        printDir(n-1,false,result);
        result.add(dir?"up":"down");
        printDir(n-1,true,result);
    }
}

树上最远距离
这里写图片描述
这里写图片描述
这里写图片描述

import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class LongestDistance {
    public int findLongest(TreeNode root) {

        int max=getDistance(root,0)[1];
        return max;
    }

    //节点只能经过一次,那么当前节点的左右两边最远距离是最可能为最大距离的,每次要计算这个值
    //同时需要返回距离头结点的单边最远距离为后续计算提供依据
    //第一个返回参数为当前头结点的单边最远距离,第二个返回的参数为当前已经求得的最远距离
    public int[] getDistance(TreeNode root,int max){
        int[] result=new int[2];
        result[1]=max;
        if(root==null){
            result[0]=0;
            return result;
        }
        int[] leftRes=getDistance(root.left,max);
        int[] rightRes=getDistance(root.right,max);
        int lDistToRoot=leftRes[0];
        int rDistToRoot=rightRes[0];
        int twoSideDist=lDistToRoot+rDistToRoot+1;
        max=Math.max(leftRes[1],rightRes[1]);
        if(twoSideDist>max){
            max=twoSideDist;
        }
        result[0]=Math.max(lDistToRoot,rDistToRoot)+1;
        result[1]=max;
        return result;

    }
}

2.三种树及其判定方法

  • 二叉树的子树定义
    子树定义
  • 平衡二叉树的定义
    平衡二叉树定义
    平衡二叉树的递归判定
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class CheckBalance {
    public boolean check(TreeNode root) {
        // write code here
        return getHeight(root)>=0;
    }
    //递归 求以root为头结点的树的最大高度
    public int getHeight(TreeNode root){
        //如果是空节点或者空树,则高度为0
        if(root==null){
            return 0;
        }
        int left=getHeight(root.left);
        int right=getHeight(root.right);
        //如果左子树或者右子树不平衡,则返回-1
        if(left<0||right<0){
            return -1;
        }
        //如果左子树或者右子树高度差大于1,则返回-1
        if(Math.abs(left-right)>1){
            return -1;
        }
        //返回左子树和右子树中最大的高度作为本棵树的高度
        return left>right?left+1:right+1;
    }
}
  • 搜索二叉树
    搜索二叉树的定义
    最大搜索子树
    最大搜索子树
    这里写图片描述
    这里写图片描述
    最大搜索二叉子树递归判定
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class MaxSubtree {
    public int nodeNum=0;//以当前节点为根节点的树的节点数
    public int MAX=Integer.MAX_VALUE;
    public int MIN=Integer.MIN_VALUE;
    public int max,min;//以当前节点为根节点的树的最大最小值

    public TreeNode getMax(TreeNode root) {
        return getInfo(root);
    }

    public TreeNode getInfo(TreeNode root){
        int lNodeNum=0,rNodeNum=0;//左子树,右子树的节点数
        int lMax,lMin;//左子树的最大节点值,最小节点值
        int rMax,rMin;
        TreeNode lSearchRoot,rSearchRoot;//左右子树如果是二叉搜索树,则保存他们的头结点引用,若为null,则不是二叉搜索树
        if(root==null){
            nodeNum=0;
            max=MIN;
            min=MAX;
            return null;
        }

        lSearchRoot=getInfo(root.left);
        lNodeNum=nodeNum;
        lMax=max;lMin=min;

        rSearchRoot=getInfo(root.right);
        rNodeNum=nodeNum;
        rMax=max;rMin=min;

        min=Math.min(root.val,lMin);
        max=Math.max(root.val,rMax);
        nodeNum=Math.max(lNodeNum,rNodeNum);
        //当前树为二叉搜索树,要求左子树和右子树均为二叉搜索树,且以当前节点为头结点的树为二叉搜索树
        //此处子树的二叉搜索树判定不能写成lSearchRoot!=null,因为要考虑到有叶节点的情况
        if(lSearchRoot==root.left&&rSearchRoot==root.right&&lMax<root.val&&root.val<rMin){
            nodeNum=lNodeNum+rNodeNum+1;
            return root;
        }
        if(lNodeNum==rNodeNum){
            return lSearchRoot.val>rSearchRoot.val?lSearchRoot:rSearchRoot;
        }
        return lNodeNum>rNodeNum?lSearchRoot:rSearchRoot;
    }
}

搜索二叉树寻找位置错误的两个节点
这里写图片描述

import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class FindErrorNode {
    public int[] findError(TreeNode root) {
        // write code here
           ArrayList<Integer> result=new ArrayList<Integer>();//保存数值的list
        int[] temp=new int[2];
        boolean descendTime=false;
        //是否曾经遇到降序:因为错位的只有2个节点,分为两种情况
        //如果此时为第一次,则降序的前面个数为错位大的数,后面的数暂时存为错位小的数
        //如果此时为第二次降序,则降序的后面的数为错位小的数
         mid(root,result);
        for(int i=1;i<result.size();i++){
           if(result.get(i)<result.get(i-1)){
               temp[0]=result.get(i);
               if(descendTime==false){
                    descendTime=true;
                       temp[1]=result.get(i-1);
               }
           }

        }
       return temp;
    }

    public void mid(TreeNode root,ArrayList<Integer> result){
        if(root==null){
            return;
        }

         mid(root.left,result);
        result.add(root.val);
        mid(root.right,result);
    }
}
  • 满二叉树
    这里写图片描述
    这里写图片描述
    这里写图片描述
    完全二叉树的判断
    这里写图片描述
    完全二叉树的判定:使用队列进行宽度优先遍历
import java.util.*;
/*
public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}*/
public class CheckCompletion {
    public boolean chk(TreeNode root) {
        // write code here
        Queue<TreeNode> queue=new LinkedList<TreeNode>();
        queue.offer(root);
        TreeNode cur;
        boolean hasToBeLeaf=false;
        while(!queue.isEmpty()){
            cur=queue.poll();
            //如果当前节点的左孩子或者右孩子有一个为空
            if(cur.left==null||cur.right==null){
                //如果当前节点有右孩子却没有左孩子,则返回false,因为违背了缺少的节点全部集中在右边这一条
               if(cur.left==null&&cur.right!=null) return false;
                //因为缺少的节点需要全部集中在右边,若当前节点只有左孩子却没有右孩子,则后继节点必须为叶节点;
               if(hasToBeLeaf==false) hasToBeLeaf=true;
                //若此节点必须为叶节点却有左孩子,则返回false
               else if(cur.left!=null){
                   return false;
               }
            }else{
                queue.offer(cur.left);
                queue.offer(cur.right);
            }

        }
        return true;
    }
}

注释和引用:

java实现:

* Stack Queue

    * Stack<TreeNode> stack=new Stack<TreeNode>();//声明和定义
    * push() pop()//入栈和出栈
    * peek() //方法调用返回在这个堆栈的顶部的对象,但是不弹出
    * Queue<TreeNode> queue=new LinkedList<TreeNode>();//队列的声明和定义
    * offer() poll()//入队和出队
    * isEmpty() //判断栈和队列是否为空
    * http://www.runoob.com/java/data-queue.html 队列 菜鸟教程
    * 数组的大小用length属性,list的大小用size()函数


    * Arraylist<type(类名比如Integer或者String)>
    * List<String> temp=new ArrayList<String>();//初始化动态链表
    * size()//List或者ArrayList的元素个数
    * add()//添加元素
    * get(i)获取元素

牛客网算法课程视频链接

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值