数据结构之二叉树及面试题讲解(三)

简介: 数据结构之二叉树及面试题讲解(三)

数据结构之二叉树及面试题讲解(二)+https://developer.aliyun.com/article/1413554

3.翻转二叉树

思路分析

 还是利用子问题思路,交换root的左右子树,再去更新root,继续交换左右子树

https://leetcode.cn/problems/invert-binary-tree/submissions/

代码实现

public TreeNode invertTree(TreeNode root) {
        if(root == null) return root;
        // 交换
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
        // 交换根节点的左子树和右子树
        invertTree(root.left);
        invertTree(root.right);
        return root;
    }

4.判断平衡二叉树

https://leetcode.cn/problems/balanced-binary-tree/submissions/

1.遍历每一个节点  判断其左右子树是否平衡  只要求出当前结点左右子树的高度即可  同时还要保证其余节点也平衡

// 求树的高度
    private int getHeight(TreeNode root) {
        if(root == null) return 0;
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        return leftHeight > rightHeight ? leftHeight+1 : rightHeight+1;
    }
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        // 高度平衡的条件 每颗结点都要高度平衡  即每颗结点的左右子树的高度差都要<=1
        return Math.abs(leftHeight-rightHeight) <= 1 &&
                isBalanced(root.left) &&
                 isBalanced(root.right);
    }

第一种方法时间复杂度达到了0(N^2),究其原因,在于在计算高度的时候发生了重复计算,在你求完root当前的高度之后还需要再去判断其左右子树是否平衡,判断的时候还需要再去求一遍高度,导致时间复杂度过高,我们发现,在求第一次高度时,整个求高度的过程中已经发现了不平衡的现象,我们可以在返回高度的过程中就去判断是否是平衡的

2.第二种思路

// 求树的高度
    private int getHeight(TreeNode root) {
        if(root == null) return 0;
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        // 返回正常高度的条件
        if(leftHeight >= 0 && rightHeight >= 0 && Math.abs(leftHeight-rightHeight) <= 1) {
            return Math.max(leftHeight,rightHeight)+1;
        }else {
            return -1;
        }
    }
    public boolean isBalanced(TreeNode root) {
        if(root == null) return true;
        return getHeight(root) >= 0;
    }

 正常返回高度的条件是

  • 左树高度和右树高度的绝对值之差 <= 1
  • 左子树的高度符合条件  即左子树的高度不是-1
  • 右子树的高度符合条件  即右子树的高度不是-1

 这道题曾经是字节面试出的一道题,第一种思路很容易想到,即通过求当前结点的左右子树的高度的绝对值之差来判断是否符合条件,同时还要满足当前结点的左子树和右子树也符合条件(这一点也容易忽视),但这种思路存在着重复计算的问题 ,时间复杂度过高;

 重复计算的是高度,那能不能在一次求高度的过程中就判断是否符合条件?答案是可以的,就是提供的第二种思路

 这种在过程中判断是否符合条件从而减少计算量的思路经常出现,也不容易实现,可以好好学习,总结一下

5.二叉树的遍历

https://www.nowcoder.com/practice/4b91205483694f449f94c179883c1fef?tpId=60&&tqId=29483&rp=1&ru=/activity/oj&qru=/ta/tsing-kaoyan/question-ranking

思路分析:

本题是根据前序遍历的方式去创建二叉树,本质上还是利用递归的方式去创建树

先创建当前的根节点,再去创建结点的左树,最后创建结点的右树

代码实现

import java.util.Scanner;
// 结点的信息需要自行创建
class TreeNode {
    char val;
    TreeNode left;
    TreeNode right;
    public TreeNode() {};
    public TreeNode(char val) {
        this.val = val;
    };
}
// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        // 注意 hasNext 和 hasNextLine 的区别
        while (in.hasNextLine()) { // 注意 while 处理多个 case
            String s = in.nextLine();
            // 获取创建树的根节点
            TreeNode root = createTree(s);
            // 中序遍历
            inOrder(root);
        }
    }
    public static int i = 0;
    // 根据前序遍历的结果创建一棵树
    public  static TreeNode createTree(String s) {
        TreeNode root = null;
        if(s.charAt(i) != '#') {
            // 不是#号,证明就是一个结点  实例化一个结点  i++  再去分别创建该节点的左树,右树
            root = new TreeNode(s.charAt(i));
            i++;
            root.left = createTree(s);
            root.right = createTree(s);
        }else {// 是#  直接i++
            i++;
        }
        // 递归到最后要把节点之间联系起来  所以返回root
        return root;
    }
    // 中序遍历
    public  static void inOrder(TreeNode root) {
        if(root == null) return;
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }
}

6.前序+中序构造二叉树

https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/

代码实现

//**
 * Definition for a binary tree node.
 * 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;
 *     }
 * }
 */
class Solution {
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        return buildChildTree(preorder,inorder,0,inorder.length-1);
    }
    // 应该将pi设置为成员变量  否则在递归回退的过程中会重新返回值
    public int pi;
    public TreeNode buildChildTree(int[] preorder,int[] inorder,int beginIndex,int endIndex) {
        // 1.没有左树  或者没有右树
        if(beginIndex > endIndex) {
            return null;
        }
        // 2.创建根节点
        TreeNode root = new TreeNode(preorder[pi]);
        // 3.在中序遍历中找到根节点
        int rootIndex = find(inorder,beginIndex,endIndex,preorder[pi]);
        if(rootIndex == -1) return null;
        pi++;
        // 创建左子树
        root.left = buildChildTree(preorder,inorder,beginIndex,rootIndex-1);
        // 创建右子树
        root.right = buildChildTree(preorder,inorder,rootIndex+1,endIndex);
        return root;
    }
    private int find(int[] inorder,int beginIndex,int endIndex,int key) {
        for(int i = beginIndex; i <= endIndex; i++) {
            if(inorder[i] == key) {
                return i;
            }
        }
        // 没找到  返回-1
        return -1;
    }
}

7.后序+中序构造二叉树

/**
 * Definition for a binary tree node.
 * 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;
 *     }
 * }
 */
class Solution {
    public int pi;
    public TreeNode buildTree(int[] inorder,int[] postorder) {
        pi = postorder.length-1;
        return buildChildTree(postorder,inorder,0,inorder.length-1);
    }
    public TreeNode buildChildTree(int[] postorder,int[] inorder,int beginIndex,int endIndex) {
        if(beginIndex > endIndex) {
            return null;
        }
        TreeNode root = new TreeNode(postorder[pi]);
        int rootIndex = find(inorder,beginIndex,endIndex,postorder[pi]);
        if(rootIndex == -1) return null;
        pi--;
        // 创建右子树
        root.right = buildChildTree(postorder,inorder,rootIndex+1,endIndex);
        // 创建左子树
        root.left = buildChildTree(postorder,inorder,beginIndex,rootIndex-1);
        return root;
    }
    private int find(int[] inorder,int beginIndex,int endIndex,int key) {
        for(int i = beginIndex; i <= endIndex; i++) {
            if(inorder[i] == key) {
                return i;
            }
        }
        // 没找到  返回-1
        return -1;
    }
}

总结:

前序/后序+中序都能构造出一棵二叉树,如果是前序+后序无法得到

8.最近的公共祖先

http://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if (root == null) return null;
        Stack<TreeNode> stack1 = new Stack<>();
        Stack<TreeNode> stack2 = new Stack<>();
        getPath(root, p, stack1);
        getPath(root, q, stack2);
        int sizeP = stack1.size();
        int sizeQ = stack2.size();
        if (sizeP > sizeQ) {
            int size = sizeP - sizeQ;
            while (size != 0) {
                stack1.pop();
                size--;
            }
        } else {
            int size = sizeQ - sizeP;
            while (size != 0) {
                stack2.pop();
                size--;
            }
        }
        // 此时两个栈的长度一致
        while(!stack1.peek().equals(stack2.peek())) {
            stack1.pop();
            stack2.pop();
        }
        return stack1.peek();
    }
    /**
     * 难点在于如何获得p,q路径上的所有节点
     * 利用栈存放通过前序遍历遇到的每一个节点  判断结点的左右子树是否包含要寻找的结点
     */
    private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
        if(root == null || node == null) return false;
        stack.push(root);
        if(root == node) return true;
        boolean flg1 = getPath(root.left,node,stack);
        if(flg1) {
            return true;
        }
        boolean flg2 = getPath(root.right,node,stack);
        if (flg2) {
            return true;
        }
        stack.pop();
        return false;
    }
}

/**
     * 找最近的公共祖先  三种情况
     * @param root
     * @param p
     * @param q
     * @return
     */
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null)  return null;
        if(root == p || root == q) return root;
        // 判断是在同一边还是两侧
        TreeNode leftTree = lowestCommonAncestor(root.lChild,p,q);
        TreeNode rightTree = lowestCommonAncestor(root.rChild,p,q);
        if(leftTree != null && rightTree != null) {
            // 都不为空 证明p,q在根节点的左右两侧  公共祖先只能是root
            return root;
        } else if (leftTree != null) {
            return leftTree;
        }else {
            return rightTree;
        }
    }



目录
相关文章
|
4月前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
198 3
|
7月前
|
存储 算法 Java
算法系列之数据结构-二叉树
树是一种重要的非线性数据结构,广泛应用于各种算法和应用中。本文介绍了树的基本概念、常见类型(如二叉树、满二叉树、完全二叉树、平衡二叉树、B树等)及其在Java中的实现。通过递归方法实现了二叉树的前序、中序、后序和层次遍历,并展示了具体的代码示例和运行结果。掌握树结构有助于提高编程能力,优化算法设计。
195 10
 算法系列之数据结构-二叉树
|
9月前
|
Java C++
【C++数据结构——树】二叉树的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现二叉树的基本运算。​ 相关知识 创建二叉树 销毁二叉树 查找结点 求二叉树的高度 输出二叉树 //二叉树节点结构体定义 structTreeNode{ intval; TreeNode*left; TreeNode*right; TreeNode(intx):val(x),left(NULL),right(NULL){} }; 创建二叉树 //创建二叉树函数(简单示例,手动构建) TreeNode*create
186 12
|
9月前
|
C++
【C++数据结构——树】二叉树的性质(头歌实践教学平台习题)【合集】
本文档介绍了如何根据二叉树的括号表示串创建二叉树,并计算其结点个数、叶子结点个数、某结点的层次和二叉树的宽度。主要内容包括: 1. **定义二叉树节点结构体**:定义了包含节点值、左子节点指针和右子节点指针的结构体。 2. **实现构建二叉树的函数**:通过解析括号表示串,递归地构建二叉树的各个节点及其子树。 3. **使用示例**:展示了如何调用 `buildTree` 函数构建二叉树并进行简单验证。 4. **计算二叉树属性**: - 计算二叉树节点个数。 - 计算二叉树叶子节点个数。 - 计算某节点的层次。 - 计算二叉树的宽度。 最后,提供了测试说明及通关代
166 10
|
9月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
262 3
|
10月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
11月前
|
机器学习/深度学习 存储 算法
数据结构实验之二叉树实验基础
本实验旨在掌握二叉树的基本特性和遍历算法,包括先序、中序、后序的递归与非递归遍历方法。通过编程实践,加深对二叉树结构的理解,学习如何计算二叉树的深度、叶子节点数等属性。实验内容涉及创建二叉树、实现各种遍历算法及求解特定节点数量。
260 4
|
11月前
|
C语言
【数据结构】二叉树(c语言)(附源码)
本文介绍了如何使用链式结构实现二叉树的基本功能,包括前序、中序、后序和层序遍历,统计节点个数和树的高度,查找节点,判断是否为完全二叉树,以及销毁二叉树。通过手动创建一棵二叉树,详细讲解了每个功能的实现方法和代码示例,帮助读者深入理解递归和数据结构的应用。
693 8
|
12月前
|
存储 算法 关系型数据库
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
这篇文章主要介绍了多路查找树的基本概念,包括二叉树的局限性、多叉树的优化、B树及其变体(如2-3树、B+树、B*树)的特点和应用,旨在帮助读者理解这些数据结构在文件系统和数据库系统中的重要性和效率。
134 0
数据结构与算法学习二一:多路查找树、二叉树与B树、2-3树、B+树、B*树。(本章为了解基本知识即可,不做代码学习)
|
11月前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet

热门文章

最新文章