首页 / 算法 / 算法(Java)——二叉树
算法(Java)——二叉树
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了算法(Java)——二叉树,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含10284字,纯文字阅读大概需要15分钟。
内容图文
![算法(Java)——二叉树](/upload/InfoBanner/zyjiaocheng/599/6cdac209580e4695844d9592e0672764.jpg)
二叉树在算法中也是比较常用的数据结构,根据二叉树的遍历算法,在算法题中遇到二叉树经常优先考虑递归算法。同时二叉树中的二叉搜索树也是常用的,主要结合中序遍历有序的特性。
二叉树
二叉树结构:
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
TreeNode(int x) { val = x; }
}
二叉树遍历主要分为前序、中序、后序和层次遍历。二叉树的深度优先搜索(DFS)和广度优先搜索(BFS)。
力扣关于二叉树的算法
1)剑指offer55_Ⅰ:二叉树的深度
题目:输入一棵二叉树的根节点,求该树的深度。
解题思路:递归左右子树的深度,最后比较左右子树深度加1。
算法代码:
class Solution { //递归实现
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
int left = maxDepth(root.left);
int right = maxDepth(root.right);
return Math.max(left,right)+1;
}
}
2)剑指offer55_Ⅱ:平衡二叉树
题目:输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。
解题思路:递归,先找树的深度,再根据树的深度递归判断平衡二叉树。
算法代码:
class Solution {
public boolean isBalanced(TreeNode root) {
if(root==null) return true;
if(Math.abs(getHeigth(root.left)-getHeigth(root.right))<=1){
return isBalanced(root.left) && isBalanced(root.right);
}
return false;
}
public int getHeigth(TreeNode root){
if(root==null) return 0;
return Math.max(getHeigth(root.left),getHeigth(root.right))+1;
}
}
3)剑指offer27:二叉树的镜像
题目:请完成一个函数,输入一个二叉树,该函数输出它的镜像。
例如输入:
4
/ \
2 7
/ \ / \
1 3 6 9
镜像输出:
4
/ \
7 2
/ \ / \
9 6 3 1
解题思路:递归左右子树的镜像,借助个临时节点暂时存储左(右)节点。
算法代码:
class Solution { //递归
public TreeNode mirrorTree(TreeNode root) {
if(root == null){
return root;
}
TreeNode tmp = root.left;
root.left = mirrorTree(root.right);
root.right = mirrorTree(tmp);
return root;
}
}
4)剑指offer68_Ⅰ:二叉树最近公共祖先
题目:给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
解题思路:递归左右子树,判断情况:
- 一个左子树一个右子树
- 都在左子树
- 都在右子树
- 一个是自身,另一个在子树
算法代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root,TreeNode p, TreeNode q) {
if(root == null) return null;
if(p == root || q == root) return root;
TreeNode left = lowestCommonAncestor(root.left,p,q);
TreeNode right = lowestCommonAncestor(root.right,p,q);
if(left!=null && right!=null) return root; //一个左子树一个右子树,最近祖先是root
if(left==null){ //都在左子树或都在右子树
return right;
}else{
return left;
}
}
}
5)剑指offer31:从上到下打印二叉树Ⅰ
题目:从上到下打印出二叉树的每个节点,同一层的节点按照从左到右的顺序打印。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回:
[3,9,20,15,7]
解题思路:使用队列对二叉树进行层次遍历,即广度优先搜索BFS。返回一层,可以直接将节点存储到list中输出。
算法代码:
class Solution {
public int[] levelOrder(TreeNode root) {
if(root==null) return new int[0]; //new int[0] 表示动态分配一个长度为0的int数组
Queue<TreeNode> queue = new LinkedList<TreeNode>(); //创建队列进行层次遍历
ArrayList<Integer> list = new ArrayList<Integer>(); //创建list存储值
queue.offer(root);
while(!queue.isEmpty()){
TreeNode node = queue.poll();
list.add(node.val);
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
int[] res = new int[list.size()]; //将List中的值输出存储到int中
for(int i=0;i<list.size();i++)
{
res[i]=list.get(i);
}
return res;
}
}
6)剑指offer32:从上到下打印二叉树Ⅱ
题目:从上到下按层打印二叉树,同一层的节点按从左到右的顺序打印,每一层打印到一行。
例如:
给定二叉树: [3,9,20,null,null,15,7],
3
/ \
9 20
/ \
15 7
返回其层次遍历结果:
[
[3],
[9,20],
[15,7]
]
解题思路:
- 从上到下按层打印又称为二叉树的广度优先搜索(BFS),BFS通常借助队列的先入先出特性来实现。同上。
- 每层打印到一行,将本层全部节点打印到一行,并将下一层全部节点加入队列,以此类推,即多行打印。
算法代码:
class Solution {
public List<List<Integer>> levelOrder(TreeNode root) {
Queue<TreeNode> queue = new LinkedList<TreeNode>();
List<List<Integer>> list = new ArrayList<>(); //List中再装一个List用来存储最后的结果
if(root!=null) queue.offer(root);
while(!queue.isEmpty()){
ArrayList<Integer> tmp = new ArrayList<Integer>(); //创建tmp存储一行节点
for(int i=queue.size();i>0;i--){
TreeNode node = queue.poll();
tmp.add(node.val);
if(node.left!=null) queue.offer(node.left);
if(node.right!=null) queue.offer(node.right);
}
list.add(tmp); //按行加入到list中
}
return list;
}
}
7)剑指offer28:对称的二叉树
题目:请实现一个函数,用来判断一棵二叉树是不是对称的。如果一棵二叉树和它的镜像一样,那么它是对称的。
例如,二叉树 [1,2,2,3,4,4,3] 是对称的。
1
/ \
2 2
/ \ / \
3 4 4 3
但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:
1
/ \
2 2
\ \
3 3
解题思路:考虑递归,递归root的左右子树。先判断root==null
- 左右节点都为空,true
- 左右一个空,false
- 左右节点不一样,false 值,左右,右左都镜像
算法代码:
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root==null) return true;
return lrbinary(root.left, root.right);
}
public boolean lrbinary(TreeNode root1, TreeNode root2){
if(root1==null && root2==null) return true;
if(root1==null || root2==null) return false;
return root1.val==root2.val && lrbinary(root1.left, root2.right) && lrbinary(root1.right, root2.left);
}
}
其它二叉树算法
1)统计根节点到叶节点的路径的和
算法代码:
public int sumNumbers(TreeNode root) {
return dfs(root,0);
}
public int dfs(TreeNode p,int s){
if(p ==null ){
return 0;
}
if(p.left == null && p.right == null){
return s*10 + p.val;
}
return dfs(p.left, s*10 +p.val) + dfs(p.right, s*10 +p.val);
}
2)统计完全树节点的个数
解题思路:简单的方法是直接统计左右子树高度,若相等,则说明以该节点为跟的子树是满二叉树,直接返回 2^h-1 即可,若不是,则返回 count(左子树)+count(右子树)+1:
算法代码:
public int countNodes(TreeNode root) {
if(root == null) return 0;
TreeNode p = root, q = root;
int hl = 0, hr = 0;
while(p != null){//统计左子树高
p = p.left;
hl ++;
}
while(q != null){//统计右子树高
q = q.right;
hr ++;
}
if(hl == hr) return (1 << hl) - 1;// <=> 2^hl - 1
return 1 + countNodes(root.left) + countNodes(root.right);
}
3)二叉树非递归遍历
a.先序遍历
算法代码:
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>();
if(root == null) return res;
LinkedList<TreeNode> s = new LinkedList<TreeNode>();
while(root != null || !s.isEmpty()){
while(root!=null){
s.push(root);
res.add(root.val);
root = root.left;
}
if(!s.isEmpty()){
root = s.pop();
root = root.right;
}
}
return res;
}
b.中序遍历
算法代码:
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<Integer>(); //结果集
LinkedList<TreeNode> s = new LinkedList<TreeNode>(); //辅助栈
if(root == null) return res;
while(root != null || !s.isEmpty()){
while(root != null){
s.push(root);
root = root.left;
}//此时 root 已经为空了
if(!s.isEmpty()){
root = s.pop();
res.add(root.val);
root = root.right;
}
}
return res;
}
c.后序遍历
(1)先序反转法
先序 : root-left-right ,后序: 需要 left-right-root,所以在先序的时候先放right再放left得到 root-right-left ,然后反转即可。
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<Integer>();
LinkedList<TreeNode> s = new LinkedList<TreeNode>();
if(root == null) return res;
while(root != null || !s.isEmpty()){
while(root != null){
s.push(root);
res.add(root.val);
//不同于 preOrder 这里先右
root = root.right;
}
if(!s.isEmpty()){
root = s.pop();
root = root.left;
}
}
Collections.reverse(res);
return res;//翻转再返回结果就好
}
(2)双栈法
其实就是利用两个栈构造出 root-right-left 的序列,然后出栈即为反转了,不如上一个快
给两个栈 s1,s2;
将root入s1;
若s1非空,将s1的栈顶元素赋给root,然后将root入s2;然后先将root左结点入s1,后将root右结点入s1,顺序不能错;
若s2非空,出s2,就获得后序遍历;
public List<Integer> postorderTraversal(TreeNode root) {
LinkedList<Integer> res = new LinkedList<Integer>();
//两个 stack ,一个 stack按跟左右进栈
//放到另一个stack,形成 跟 右左,然后出栈即得到结果
LinkedList<TreeNode> s1 = new LinkedList<TreeNode>();
LinkedList<TreeNode> s2 = new LinkedList<TreeNode>();
if(root == null) return res;
s1.push(root);
while(!s1.isEmpty()){
root = s1.pop();
s2.push(root);
if(root.left != null)
s1.push(root.left);
if(root.right!= null)
s1.push(root.right);
}
while(!s2.isEmpty())
res.add(s2.pop().val);
return res;
}
d.层次遍历
这里直接给出层次遍历的分层打印方法,用parent 与 children 两个变量分别统计当前层与下一层的节点数,每次拿出一个节点,当前层计数 parent-1;当parent == 0;输出一个换行符,然后另 parent = children ,children =0 即可。
public void lfs(Node root) {
if(root == null) return ;
//十分万能的,当队列或者栈都可以用的 LinkedList。
LinkedList<Node> queue = new LinkedList<Node>();
queue.add(root);
int parent = 1;
int children = 0;
while(!queue.isEmpty()){
System.out.print(queue.peek() +" ");
Node cur = queue.removeFirst();
--parent;
if(cur.left != null) {queue.addLast(cur.left ); ++children;}
if(cur.right != null) {queue.addLast(cur.right); ++children;}
if(parent == 0) {System.out.println(); parent = children; children = 0;}
}
}
二叉搜索树
二叉搜索树是一种节点值之间具有一定数量级次序的二叉树,对于树中每个节点:
- 若其左子树存在,则其左子树中每个节点的值都不大于该节点值;
- 若其右子树存在,则其右子树中每个节点的值都不小于该节点值。
因此,二叉搜索树的特点:中序遍历是顺序的。是经常用的考察点。
力扣关于二叉搜素树的算法
1)剑指offer54:二叉搜索树的第k大节点
题目:给定一棵二叉搜索树,请找出其中第k大的节点。
解题思路:中序遍历顺序,左根右从小到大,逆序右根左。
示例 1:
输入: root = [3,1,4,null,2], k = 1
3
/ \
1 4
\
2
输出: 4
示例 2:
输入: root = [5,3,6,2,4,null,null,1], k = 3
5
/ \
3 6
/ \
2 4
/
1
输出: 4
算法代码:
class Solution { //中序遍历左根右顺序,右根左逆序,第k大,所以用逆序
private int ans=0, count=0;
private void Middle(TreeNode root, int k){
if(root == null) return;
Middle(root.right,k);
if(++count==k){
ans=root.val;
}
Middle(root.left,k);
}
public int kthLargest(TreeNode root, int k) {
Middle(root,k);
return ans;
}
}
2)剑指offer68_Ⅱ:二叉搜索树的最近公共祖先
题目:给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
解题思路:要利用二叉搜索树的性质,左子树都比根小,右子树都比根大,与一般的二叉树相比,只需要与根的值比较就可以判断左右子树,不需要具体到节点。
算法代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p,TreeNode q) {
if(root==null) return null;
if(root.val>p.val && root.val>q.val) //说明在左子树
return lowestCommonAncestor(root.left, p, q);
else if(root.val<p.val && root.val<q.val) //说明在右子树
return lowestCommonAncestor(root.right, p, q);
else //各在一边
return root;
}
}
内容总结
以上是互联网集市为您收集整理的算法(Java)——二叉树全部内容,希望文章能够帮你解决算法(Java)——二叉树所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。