数据结构笔记四

数据结构笔记四


前言

这一节是二叉树的代码,主要包括二叉树的递归遍历,非递归遍历,层序遍历,最大宽度等等


二叉树的前中后序遍历

递归遍历

原理很简单代码如下

public class TreeNode {//先放上基本的树结构
    int val;
    TreeNode left;
    TreeNode right;
    TreeNode() {
    }
    TreeNode(int val) {
        this.val = val;
    }
    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}
public  void PreOrder(TreeNode root,List<Integer> list){//前序:头左右
        //运行到这里第一次经过这个节点
        if(root==null)return;
        list.add(root.val);
        PreOrder(root.left,list);
        //运行到这里第二次经过这个节点,从左树返回
        PreOrder(root.right,list);
        //运行到这里第三次经过这个节点,从右树返回
    }
    public  void InOrder(TreeNode root,List<Integer> list){//中序:左头右
        //运行到这里第一次经过这个节点
        if(root==null)return;
        PreOrder(root.left,list);
        //运行到这里第二次经过这个节点,从左树返回
         list.add(root.val);
        PreOrder(root.right,list);
        //运行到这里第三次经过这个节点,从右树返回
    }
    public  void PostOrder(TreeNode root,List<Integer> list){//后序:左右头
        //运行到这里第一次经过这个节点
        if(root==null)return;
        PreOrder(root.left,list);
        //运行到这里第二次经过这个节点,从左树返回
        PreOrder(root.right,list);
        //运行到这里第三次经过这个节点,从右树返回
        list.add(root.val);
    }

非递归遍历

先序遍历:准备一个栈,先把头节点放到栈里,然后是固定流程:
1.弹出一个节点并打印(如果此时栈空,没有元素可以弹出就结束)
2.先把右孩子入栈,再把左孩子入栈(如果有的话,因为从栈里弹出相当于逆序,所以要先右再左)
3.回到步骤一
(原理的话脑子里面自行模拟一下,把左右孩子入栈相当于是往下遍历,然后在栈里是逆序)
代码如下:

public List<Integer> preorderTraversal(TreeNode root) {
     List<Integer> list=new ArrayList<>();
        if (root==null)return list;
        Stack<TreeNode>stack=new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode a=stack.pop();
            list.add(a.val);
            if(a.right!=null)stack.push(a.right);
            if(a.left!=null)stack.push(a.left);
        }
        return list;
    }

后序遍历:
我们根据先序遍历可以得到头左右的顺序,那么按上面所写的代码,我左右孩子进栈的顺序调整一下就可以变成头右左的顺序,这时我不直接打印,我使用一个栈来将顺序颠倒就变成了左右头代码如下:

 public List<Integer> PostorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if (root==null)return list;
        Stack<TreeNode>stack=new Stack<>();
        Stack<TreeNode>stack2=new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode a=stack.pop();
            stack2.push(a);
            if(a.right!=null)stack.push(a.right);
            if(a.left!=null)stack.push(a.left);
        }
        while (!stack2.isEmpty()){
            list.add(stack2.pop().val);
        }
        return list;
    }

中序遍历:方法如下:
1.先将整颗树的左边界进栈
2.顺序弹出,但是在弹出的过程中如果该节点有右子树的话,在弹出该节点之后将该节点的右子树的整个左边界进栈
3.重复2过程直到栈空。
代码如下:

 public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list=new ArrayList<>();
        if (root==null)return list;
        Stack<TreeNode>stack=new Stack<>();
        while (root!=null){//步骤一
            stack.push(root);
            root=root.left;
        }
        while (!stack.isEmpty()){//步骤二
            TreeNode a=stack.pop();
            list.add(a.val);
            TreeNode b=a.right;
            while (b!=null){
                    stack.push(b);
                    b=b.left;
                }
        }
        return list;
    }

二叉树的层序遍历

层序遍历也就是按照二叉树的结构一层一层的输出,实现的思路呢也就是和宽度优先遍历相似,使用队列来遍历,每一个节点进队列,到弹出的时候需要把这个节点的左右孩子也放到队列里面,先进先出,但是再次之外我们还需要几个变量来记录打印的信息
代码如下提供两种思路,一种是用额外的变量记录队列现在遍历到了第几层(记录该层的节点数,每循环一次就减一,当为0时也就说明这这一层遍历完了),一种是在每遍历完一层时先的到队列的长度,记录下下一层共有几个节点

public List<List<Integer>> levelOrder(TreeNode root) {
        List<List<Integer>> list=new ArrayList<List<Integer>>();
        if (root==null)return list;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        int num=1;//记录这一层有几个节点
        while (!queue.isEmpty()){
            int nextnum=0;//记录下一层有几个结点
            List<Integer> tem=new ArrayList<>();
            while (num!=0){
                TreeNode a=queue.poll();
                tem.add(a.val);
                if(a.left!=null){
                    nextnum++;
                    queue.add(a.left);
                }
                if(a.right!=null){
                    nextnum++;
                    queue.add(a.right);
                }
                num--;
            }
            list.add(tem);
            num=nextnum;
            nextnum=0;
        }
        return list;
    }
    public List<List<Integer>> levelOrder(TreeNode root) {//第二种
            List<List<Integer>> list=new ArrayList<List<Integer>>();
            if(root==null){
                return list;
            }
            Queue<TreeNode> queue=new LinkedList<TreeNode>();
            queue.add(root);
            while(!queue.isEmpty()){
                List<Integer> res=new ArrayList<>();
                int currentLeversize= queue.size();//在这里获得该层有几个节点
                for(int i=1;i<=currentLeversize;i++){
                    TreeNode t=queue.poll();
                    res.add(t.val);
                    if(t.left!=null){
                        queue.add(t.left);
                    }
                    if(t.right!=null){
                        queue.add(t.right);
                    }
                }
                list.add(res);
            }
            return list;
        }


二叉树的宽度、每一层的最大值

二叉树的宽度使用上一节的代码稍微改改就行
宽度只需要保持一个变量,当这一层的节点数大于这个值时更新
代码如下:

 public int GetTreewidth(TreeNode root) {
        if (root==null)return 0;
        int width=1;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        int num=1;//记录这一层有几个节点
        while (!queue.isEmpty()){
            int nextnum=0;
            while (num!=0){
                TreeNode a=queue.poll();
                if(a.left!=null){
                    nextnum++;
                    queue.add(a.left);
                }
                if(a.right!=null){
                    nextnum++;
                    queue.add(a.right);
                }
                num--;
            }
           width=width<nextnum?nextnum:width;
            num=nextnum;
            nextnum=0;
        }
        return width;
    }

每一层的最大值也是同理,在遍历这一层的节点时维持一个最大值即可
代码如下:

public List<Integer> largestValues(TreeNode root) {
        List<Integer>list=new ArrayList<>();
        if (root==null)return list;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        int num=1;//记录这一层有几个节点
        while (!queue.isEmpty()){
            int nextnum=0,tem=Integer.MIN_VALUE;
            while (num!=0){
                TreeNode a=queue.poll();
                tem=tem>a.val?tem:a.val;
                if(a.left!=null){
                    nextnum++;
                    queue.add(a.left);
                }
                if(a.right!=null){
                    nextnum++;
                    queue.add(a.right);
                }
                num--;
            }
            list.add(tem);
            num=nextnum;
            nextnum=0;
        }
        return list;
    }

判断一棵树是否为搜索二叉树

搜索二叉树:对于一棵树,他的左树上的节点都比他小,右树上的节点都比他大
那么根据搜索二叉树的特征我们可以使用中序遍历的方法,如果在遍历的过程中,每个节点的值都是严格递增的,那么这个树就是搜索二叉树,这个代码很简单,这里就不演示了

判断一棵树是否为完全二叉树

完全二叉树:完全二叉树是由满二叉树而引出来的,若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数(即1~h-1层为一个满二叉树),第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。
判断的方法,使用宽度优先遍历,1.如果一个节点有右孩子但是没有左孩子就直接返回false 2.在1的前提下,如果出现了第一个左右孩子不双全的节点(左右孩子同时存在),那么接下来遍历到的每一个节点都要求是叶节点,原理的话建议自己画一棵树感觉一下。代码如下:

public boolean panduan(TreeNode root) {
        if (root==null)return false;
        Queue<TreeNode> queue=new LinkedList<>();
        queue.add(root);
        while (!queue.isEmpty()){
                TreeNode a=queue.poll();
                if(a.left==null&&a.right!=null)return false;
                if(a.left!=null&&a.right!=null){
                    queue.add(a.left);
                    queue.add(a.right);
                }else break;
        }
        while (!queue.isEmpty()){
            TreeNode a=queue.poll();
            if(a.left!=null||a.right!=null) return false;
        }
        return true;
    }

判断一棵树是否为平衡二叉树

平衡二叉树:树上的每一个树左右子树的高度差不超过一
判断方法:使用递归的思想,对于一颗二叉树我们可以抽象为包括根节点,左子树,右子树,那么根节点向左右子树询问是否为平衡二叉树,如果是,那么返回高度,如果不是就返回false,根节点得到左右子树的回复之后计算左右子树的高度差,如果符合高度差不查过1,那么就继续向根节点的上级节点返回高度,如果不符合,那么相应的也就返回false。代码如下:

public boolean isBalanced(TreeNode root) {
        if(isBalanced1(root)<0)return false;
        else return true;
    }
    public int  isBalanced1(TreeNode root){
        if (root==null)return 0;
        int left=isBalanced1(root.left);
        int right=isBalanced1(root.right);
        if(left<0||right<0||Math.abs(left-right)>1)return -1;
        else return Math.max(left,right)+1;
    }

寻找一棵树上的两个节点的最低公共祖先

给一张网课里面的图片做示例吧
在这里插入图片描述
d和e的最低公共祖先是b,b和d的最低公共祖先是b

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null||root==p||root==q) return root;
        TreeNode left=lowestCommonAncestor(root.left,p,q);
        TreeNode right=lowestCommonAncestor(root.right,p,q);
        if(left!=null&&right!=null) return root;
        return left!=null?left:right;
    }

代码的原理和思路:我们使用递归函数可以从孩子节点得到消息,设我们要寻找公共祖先的两个节点为p和q,那么我们随机的从树上挑出一个节点,现在我们关注这个节点:假设p和q分别在他的两个子树上,而且在此之前没有其他的公共祖先,那么该节点肯定是返回自己,如果p,q已经有公共祖先了在这个节点的左子树或者右子树上,子树返回的是一个节点,对于该子树不必关注这个节点是pq还是公共祖先,只需要向上继续返回就行了,如果他的子树什么也不返回或者这个节点本身为空,返回值就为空,从前面的流程中我们发现只有左右子树同时有返回值才意味着pq的最近公共祖先就是这个节点,而且,对于每一个节点而已,返回的值要么是他自己,要么是空,要么就是子树传递的值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值