二叉树
二叉树有左子树,右子树之分,因此是一个有序树
二叉树分为 满二叉树 和 完全二叉树
二叉树的性质:
结论3的推导过程:
通过性质 做两道题:
自己简单实现一个二叉树
二叉树的遍历(通过递归实现)
前序遍历,中序遍历,后序遍历
package dataStructBinaryTree;
public class MyBinaryTree {
static class TreeNode{
public char val; // 数据域
public TreeNode left; //左孩子引用
public TreeNode right; // 有孩子引用
public TreeNode(char val) {
this.val = val;
}
}
public TreeNode root; // 根节点
public TreeNode createTree(){
TreeNode A = new TreeNode('A');
TreeNode B = new TreeNode('B');
TreeNode C = new TreeNode('C');
TreeNode D = new TreeNode('D');
TreeNode E = new TreeNode('E');
TreeNode F = new TreeNode('F');
TreeNode G = new TreeNode('G');
TreeNode H = new TreeNode('H');
A.left = B;
A.right = C;
B.left = D;
B.right = E;
C.left = F;
C.right = G;
E.right = H;
this.root = A;
return root;
}
// 前序遍历
public void preOrder(TreeNode root){
if(root == null){
return;
}
System.out.print(root.val+" ");
preOrder(root.left);
preOrder(root.right);
}
// 中序遍历
void inOrder(TreeNode root){
if (root == null){
return;
}
inOrder(root.left);
System.out.print(root.val+" ");
inOrder(root.right);
}
// 后序遍历
void postOrder(TreeNode root){
if(root == null){
return;
}
postOrder(root.left);
postOrder(root.right);
System.out.print(root.val+" ");
}
}
二叉树的层序遍历 (*)
思路:
如图,可以建立一个队列 来按顺序输出 从上到下,从左到右的二叉树
定义一个中间变量 cur
从队列弹出pop一个元素赋值给cur,打印这个元素,然后将cur的左子树,右子树 入队列
然后再弹出队列的下一个元素
依次循环下去,直到队列为空。
class Solution {
public void levelOrder(TreeNode root) {
if (root == null){
return null;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
TreeNode cur = queue.poll();
System.out.println(cur.val +" ");
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
}
}
}
而在本题中,要求输出的是一个二维List , 因此要多一次循环生成二维顺序表
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if (root == null){
return list;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
int size = queue.size();
List<Integer> tmp = new ArrayList<>();
while (size>0){
TreeNode cur = queue.poll();
tmp.add(cur.val);
size--;
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
}
list.add(tmp);
}
return list;
}
}
思考:
如何输出二叉树的左视图?
假设为下图:
则此图 的左视图 就为 A B E H
使用层序遍历方法,即为 求每一层 的第一个元素
右视图 同理。。。。
二叉树的常用方法
// 子问题法 (递归) 求 节点个数
// 获取树中节点的个数
public int size(TreeNode root){
if(root == null){
return 0;
}
int count = 1;
int leftCount = size(root.left);
int rightCount = size(root.right);
count = count + leftCount + rightCount;
return count;
}
// 遍历法求 节点个数
// public int usedSize;
// public void size2(TreeNode root){
// if(root == null){
// return;
// }
// usedSize++;
// preOrder(root.left);
// preOrder(root.right);
// }
// 获取叶子节点的个数
public int getLeafNodeCount(TreeNode root){
if(root == null){
return 0;
}
if(root.left == null && root.right== null){
return 1;
}
int left = getLeafNodeCount(root.left);
int right = getLeafNodeCount(root.right);
return left+right;
}
// 获取第K层节点的个数
public int getKLevelNodeCount(TreeNode root,int k){
if (root == null){
return 0;
}
if (k == 1){
return 1;
}
int left = getKLevelNodeCount(root.left,k-1);
int right = getKLevelNodeCount(root.right,k-1);
return left+right;
}
// 获取二叉树的高度
// 求左树的高度和右树的高度 然后取最大值 然后加一 就是树的高度
int getHeight(TreeNode root){
if (root == null){
return 0;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
return left>right ? left+1 : right+1;
}
// 检测值为value的元素是否存在
TreeNode find(TreeNode root, int val){
if(root == null){
return null;
}
if(root.val == val){
return root;
}
TreeNode left = find(root.left,val);
if (left != null){
return left;
}
TreeNode right = find(root.right,val);
if (right != null){
return right;
}
return null;
}
二叉树的算法题
数据结构11节
1. 检查两颗树是否相同。
OJ链接100. 相同的树 - 力扣(LeetCode)
思路:
从root 开始判断:
结构是否相同,如果一个为空一个非空 则一定不相等
结构相同: 判断是否都是空
结构相同且都不为空:判断值是否相等 如果值也相等 则证明 root 相同
然后再进去左右子树 判断子树的 root 是否相同(如上流程)依次不断递归,直到值都为空返回
class Solution {
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q != null || p != null && q == null){
return false;
}
if(p == null && q == null){
return true;
}
if(p.val != q.val){
return false;
}
boolean left = isSameTree(p.left,q.left);
boolean right = isSameTree(p.right,q.right);
return left && right;
}
}
2. 另一颗树的子树。
OJ链接572. 另一棵树的子树 - 力扣(LeetCode)
思路:
如果发现 有相同的树 就直接返回true
判断是否有相同树的方法 如上题
class Solution {
public boolean isSubtree(TreeNode root, TreeNode subRoot) {
if (root == null || subRoot == null){
return false;
}
if (isSameTree(root,subRoot)) return true;
if (isSubtree(root.left,subRoot)) return true;
if (isSubtree(root.right,subRoot)) return true;
return false;
}
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q != null || p != null && q == null){
return false;
}
if(p == null && q == null){
return true;
}
if(p.val != q.val){
return false;
}
return isSameTree(p.left,q.left) && isSameTree(p.right,q.right) ;
}
}
3. 翻转二叉树。
Oj链接226. 翻转二叉树 - 力扣(LeetCode)
思路:
定义一个中间变量
再交换左右子树即可
不断递归下去
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null){
return null;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
}
4. 判断一颗二叉树是否是平衡二叉树。 (重点 !!)
OJ链接110. 平衡二叉树 - 力扣(LeetCode)
思路:
求两数的绝对值
要使高度差不超过1,所以首先要定义一个方法求出左右子树的高度(参考上文的二叉树常用方法实现 )
得到左右子树的高度差小于2才返回true ,
同时要满足 当前根 的左子树 && 当前根 的右子树 也符合这个条件 ( 相当于此处要递归判断是否成立 )
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int left = getHight(root.left);
int right = getHight(root.right);
return Math.abs(left-right)<2 && isBalanced(root.left) && isBalanced(root.right);
}
public int getHight(TreeNode root){
if(root == null){
return 0;
}
int left = getHight(root.left);
int right = getHight(root.right);
return left>right ? left+1 : right+1;
}
}
当前代码的时间复杂度是O(n2) , 为什么 ?
如果要变为O(n) 要怎么写 ?
数据结构11节 2:00:00 ~2:10:00
思路:
在求高度的时候就判断是否 左右子树 的高度差值<2
如果不满足平衡,返回一个 负数
class Solution {
public boolean isBalanced(TreeNode root) {
return getHight(root)>=0;
}
public int getHight(TreeNode root){
if(root == null){
return 0;
}
int left = getHight(root.left);
if(left < 0) return -1;
int right = getHight(root.right);
if(right < 0) return -1;
if(Math.abs(left-right)<2){
return Math.max(left,right)+1;
}else {
return -1;
}
}
}
5. 对称二叉树。
OJ链接101. 对称二叉树 - 力扣(LeetCode)
思路一
常规解法:
观察可以发现,
对于root, root.left 和 root.right 的值要相同,
左树的左子树 要等于右树的右子树,
左树的右子树,要等于右树的左子树
class Solution {
public boolean isSymmetric(TreeNode root) {
if (root == null){
return false;
}
// 存在root,开始判断下一层
return isSymmetricChild(root.left,root.right);
}
public boolean isSymmetricChild(TreeNode rootLeft,TreeNode rootRight){
// 左为空,右不为空 或 左不为空,右为空 则结构不相等,false
if(rootLeft == null && rootRight != null || rootLeft != null && rootRight == null ){
return false;
}
// 左右子树的跟都为空时,结构相等 true
if (rootLeft == null && rootRight == null){
return true;
}
// 此时 左右子树的跟结构相等,且都不为空,判断值是否相同, 不同 false
if(rootLeft.val != rootRight.val){
return false;
}
// 左右子树的root都相同,判断下一层是否符合
// 左的左 == 右的右 , 左的右 == 右的左
return isSymmetricChild(rootLeft.left,rootRight.right) &&
isSymmetricChild(rootLeft.right,rootRight.left);
}
}
思路二:
灵活运用前面写的代码
先翻转二叉树的右(左)子树,再判断两个二叉树是否相等
调用翻转二叉树方法,和判断二叉树是否相等方法
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null){
return false;
}
if (root.left == null && root.right == null){
return true;
}
invertTree(root.right);
boolean ret = isSameTree(root.left,root.right);
return ret;
}
// 翻转 二叉树
public TreeNode invertTree(TreeNode root) {
if (root == null){
return null;
}
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
// 判断 二叉树是否相等
public boolean isSameTree(TreeNode p, TreeNode q) {
if (p == null && q != null || p != null && q == null){
return false;
}
if(p == null && q == null){
return true;
}
if(p.val != q.val){
return false;
}
boolean left = isSameTree(p.left,q.left);
boolean right = isSameTree(p.right,q.right);
return left && right ;
}
}
6. 二叉树的构建及遍历。
OJ链接102. 二叉树的层序遍历 - 力扣(LeetCode)
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> list = new ArrayList<>();
if (root == null){
return list;
}
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()){
int size = queue.size();
List<Integer> tmp = new ArrayList<>();
while (size>0){
TreeNode cur = queue.poll();
tmp.add(cur.val);
size--;
if (cur.left != null){
queue.offer(cur.left);
}
if (cur.right != null){
queue.offer(cur.right);
}
}
list.add(tmp);
}
return list;
}
}
7. 二叉树的分层遍历 。
OJ链接
8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。
236. 二叉树的最近公共祖先 - 力扣(LeetCode)
有三种情况:
用递归的方法实现: 常规方法
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null){
return null;
}
if( root == p || root == q ){
return root;
}
TreeNode leftRet = lowestCommonAncestor(root.left,p,q);
TreeNode rightRet = lowestCommonAncestor(root.right,p,q);
if( leftRet != null && rightRet != null ){
return root;
} else if (leftRet != null) {
return leftRet;
} else if ( rightRet != null ) {
return rightRet;
}
return null;
}
}
用栈实现: 讲解:二叉树04 - 1:10:00
9. 根据一棵树的前序遍历与中序遍历构造二叉树。
105. 从前序与中序遍历序列构造二叉树 - 力扣(LeetCode)