二叉树
1、二叉树
n(n>=0)个结点的有限集合,该集合或者为空集(称为空二叉树),或者由一个根结点和两棵互不相交的、分别称为根结点的左子树和右子树组成。
(1)满二叉树
在一棵二叉树中。如果所有分支结点都存在左子树和右子树,并且所有叶子都在同一层上,这样的二叉树称为满二叉树。
特点:
1)叶子只能出现在最下一层。出现在其它层就不可能达成平衡。
2)非叶子结点的度一定是2。
3)在同样深度的二叉树中,满二叉树的结点个数最多,叶子数最多。
(2)完全二叉树
对一颗具有n个结点的二叉树按层编号,如果编号为i(1<=i<=n)的结点与同样深度的满二叉树中编号为i的结点在二叉树中位置完全相同,则这棵二叉树称为完全二叉树。
特点:
1)叶子结点只能出现在最下层和次下层。
2)最下层的叶子结点集中在树的左部。
3)倒数第二层若存在叶子结点,一定在右部连续位置。
4)如果结点度为1,则该结点只有左孩子,即没有右子树。
5)同样结点数目的二叉树,完全二叉树深度最小。
6)满二叉树一定是完全二叉树,但反过来不一定成立。
(3)二叉搜索树
又称二叉查找树或二叉排序树。一棵二叉搜索树是以二叉树来组织的,可以使用一个链表数据结构来表示,其中每一个结点就是一个对象。
特点:
1)若任意结点的左子树不空,则左子树上所有结点的值均不大于它的根结点的值。
2)若任意结点的右子树不空,则右子树上所有结点的值均不小于它的根结点的值。
3)任意结点的左、右子树也分别为二叉搜索树。
2、二叉树遍历与递归
(1)三种遍历
-
以根访问顺序决定是什么遍历
-
左子树都是优先右子树
① 前序遍历:
Root -> Left -> Right 先访问根节点,再前序遍历左子树,再前序遍历右子树
/** leetcode 144 二叉树的前序遍历
* 给你二叉树的根节点 root ,返回它节点值的前序遍历。
* 输入:root = [1,null,2,3]
* 输出:[1,2,3]
*/
/**
* 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 List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preTra(res,root);
return res;
}
void preTra(List<Integer> res,TreeNode root){
if(root==null) return;
res.add(root.val);
preTra(res,root.left);
preTra(res,root.right);
}
}
② 中序遍历
Left-> Root-> Right 先中序遍历左子树,再访问根节点,再中序遍历右子树
/** leetcode 94 二叉树的中序遍历
* 给你二叉树的根节点 root ,返回它节点值的中序遍历。
* 输入:root = [1,null,2,3]
* 输出:[1,3,2]
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inoTra(res,root);
return res;
}
void inoTra(List<Integer> res,TreeNode root){
if(root==null) return;
inoTra(res,root.left);
res.add(root.val);
inoTra(res,root.right);
}
}
③ 后序遍历
Left-> Right-> Root 先后序遍历左子树,再后序遍历右子树,再访问根节点
/** leetcode 94 二叉树的后序遍历
* 给你二叉树的根节点 root ,返回它节点值的后序遍历。
* 输入:root = [1,null,2,3]
* 输出:[3,2,1]
*/
/**
* 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 List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
posTra(res,root);
return res;
}
void posTra(List<Integer> res,TreeNode root){
if(root==null) return;
posTra(res,root.left);
posTra(res,root.right);
res.add(root.val);
}
}
(2)分治法
先分别处理局部,再合并结果
分治法模板
- 递归返回条件
- 分段处理
- 合并结果
/**leetcode 110 平衡二叉树
给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
思路:分治法,左边平衡 && 右边平衡 && 左右两边高度 <= 1, 因为需要返回是否平衡及高度,要么返回两个数据,要么合并两个数据, 所以用-1 表示不平衡,>0 表示树高度(二义性:一个变量有两种含义)。
*/
/**
* 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 boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int d = judge(root);
if(d>=0){
return true;
}
return false;
}
public int judge(TreeNode root){
if(root == null){
return 0;
}
//分治
int l = judge(root.left);
int r = judge(root.right);
if(l >= 0 && r >= 0 && Math.abs(l - r) <= 1) {
return Math.max(l,r)+1;
}else{
return -1;
}
}
}
/** leetcode 124 二叉树中的最大路径和
给定一个非空二叉树,返回其最大路径和。
本题中,路径被定义为一条从树中任意节点出发,沿父节点-子节点连接,达到任意节点的序列。该路径至少包含一个节点,且不一定经过根节点。
示例 1:
输入:[1,2,3]
1
/ \
2 3
输出:6
示例 2:
输入:[-10,9,20,null,null,15,7]
-10
/ \
9 20
/ \
15 7
输出:42
思路:分治法,分为三种情况:左子树最大路径和最大,右子树最大路径和最大,左右子树最大加根节点最大,需要保存两个变量:一个保存子树最大路径和,一个保存左右加根节点和,然后比较这个两个变量选择最大值即可
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
private int ans = Integer.MIN_VALUE;
public int maxPathSum(TreeNode root) {
fun(root);
return ans;
}
public int fun(TreeNode root){
if(root == null){
return 0;
}
int leftNum = Math.max(0, fun(root.left));
int rightNum = Math.max(0, fun(root.right));
ans = Math.max(ans, leftNum+rightNum+root.val);
return Math.max(leftNum,rightNum)+root.val;
}
}
(2)DFS_深度优先
从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,尽量往深处走。
/**剑指offer 55 二叉树的深度
输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例:
给定二叉树 [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回它的最大深度 3 。
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
int res = 0;
public int maxDepth(TreeNode root) {
dfs(root,0);
return res;
}
void dfs(TreeNode root,int count){
//终止条件
if(root==null) return;
count++;
if(root.left!=null){
dfs(root.left,count);
}
if(root.right!=null){
dfs(root.right,count);
}
res = Math.max(res,count);
}
}
- 查找二叉树所有路径
/** leetcode 257 二叉树的所有路径
给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。
示例:
输入:
1
/ \
2 3
\
5
输出: ["1->2->5", "1->3"]
解释: 所有根节点到叶子节点的路径为: 1->2->5, 1->3
*/
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
List<String> res = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
if(root==null) return res;
dfs(root,""+root.val,1); //分别表示二叉树,待加入res的字符串,当前结点深度
return res;
}
void dfs(TreeNode root,String temp,int count){
if(root == null) return;
if(count>1){ //当深度不在原始根节点时,字符串中添加内容
temp = temp+"->"+root.val;
}
count++;
dfs(root.left,temp,count);
dfs(root.right,temp,count);
if(root.left == null && root.right==null){
res.add(temp);
}
}
}
- 二叉树中和为某一值的路径
/**剑指 Offer 34. 二叉树中和为某一值的路径
输入一棵二叉树和一个整数,打印出二叉树中节点值的和为输入整数的所有路径。从树的根节点开始往下一直到叶节点所经过的节点形成一条路径。
示例:
给定如下二叉树,以及目标和 sum = 22,
5
/ \
4 8
/ / \
11 13 4
/ \ / \
7 2 5 1
返回:
[
[5,4,11,2],
[5,8,4,5]
] */
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> res = new ArrayList<>();
if(root==null) return res;
dfs(root,sum,res,new ArrayList<>());
return res;
}
void dfs(TreeNode root,int sum,List<List<Integer>> res,List<Integer> temp){
if(root==null){
if(sumFun(temp)==sum){
res.add(new ArrayList(temp));
}
return;
}
temp.add(root.val);
if(root.left==null){
dfs(root.right,sum,res,temp);
}else if(root.right==null){
dfs(root.left,sum,res,temp);
}else{
dfs(root.left,sum,res,temp);
dfs(root.right,sum,res,temp);
}
temp.remove(temp.size()-1);
}
int sumFun(List<Integer> temp){
int sum = 0;
for(int i=0;i<temp.size();i++){
sum+=temp.get(i);
}
return sum;
}
}
(3)BFS_广度优先
搜索是从根节点开始,沿着树的宽度遍历树的节点。如果所有节点均被访问,则算法中止。
/** 剑指 Offer 32 - I. 从上到下打印二叉树
从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回:
[3,9,20,15,7]
class Solution {
public int[] levelOrder(TreeNode root) {
if (root == null) return new int[]{};
int[] ans = new int[1024];
int counter = 0;
Queue<TreeNode> queue = new LinkedList<TreeNode>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode cur = queue.poll();
ans[counter++] = cur.val;
if (cur.left != null) {
queue.offer(cur.left);
}
if (cur.right != null) {
queue.offer(cur.right);
}
}
return Arrays.copyOf(ans, counter);
}
}