一、思路
求二叉树最大深度,就是最长的从上到下的一条线右几个节点。
从根节点出发,递归调用算法,求左右子树的最大深度,每次计算深度都是取左右子树深度较大的一个+1,当递归到最后时,递归就会慢慢返回,原问题得到结果。
二、Java代码实现
直接看42行代码即可
package mypackage;
import java.util.Iterator;
//队列类,用链表实现,遍历二叉树时需要用来装键
class Queue<T> implements Iterable<T>{
// 节点个数,头节点,尾节点
private int N;
private Node head;
private Node last;
//节点类
public class Node {
public T data;
public Node next;
public Node(T data, Node next) {
this.data = data;
this.next = next;
}
}
//构造方法,初始化
public Queue() {
this.N = 0;
this.head = new Node(null,null);
this.last = null;
}
//队列长度
public int size(){
return N;
}
//队列是否为空
public boolean isEmpty(){
return N==0;
}
//入队列
public void enqueue(T data){
// 如果队列为空,说明尾节点为空,让新节点为尾节点,头借点指向尾节点
if (isEmpty()){
last=new Node(data,null);
head.next=last;
// 如果队列不为空,让新节点为尾节点,老的尾节点指向新尾节点
}else {
Node oldlast=last;
last=new Node(data,null);
oldlast.next=last;
}
// 最后元素+1
N++;
}
//出队列,注意先入先出,每次出的节点就是head指向的第一个节点,然后让head只想第二个节点即可
// 且注意,如果队列为空,要将last=null
public T dequeue(){
// 如果为空,返回null
if (isEmpty()){
return null;
// 如果不为空,让head只想第二个节点,元素-1,且如果队列为空,要将last=null
}else {
Node oldfirst=head.next;
head.next=oldfirst.next;
N--;
if (isEmpty()){
last=null;
}
// 返回弹出的元素
return oldfirst.data;
}
}
// 遍历
@Override
public Iterator iterator() {
return new QIterator();
}
// 创建一个内部类实现Iterator接口
public class QIterator implements Iterator {
// 定义一个遍历的节点
private Node n;
public QIterator() {
// 初始化为0索引位置
this.n = head;
}
//重写两个方法
@Override
public boolean hasNext() {
// 这个方法判断是否超出最大索引,如果超出会停止遍历
return n.next != null;
}
@Override
public Object next() {
// 这个方法会遍历得每个节点
n = n.next;
return n.data;
}
}
}
//二叉树类
class Btree<key extends Comparable, value> {
public Node root;
private int N;
// 节点类,包括键值对,左子节点和右子节点
public class Node {
public key key;
public value value;
public Node left;
public Node right;
public Node(key key, value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
// 获取元素个数
public int size() {
return N;
}
// 向树中添加元素,这个是对外调用的方法,从root开始递归,添加元素
// 核心是下面的put(Node node, key key, value value)方法
public void put(key key, value value) {
root = put(root, key, value);
}
// 向指定树中添加元素
public Node put(Node node, key key, value value) {
// 如果子树node为空,直接返回新节点
// 其实这个是递归结束的条件之一
if (node == null) {
N++;
return new Node(key, value, null, null);
}
// 判断如果key大于node.key,向右子树递归
int compare1 = key.compareTo(node.key);
if (compare1 > 0) {
// 注意为什么不put(node.right, key, value),还要加上node.right = ?
// 这样才能实现节点节点之间的连接,因为put(node.right, key, value);返回的是node.right
node.right = put(node.right, key, value);
// 判断如果key小于node.key,向左子树递归
} else if (compare1 < 0) {
node.left = put(node.left, key, value);
// 判断如果key==node.key,替换这个值
} else {
node.value = value;
}
// 返回这个节点
return node;
}
// 查询某key的值这个是对外调用的方法,从root开始递归,查询元素
// 核心是下面的get(Node node, key key)方法
public value get(key key){
return get(root,key);
}
// 查询指定子树某key的值
public value get(Node node, key key){
// 如果为空,无法找到,返回null
if (node==null){
return null;
}
int compare1 = key.compareTo(node.key);
// 判断如果key大于node.key,向右子树递归
if (compare1 > 0) {
return get(node.right,key);
// 判断如果key小于node.key,向左子树递归
} else if (compare1 < 0) {
return get(node.left,key);
// 判断如果key==node.key,找到这个值
} else {
return node.value;
}
}
// 删除操作,比较麻烦的操作,
// 主要是因为删除一个节点后,原本这个节点有左右子树的话,要找一个节点去填充这个节点,
// 找哪个节点去填充呢,找右子数种最小的节点,这个节点能满足大于左子树种中的所有节点,小于右子树的所有节点
// 如果这个节点没有右子树,只有左子树,让上一个节点连接这个节点的下一个节点
// 如果没有左、右子树,直接删除就行了
public void delete(key key){
root=delete(root,key);
}
// 删除指定数种的key对应的value,并返回删除后的新树
public Node delete(Node node,key key){
// 如果节点不存在就返回null
if (node==null){
return null;
}
int compare1 = key.compareTo(node.key);
// 判断如果key大于node.key,向右子树递归
if (compare1 > 0) {
// 递归,结果返回删除后的新树,让node.right指向返回的新树
node.right = delete(node.right, key);
// 判断如果key小于node.key,向左子树递归
} else if (compare1 < 0) {
// 递归,结果返回删除后的新树,让node.left指向返回的新树
node.left = delete(node.left, key);
// 判断如果key==node.key,找到了这个要删除的值,之后要找到一个值来填充这个位置
} else {
N--;
// 如果没有右子树,返回的新树为当前结点的左子树即可
if (node.right==null){
return node.left;
}
// 如果没有左子树,返回的新树为当前结点的右子树即可
if (node.left==null){
return node.right;
}
// 如果有左子树和右子树,去找右子树中的最小的值
Node minNode=node.right;
// 一直找左子树就是最小的值,如果没有左子树,那么当前就是最小的值
while (minNode.left!=null){
minNode=minNode.left;
}
// 上面找到了最小节点,现在要删除这个最小节点,然后放到待删除的节点处覆盖
// 怎么删除呢,就是要找到这个节点的上一个节点,让他指向null
// 因此关键就是要找到最小节点的上一个节点
Node cur=node.right;
Node pre=node;
// 如果待删除节点的右子树有左节点,那么就去找这个最小的左节点
// 然后让最小节点的上一个节点指向null
if (cur.left!=null){
while (cur.left!=null){
pre=cur;
cur=cur.left;
}
pre.left=null;
// 如果待删除节点的右子树没有左节点,这个时候又要分两种情况
// 一种情况是没有左节点且没有右节点,这时让node.right指向null即可
// 另外一种情况是没有左节点但是有右节点,因此要让node.right指向node.right.right
}else {
if (cur.right==null){
pre.right=null;
}else {
pre.right=cur.right;
}
}
// 删除最小元素后,让最小元素左子树为待删除元素的左子树,让最小元素右子树为待删除元素的右子树
minNode.left=node.left;
minNode.right=node.right;
// 这个是让node的上一个节点指向这个最小的节点,此时这个最小的节点已经替换了待删除的元素的位置
node= minNode;
}
// 这个返回值至关重要,作为这个方法的返回值,
// 用作上一级的node.right = delete(node.right, key)或者node.left = delete(node.left, key)的返回值
return node;
}
// 查找树中最小的key,调用重载方法
public key getmin(){
return getmin(root).key;
}
// 查找指定树中最小的key的节点
// 递归去找右子树
public Node getmin(Node node){
if (node.left!=null){
return getmin(node.left);
}else {
return node;
}
}
// 查找树中最大的key,调用重载方法
public key getmax(){
return getmax(root).key;
}
// 查找指定树中最大的key的节点
// 递归去找右子树
public Node getmax(Node node){
if (node.right!=null){
return getmax(node.right);
}else {
return node;
}
}
// 前序遍历,顺序:根节点-左子树-右子树
// 这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
public Queue<key> preErgodic(){
// 创建一个队列,用于装二叉树的所有节点的键
Queue<key> keys=new Queue<>();
// 调用重载方法
preErgodic(root,keys);
// 方法运行完毕后,返回队列keys就是二叉树所有的键了
return keys;
}
// 重载方法,这个是核心,期间用到了递归,很重要
public void preErgodic(Node node,Queue<key> keys){
// 如果为空,直接返回
if (node==null){
return;
}else {
// 先将根节点的键装入队列
keys.enqueue(node.key);
// 如果有左子树,递归调用这个方法
if (node.left!=null){
preErgodic(node.left,keys);
}
// 如果有右子树,递归调用这个方法
if (node.right!=null){
preErgodic(node.right,keys);
}
}
}
// 中序遍历,顺序:左子树-根节点-右子树
// 这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
public Queue<key> midErgodic(){
// 创建一个队列,用于装二叉树的所有节点的键
Queue<key> keys=new Queue<>();
// 调用重载方法
midErgodic(root,keys);
// 方法运行完毕后,返回队列keys就是二叉树所有的键了
return keys;
}
// 重载方法,这个是核心,期间用到了递归,很重要
public void midErgodic(Node node,Queue<key> keys){
// 如果为空,直接返回
if (node==null){
return;
}else {
// 如果有左子树,递归调用这个方法
if (node.left!=null){
midErgodic(node.left,keys);
}
// 根节点的键装入队列
keys.enqueue(node.key);
// 如果有右子树,递归调用这个方法
if (node.right!=null){
midErgodic(node.right,keys);
}
}
}
// 后序遍历,顺序:左子树-右子树-根节点
// 这个是对外调用的方法,底层是preErgodic(Node node,Queue<key> keys)方法
public Queue<key> afterErgodic(){
// 创建一个队列,用于装二叉树的所有节点的键
Queue<key> keys=new Queue<>();
// 调用重载方法
afterErgodic(root,keys);
// 方法运行完毕后,返回队列keys就是二叉树所有的键了
return keys;
}
// 重载方法,这个是核心,期间用到了递归,很重要
public void afterErgodic(Node node,Queue<key> keys){
// 如果为空,直接返回
if (node==null){
return;
}else {
// 如果有左子树,递归调用这个方法
if (node.left!=null){
afterErgodic(node.left,keys);
}
// 如果有右子树,递归调用这个方法
if (node.right!=null){
afterErgodic(node.right,keys);
}
// 根节点的键装入队列
keys.enqueue(node.key);
}
}
// 层序遍历
public Queue<key> layerErgodic(){
//创建2个队列,用于装二叉树的所有节点的键和节点
Queue<key> keys=new Queue<>();
Queue<Node> nodes=new Queue<>();
// 先把root根节点放入队列中
nodes.enqueue(root);
// 开始循环,这里是重点
// 如果nodes队列不为空,弹出一个节点,注意是先进先出的
// 然后将这个节点的键装入keys队列
// 判断如果有左节点,将左节点装入node队列
// 判断如果有右节点,将右节点装入node队列
// 注意每循环一次,其实n都是不一样的
while(!nodes.isEmpty()){
Node n=nodes.dequeue();
keys.enqueue(n.key);
if (n.left!=null){
nodes.enqueue(n.left);
}
if (n.right!=null){
nodes.enqueue(n.right);
}
}
// 循环完毕后返回整个keys队列
return keys;
}
// 寻找树的最大深度,即从上到下,一条线最多几个节点
// 对外调用方法,核心是maxDepth(Node node)重载方法
public int maxDepth(){
return maxDepth(root);
}
// 如果node为空,返回0
// 如果有左右节点,递归
public int maxDepth(Node node){
if (node==null){
return 0;
}
// 先定义三个数,用于记录总深度,左子树深度和右子树深度
int max=0;
int maxleft=0;
int maxright=0;
if (node.left!=null){
maxleft=maxDepth(node.left);
}
if (node.right!=null){
maxright=maxDepth(node.right);
}
// 取最大的一个,+1,就是指定数的深度
max=Math.max(maxleft,maxright)+1;
// 最后返回总深度
return max;
}
}
//测试
public class MyJava {
public static void main(String[] args) {
Btree<Integer,String>btree=new Btree();
// 插入元素
btree.put(6,"刘备");
btree.put(4,"关羽");
btree.put(9,"张飞");
btree.put(1,"曹操");
btree.put(5,"吕布");
btree.put(7,"黄忠");
btree.put(10,"赵云");
System.out.println("插入完毕后元素的个数为:"+btree.size());
// 前序遍历
Queue<Integer> preKeys=btree.preErgodic();
System.out.print("前序遍历的结果:");
for (Integer prekeys:preKeys){
System.out.print(prekeys+",");
}
System.out.println();
System.out.println("--------------------------------");
// 中序遍历
Queue<Integer> midKeys=btree.midErgodic();
System.out.print("中序遍历的结果:");
for (Integer midkeys:midKeys){
System.out.print(midkeys+",");
}
System.out.println();
System.out.println("--------------------------------");
// 后序遍历
Queue<Integer> afterKeys=btree.afterErgodic();
System.out.print("后序遍历的结果:");
for (Integer afterkeys:afterKeys){
System.out.print(afterkeys+",");
}
System.out.println();
System.out.println("--------------------------------");
//层序遍历
Queue<Integer> layerKeys=btree.layerErgodic();
System.out.print("层序遍历的结果:");
for (Integer layerkeys:layerKeys){
System.out.print(layerkeys+",");
}
System.out.println();
System.out.println("--------------------------------");
// 查询元素
System.out.println("查询key=4元素:"+btree.get(4));
// 找最小的key
System.out.println("最小的key:"+btree.getmin());
// 找最大的key
System.out.println("最大的key:"+btree.getmax());
// 删除元素
btree.delete(4);
System.out.println("删除后元素个数:"+btree.size());
System.out.println("删除后查询4元素:"+btree.get(4));
// 求最大深度
System.out.println("树最大深度:"+btree.maxDepth());
}
}