算法总结之二叉树


遍历二叉树

给定一个二叉树的根结点root,请依次返回二叉树的先序,中序和后续遍历(二维数组的形式)。

递归方式
		/*
		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) {
		        List<Integer> list1 = new ArrayList<Integer>();
		        List<Integer> list2 = new ArrayList<Integer>();
		        List<Integer> list3 = new ArrayList<Integer>();
		        
		        qianxu(list1,root);
		        zhongxu(list2,root);
		        houxu(list3,root);
		        int[][] res = new int[3][list1.size()];
		        for(int i=0;i<list1.size();i++){
		            res[0][i] = list1.get(i);
		            res[1][i] = list2.get(i);
		            res[2][i] = list3.get(i);
		        }
		        return res;
		    }
		    
		    private void qianxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        list.add(tree.val);
		        qianxu(list,tree.left);
		        qianxu(list,tree.right);
		    }
		    
		    private void zhongxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        zhongxu(list,tree.left);
		        list.add(tree.val);
		        zhongxu(list,tree.right);
		    }
		    
		    private void houxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        houxu(list,tree.left);
		        houxu(list,tree.right);
		        list.add(tree.val);
		    }
		}
非递归方式

1.先序遍历思路:
(1)cur = root,cur 入栈,cur 出栈存入 list
(2)判断 cur 右孩子是否为空,不为空则入栈;再判断 cur 左孩子是否为空,不为空则入栈
(2)弹出栈顶存入 list ,cur = 栈顶节点,再重复步骤(2)(3)
2.中序遍历思路:
(1)cur = root,如果 cur 不为空则入栈,cur = cur.left,并重复(1)
(2)若 cur 为空,则弹出栈顶存入 list,并另 cur = cur.right;重复(1)(2)
3.后序遍历思路:
(1)定义两个节点 h = root ,c = 栈顶节点(初始为 null)
(2)tree入栈,c = tree;如果 c.left 不为空且 h != c.left 和 c.right,则 c 入栈
(3)否则若 c.right 不为空且 h != c.right,则 c 入栈
(4)若(2)(3)都不满足,则 h = c(栈顶节点),栈顶出栈存入 list ,重复(2)(3)(4)

		public class TreeToSequence {
		    public int[][] convert(TreeNode root) {
		        List<Integer> list1 = new ArrayList<Integer>();
		        List<Integer> list2 = new ArrayList<Integer>();
		        List<Integer> list3 = new ArrayList<Integer>();
		        
		        qianxu(list1,root);
		        zhongxu(list2,root);
		        houxu(list3,root);
		        int[][] res = new int[3][list1.size()];
		        for(int i=0;i<list1.size();i++){
		            res[0][i] = list1.get(i);
		            res[1][i] = list2.get(i);
		            res[2][i] = list3.get(i);
		        }
		        return res;
		    }
		    //前序遍历
		    private void qianxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        Stack<TreeNode> temp = new Stack<TreeNode>();
		        temp.push(tree);
		        while(!temp.empty()){
		            TreeNode cur = temp.pop();
		            list.add(cur.val);
		            if(cur.right!=null)
		                temp.push(cur.right);
		            if(cur.left!=null)
		                temp.push(cur.left);
		        }
		    }
		    //中序遍历
		    private void zhongxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        Stack<TreeNode> temp = new Stack<TreeNode>();
		        TreeNode cur = tree;
		        while(!temp.empty()||cur!=null){
		            if(cur!=null){
		                temp.push(cur);
		                cur = cur.left;
		            }else{
		                cur = temp.pop();
		                list.add(cur.val);
		                cur = cur.right;
		            }
		        }
		    }
		    //后序遍历
		    private void houxu(List<Integer> list,TreeNode tree){
		        if(tree==null)
		            return;
		        TreeNode h = tree;
		        TreeNode c = null;
		        Stack<TreeNode> temp = new Stack<TreeNode>();
		        temp.push(tree);
		        while(!temp.empty()){
		            c = temp.peek();
		            if(c.left!=null && (h!=c.left && h!=c.right)){
		                temp.push(c.left);
		                c = temp.peek();
		            }else if(c.right!=null && h!=c.right){
		                temp.push(c.right);
		                c = temp.peek();
		            }else{
		                h = c;
		                TreeNode tmp = temp.pop();
		                list.add(tmp.val);
		            }
		        }
		    }
		}

按层次打印

有一棵二叉树,请设计一个算法,按照层次打印这棵二叉树
思路:
(1)将A出队,并将A的子孩子入队,更新nlast=A最后入队的子孩子(最右边的孩子);
(2)打印上次出队的A,并和last比较, 如果相同就打印换行,并更新last=nlast,如果不相同,则继续

		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) {
		        if(root==null)
		            return null;
		        TreeNode print = null;
		        TreeNode last = null;
		        TreeNode nlast = null;
		        ArrayList<ArrayList<Integer>> res = new ArrayList<ArrayList<Integer>>();
		        ArrayList<Integer> rows = new ArrayList<Integer>();
		        LinkedList<TreeNode> q = new LinkedList();
		        last = root;
		        q.offer(root);
		        while(!q.isEmpty()){
		            print = q.poll();
		            rows.add(print.val);
		            if(print.left!=null){
		                q.offer(print.left);
		                nlast = print.left;
		            }
		            if(print.right!=null){
		                q.offer(print.right);
		                nlast = print.right;
		            }
		            if(print == last){
		                res.add(rows);
		                rows = new ArrayList<Integer>();
		                last = nlast;
		            }
		        }
		        int[][] result = new int[res.size()][];
		        for(int i=0;i<res.size();i++){
		            result[i] = new int[res.get(i).size()];
		            for(int j=0;j<res.get(i).size();j++){
		                result[i][j] = res.get(i).get(j);
		            }
		        }
		        return result;
		    }
		}

二叉树序列化

首先我们介绍二叉树先序序列化的方式,假设序列化的结果字符串为str,初始时str等于空字符串。先序遍历二叉树,如果遇到空节点,就在str的末尾加上“#!”,“#”表示这个节点为空,节点值不存在,当然你也可以用其他的特殊字符,“!”表示一个值的结束。如果遇到不为空的节点,假设节点值为3,就在str的末尾加上“3!”。现在请你实现树的先序序列化。
给定树的根结点root,请返回二叉树序列化后的字符串。

		/*
		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) {
		        String res = "";
		        if(root==null)
		            return res;
		        TreeNode temp = root;
		        LinkedList<TreeNode> stack = new LinkedList<TreeNode>();//HashSet、HashMap、ArrayList、LinkedList均可接受null值
		        stack.push(temp);
		        while(!stack.isEmpty()){
		            TreeNode cur = stack.pop();
		            if(cur==null)
		                res = res + "#!";
		            else{
		                res = res + String.valueOf(cur.val) + "!";
		                stack.push(cur.right);
		                stack.push(cur.left);
		            }
		        }
		        return res;
		    }
		}

平衡二叉树判断

有一棵二叉树,请设计一个算法判断这棵二叉树是否为平衡二叉树。
给定二叉树的根结点root,请返回一个bool值,代表这棵树是否为平衡二叉树。
思路:
(1)平衡二叉树:每个节点左右子树的高度差不超过 1,且空树也是平衡二叉树
(2)递归获得左右子树的深度,深度小于0或者左右子树深度差大于1,则直接返回false;否则继续递归,能返回树的深度(>=0)则说明是平衡二叉树

		/*
		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) {
		        return getDepth(root)>=0;
		    }
		    
		    private int getDepth(TreeNode root){
		        if(root==null)
		            return 0;
		        int left = getDepth(root.left);
		        int right = getDepth(root.right);
		        if(left<0||right<0)
		            return -1;
		        if(Math.abs(left-right)>1)
		            return -1;
		        return Math.max(left,right)+1;
		    }
		}

完全二叉树判断

有一棵二叉树,请设计一个算法判断它是否是完全二叉树。
给定二叉树的根结点root,请返回一个bool值代表它是否为完全二叉树。树的结点个数小于等于500。
思路:
(1)新建队列,按层遍历二叉树
(2)如果有节点的左孩子为空且右孩子不为空直接返回 false,如果左孩子不为空而右孩子为空,则此节点必须为叶子节点,如果不是叶子节点直接返回 false;循环中途没有返回 false,最后返回 true

		public class CheckCompletion {
		    public boolean chk(TreeNode root) {
		        if(root==null)
		            return true;
		        Queue<TreeNode> q = new LinkedList<>();
		        boolean leaf = false;
		        q.offer(root);
		        while(!q.isEmpty()){
		            TreeNode temp = q.poll();
		            if((leaf&&(temp.left!=null||temp.right!=null))||(temp.left==null&&temp.right!=null))
		               return false;
		            if(temp.left!=null&&temp.right==null)
		               leaf = true;
		            if(temp.left!=null)
		               q.offer(temp.left);
		            if(temp.right!=null)
		               q.offer(temp.right);
		        }
		        return true;
		    }
		}

折纸的折痕判断

请把纸条竖着放在桌⼦上,然后从纸条的下边向上⽅对折,压出折痕后再展 开。此时有1条折痕,突起的⽅向指向纸条的背⾯,这条折痕叫做“下”折痕 ;突起的⽅向指向纸条正⾯的折痕叫做“上”折痕。如果每次都从下边向上⽅ 对折,对折N次。请从上到下计算出所有折痕的⽅向。
给定折的次数n,请返回从上到下的折痕的数组,若为下折痕则对应元素为"down",若为上折痕则为"up".
测试样例:
1
返回:[“down”]
思路:第一次折纸,折痕为下折痕,第二次折纸,两边各多了下折痕和上折痕,相当于二叉树的左右孩子,每折纸一次节点就增加左右两个孩子。这是一颗满二叉树,而从上到下的折痕就是中序遍历这个二叉树。

		public class FoldPaper {
		    public String[] foldPaper(int n) {
		        List<String> temp = new LinkedList<String>();
		        fold(1,n,true,temp);
		        String[] res = new String[temp.size()];
		        for(int i=0;i<temp.size();i++){
		            res[i] = temp.get(i);
		        }
		        return res;
		    }
		    private  void fold(int i,int n,boolean flag,List<String> list){//flag用于判断左右孩子
		        if(i>n)
		            return;
		        fold(i+1,n,true,list);
		        if(flag)
		            list.add("down");
		        else
		            list.add("up");
		        fold(i+1,n,false,list);
		    }
		}

搜索二叉树错误节点

一棵二叉树原本是搜索二叉树,但是其中有两个节点调换了位置,使得这棵二叉树不再是搜索二叉树,请找到这两个错误节点并返回他们的值。保证二叉树中结点的值各不相同。
给定一棵树的根结点,请返回两个调换了位置的值,其中小的值在前。
思路
(1)中序遍历搜索二叉树,其结果是按照升序排列的
(2)遍历结果,如果只出现一次逆序则大小的节点都是错误节点;如果出现两次逆序,则第一次逆序的大节点,和第二次逆序的小节点才是两个错误节点。

		public class FindErrorNode {
		    public int[] findError(TreeNode root) {
		        List<Integer> temp = new ArrayList<Integer>();
		        int[] res = new int[2];
		        int index = 0;//用于判断是第几次逆序
		        zhongxu(root,temp);
		        for(int i=0;i<temp.size()-1;i++){
		            if(temp.get(i)>temp.get(i+1)){
		                index++;
		                if(index==1){
		                    res[1] = temp.get(i);
		                    res[0] = temp.get(i+1);
		                }else
		                    res[0] = temp.get(i+1);
		            }
		        }
		        return res;
		    }
		    public void zhongxu(TreeNode root,List<Integer> temp){
		        if(root==null)
		            return;
		        zhongxu(root.left,temp);
		        temp.add(root.val);
		        zhongxu(root.right,temp);
		    }
		}

树上最远距离

从二叉树的节点A出发,可以向上或者向下走,但沿途的节点只能经过一次,当到达节点B时,路径上的节点数叫作A到B的距离。对于给定的一棵二叉树,求整棵树上节点间的最大距离。
给定一个二叉树的头结点root,请返回最大距离。保证点数大于等于2小于等于500.
思路
最大距离只可能有三种情况:
(1)root左子树的最大距离;
(2)root右子树的最大距离;
(3)root左子树上离root左孩子最远结点的距离,加上root自身;再加上root右子树上离root右孩子最远结点的距离
三个中,最大的即为所求

		public class LongestDistance {
		    public int findLongest(TreeNode root) {
		        int[] res = new int[1];
		        houxu(root,res);
		        return res[0];
		    }
		    private int houxu(TreeNode root,int[] res){
		        if(root==null)
		            return 0;
		        int left = houxu(root.left,res);
		        int right = houxu(root.right,res);
		        res[0] = Math.max(res[0],left+right+1);
		        return Math.max(left,right)+1;
		    }
		}

最大二叉搜索子树

有一棵二叉树,其中所有节点的值都不一样,找到含有节点最多的搜索二叉子树,并返回这棵子树的头节点.
给定二叉树的头结点root,请返回所求的头结点,若出现多个节点最多的子树,返回头结点权值最大的。
思路
二叉搜索树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树。
(1)整体过程为二叉树的后序遍历;
(2) 遍历到当前结点记作cur时,先遍历cur的左子树并收集4个信息:分别是左子树上,最大搜索二叉树的头结点、结点数、树上最大值和树上最小值。再遍历cur的右子树,收集以上四个信息。
(3)根据步骤2所收集的信息,判断是否满足:是否以cur为头的子树,整体都是搜索二叉树,如果满足,就返回cur。如果不满足,就返回左子树和右子树各自最大搜索二叉树中,节点数较多的头结点。

		public class MaxSubtree {
		    public TreeNode getMax(TreeNode root) {
		        int[] temp = new int[3];
		        return houxu(root,temp);
		    }
		    
		    private TreeNode houxu(TreeNode root,int[] temp){
		        if(root==null){
		            temp[0]=Integer.MIN_VALUE;
		            temp[1]=Integer.MAX_VALUE;
		            temp[2]=0;
		            return null;
		        }
		        TreeNode left = houxu(root.left,temp);
		        int l_max = temp[0];//最大值
		        int l_min = temp[1];//最小值
		        int l_num = temp[2];//节点数
		        TreeNode right = houxu(root.right,temp);
		        int r_max = temp[0];
		        int r_min = temp[1];
		        int r_num = temp[2];
		        
		        //递归到最后temp[0],temp[1]中存着的是节点的最大值最小值,初始r_max==Integer.MIN_VALUE,l_min(Integer.MAX_VALUE)
		        temp[0] = Math.max(r_max,root.val);
		        temp[1] = Math.min(l_min,root.val);
		        
		        if(left==root.left && right==root.right && l_max<root.val && r_min>root.val){
		            temp[2] = l_num+r_num+1;
		            return root;
		        }
		        temp[2] = Math.max(l_num,r_num);//保存大的节点数
		        
		        return l_num>r_num?left:right;
		    }
		}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值