Java数据结构-二叉树(BinaryTree)(九)

*/

private Node delete(Node node,Key key){

if(node == null){

return null;

}

int i = key.compareTo(node.key);

if(i<0){

// 如果要查询的key小于当前结点的key,则继续找当前结点的左子结点

node.left = delete(node.left,key);

}else if(i>0){

// 如果要查询的key大于当前结点的key,则继续找当前结点的右子结点;

node.right = delete(node.right,key);

}else {

// 如果当前节点的右节点为空,则返回左节点

if(node.right == null){

return node.left;

}

// 如果当前节点的左节点为空,则返回右节点

if(node.left == null){

return node.right;

}

// 如果两个节点都存在,则去找 右节点的最小值;

Node midNode = node.right;

while (midNode.left != null){

midNode = midNode.left;

}

// 删除右节点上的最小节点

// 找到右节点最小值

Node temp = node.right;

// 如果右节点temp就是最小节点,表示temp没有左节点了,那就把temp的右节点赋值为 node 的右节点。达到删除teme 的效果

if(temp.left == null){

node.right = temp.right;

}else {

// 否则循环寻找最小值,并且删除最小值

while (temp.left != null){

// 如果右节点最小节点 temp ,存在右节点则把右节点赋值给temp,否则为空,并返回

if (temp.left.right != null) {

temp.left = temp.left.right;

break;

}else if(temp.left.left == null){

temp.left = null;

}else {

temp = temp.left;

}

}

}

midNode.left = node.left;

midNode.right = node.right;

node = midNode;

N–;

}

return node;

}

}

查找二叉树中最小的键

public Key min(){

return min(root).key;

}

// 找出指定树x中最小的键所在的结点

private Node min(Node x){

if(x.left!=null){

return min(x.left);

}else{

return x;

}

}

查找二叉树中最大的键

//找出整个树中最 大的键

public Key max(){

return max(root).key;

}

// 找出指定树x中最小的键所在的结点

private Node max(Node x){

if(x.right!=null){

return max(x.right);

}else{

return x;

}

}

二叉树的基础遍历

二叉树的遍历主要有三种:

  1. 前序遍历;

先访问根结点,然后再访问左子树,最后访问右子树 ;

  1. 中序遍历;

先访问左子树,中间访问根节点,最后访问右子树 ;

  1. 后序遍历;

先访问左子树,再访问右子树,最后访问根节点;

举个例子:

先(根)序遍历(根左右):E B A D C G F H

中(根)序遍历(左根右) : A B C D E F G H

后(根)序遍历(左右根) : A C D B F H G E

代码实现

  • 前序遍历

// 前序遍历 先访问根结点,然后再访问左子树,最后访问右子树 ;

public MyQueue preErgodic(){

MyQueue keys = new MyQueue<>();

preErgodic(this.root,keys);

return keys;

};

private void preErgodic(Node x,MyQueue keys){

if(x == null){

return;

}

keys.push(x.key);

if(x.left!= null){

preErgodic(x.left,keys);

}

if(x.right !=null){

preErgodic(x.right,keys);

}

}

  • 中序遍历

// 中序遍历 先访问左子树,中间访问根节点,最后访问右子树 ;

public MyQueue midErgodic(){

MyQueue keys = new MyQueue<>();

midErgodic(this.root,keys);

return keys;

}

private void midErgodic(Node x,MyQueue keys){

if(x == null){

return;

}

if(x.left!= null){

midErgodic(x.left,keys);

}

keys.push(x.key);

if(x.right !=null){

midErgodic(x.right,keys);

}

}

  • 后序遍历

// 后续遍历 先访问左子树,再访问右子树,最后访问根节点;

public MyQueue afterErgodic(){

MyQueue keys = new MyQueue<>();

afterErgodic(this.root,keys);

return keys;

}

private void afterErgodic(Node x,MyQueue keys){

if(x == null){

return;

}

if(x.left!= null){

afterErgodic(x.left,keys);

}

if(x.right !=null){

afterErgodic(x.right,keys);

}

keys.push(x.key);

}

二叉树的层序遍历

层序遍历,就是从根节点(第一层)开始,依次向下,获取每一层所有结点的值,有二叉树如下:

层序遍历的结果是:EBGADFHC

实现步骤:

  1. 创建队列,存储每一层的结点;

  2. 使用循环从队列中弹出一个结点:

2.1 获取当前结点的key;

2.2 如果当前结点的左子结点不为空,则把左子结点放入到队列中;

2.3 如果当前结点的右子结点不为空,则把右子结点放入到队列中;

// 有所谓的层序遍历,就是从根节点(第一层)开始,依次向下,获取每一层所有结点的值,二叉树如下:

public MyQueue layerErgodic(){

MyQueue keys = new MyQueue<>();

MyQueue nodes = new MyQueue<>();

nodes.push(root);

while (!nodes.isEmpty()){

Node node = nodes.get();

keys.push(node.key);

if(node.left!= null){

nodes.push(node.left);

}

if(node.right != null){

nodes.push(node.right);

}

}

return keys;

}

二叉树的最大深度

实现步骤:

  1. 如果根结点为空,则最大深度为0;

  2. 计算左子树的最大深度;

  3. 计算右子树的最大深度;

  4. 当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1;

代码实现

public int maxDepth(){

return maxDepth(root);

}//计算指定树x的最大深度

private int maxDepth(Node x){

//1.如果根结点为空,则最大深度为0;

if(x==null){

return 0;

}

int max=0;

int maxL=0;

int maxR=0;

//2.计算左子树的最大深度;

if(x.left!=null){

maxL=maxDepth(x.left);

}

//3.计算右子树的最大深度;

if(x.right!=null){

maxR=maxDepth(x.right);

}

//4.当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1

max=maxL>maxR?maxL+1:maxR+1;

return max;

}

实际案例(折纸问题)

请把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从纸条的下边向上方连续对折2 次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕;

给定一个输入参数N,代表纸条都从下边向上方连续对折N次,请从上到下打印所有折痕的方向例如:N=1时,打印: down;N=2时,打印: down down up

分析:我们把对折后的纸张翻过来,让粉色朝下,这时把第一次对折产生的折痕看做是根结点,那第二次对折产生的下折痕就是该结点的左子结点,而第二次对折产生的上折痕就是该结点的右子结点,这样我们就可以使用树型数据结构来描述对折后产生的折痕。

这棵树有这样的特点:

  1. 根结点为下折痕;

  2. 每一个结点的左子结点为下折痕;

  3. 每一个结点的右子结点为上折痕;

实现步骤:

  1. 定义结点类

  2. 构建深度为N的折痕树;

  3. 使用中序遍历,打印出树中所有结点的内容;

构建深度为N的折痕树:

  1. 第一次对折,只有一条折痕,创建根结点;

  2. 如果不是第一次对折,则使用队列保存根结点;

  3. 循环遍历队列:

3.1 从队列中拿出一个结点;

3.2 如果这个结点的左子结点不为空,则把这个左子结点添加到队列中;

3.3 如果这个结点的右子结点不为空,则把这个右子结点添加到队列中;

3.4 判断当前结点的左子结点和右子结点都不为空,如果是,则需要为当前结点创建一个值为down的左子结点,一个值为up的右子结点。

代码实现

public class Node{

String item;

Node left;

Node right;

public Node(String item,Node left,Node right){

this.item = item;

this.left = left;

this.right = right;

}

}

public void printTree(Node tree){

if(tree == null){

return;

}

printTree(tree.left);

System.out.println(tree.item);

printTree(tree.right);

}

public Node createTree(int number){

Node root = null;

for (int i = 0; i < number; i++) {

if(i == 0){

// 1. 如果是第一次对折,只会有一条折痕,创建根节点

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

image

item;

this.left = left;

this.right = right;

}

}

public void printTree(Node tree){

if(tree == null){

return;

}

printTree(tree.left);

System.out.println(tree.item);

printTree(tree.right);

}

public Node createTree(int number){

Node root = null;

for (int i = 0; i < number; i++) {

if(i == 0){

// 1. 如果是第一次对折,只会有一条折痕,创建根节点

总结

虽然我个人也经常自嘲,十年之后要去成为外卖专员,但实际上依靠自身的努力,是能够减少三十五岁之后的焦虑的,毕竟好的架构师并不多。

架构师,是我们大部分技术人的职业目标,一名好的架构师来源于机遇(公司)、个人努力(吃得苦、肯钻研)、天分(真的热爱)的三者协作的结果,实践+机遇+努力才能助你成为优秀的架构师。

如果你也想成为一名好的架构师,那或许这份Java成长笔记你需要阅读阅读,希望能够对你的职业发展有所帮助。

[外链图片转存中…(img-U2PidxZT-1714373217317)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值