java 3 4_Java-3/4_树.md at master · yrcDream/Java-3 · GitHub

二叉树

二叉树具有唯一根节点

二叉树每个节点最多有两个孩子,最多有一个父亲

二叉树具有天然递归结构

719326c0f5a29800a0bab2897e81697b.png

二叉树不一定是 “满” 的:一个节点也是二叉树、空节点也是二叉树

二叉搜索树(BST)

48d23d448337ecbeb97e540a88ce0b15.png

BST 的基本功能

public class BST> {

private Node root;

private int size;

public BST(){

root=null;

size=0;

}

public int size(){

return size;

}

public boolean isEmpty(){

return size==0;

}

private class Node{

public E e;

public Node left,right;

public Node(E e){

this.e=e;

this.left=null;

this.right=null;

}

}

}

添加元素

思路

对于如下的 BST,向其中添加 28:

14cdede84a4f2ea295d6465edfd3c0a5.png

28 与根结点 41 比较,应该插入左子树

9b17363af2f360a286571f673d60990b.png

28 与左子树的根结点 22 比较,应该插入该结点的右子树

51a1ac604f086a309315b3c62ca700d4.png

28 与 33 比较,应该插入该结点的左子树,该结点的左子树为空,则将值为 28 的结点添加到该结点的左子树中

c5de971927364990a2fe31d5a020aa9c.png

最终将 28 添加到该 BST 中

0e19b899275d3ba3f54e4f929b310d45.png

代码

//向BST中添加新元素e

public void add(E e){

if(root==null){

root=new Node(e);

size++;

return;

}else{

add(root,e);

}

}

//向以node为根节点的BST树种插入元素e

private void add(Node node,E e){

if(e.equals(node.e)){

//插入元素与根节点相等,直接返回

return;

}else if(e.compareTo(node.e)<0 && node.left==null){

node.left=new Node(e);

size++;

return;

}else if(e.compareTo(node.e)>0 && node.right==null){

node.right=new Node(e);

size++;

return;

}

if(e.compareTo(node.e)<0){

add(node.left,e);

}else{ //e.compareTo(node.e)>0

add(node.right,e);

}

}

改进添加元素

前文中的添加操作,对 root==null 的情况进行了特殊处理,并且 add(Node,E) 中的递归条件也太冗长了。

这里我们写一个 add() 方法,返回插入新节点后该 BST 的根节点

//向BST中添加新元素e

public void add(E e){

root=add(root,e);

}

//向以node为根节点的BST树种插入元素e

//返回插入新元素后该BST的根

private Node add(Node node,E e){

if(node==null){

size++;

return new Node(e);

}

//注意:对于e.equals(node.e)不做处理

if(e.compareTo(node.e)<0){

node.left=add(node.left,e);

}else if(e.compareTo(node.e)>0){

node.right=add(node.right,e);

}

return node;

}

查询元素

//查看BST中是否包含元素e

public boolean contains(E e){

return contains(root,e);

}

//查看以node为根节点的BST中是否包含元素e

private boolean contains(Node node,E e){

if(node==null){

return false;

}

if(e.compareTo(node.e)==0){

return true;

}else if(e.compareTo(node.e)<0){

return contains(node.left,e);

}else{

return contains(node.right,e);

}

}

遍历元素(递归形式)

前序遍历

//BST的前序遍历

public void preOrder(){

preOrder(root);

}

private void preOrder(Node node){

if(node==null){

return;

}

System.out.println(node.e);

preOrder(node.left);

preOrder(node.right);

}

/**

* 利用前序遍历打印 BST

*/

@Override

public String toString() {

StringBuilder res=new StringBuilder();

generateBST(root,0,res);

return res.toString();

}

//生成以node为根节点,深度为depth的描述二叉树的字符串(利用前序遍历)

private void generateBST(Node node,int depth,StringBuilder res){

if(node==null){

res.append(generateDepth(depth)+"null\n");

return;

}

res.append(generateDepth(depth)+node.e+"\n");

generateBST(node.left,depth+1,res);

generateBST(node.right,depth+1,res);

}

private String generateDepth(int depth){

StringBuilder res=new StringBuilder();

for(int i=0;i

res.append("--");

}

return res.toString();

}

中序遍历

//BST的中序遍历

public void inOrder(){

inOrder(root);

}

private void inOrder(Node node){

if(node==null){

return;

}

inOrder(node.left);

System.out.println(node.e);

inOrder(node.right);

}

后序遍历

//BST的后序遍历

public void postOrder(){

postOrder(root);

}

private void postOrder(Node node){

if(node==null){

return;

}

postOrder(node.left);

postOrder(node.right);

System.out.println(node.e);

}

遍历元素(非递归形式)

//枚举命令,GO表示访问元素,COUT表示打印元素

private enum Command{GO,COUT};

private class StackNode{

Command command;

Node node;

StackNode(Command command,Node node){

this.command=command;

this.node=node;

}

}

前序遍历

//BST的前序遍历(非递归方式)

public void preOrderNR(){

preOrderNR(root);

}

private void preOrderNR(Node node){

if(node==null){

return;

}

Stack stack=new Stack<>();

stack.push(new StackNode(Command.GO,node));

while(!stack.isEmpty()){

StackNode stackNode=stack.pop();

Node n=stackNode.node;

Command command=stackNode.command;

if(command==Command.COUT){

System.out.println(stackNode.node.e);

}else{

if(n.right!=null){

stack.push(new StackNode(Command.GO,n.right));

}

if(n.left!=null){

stack.push(new StackNode(Command.GO,n.left));

}

stack.push(new StackNode(Command.COUT,n));

}

}

}

中序遍历

//BST的中序遍历(非递归方式)

public void inOrderNR(){

inOrderNR(root);

}

private void inOrderNR(Node node){

if(node==null){

return;

}

Stack stack=new Stack<>();

stack.push(new StackNode(Command.GO,node));

while(!stack.isEmpty()){

StackNode stackNode=stack.pop();

Node n=stackNode.node;

Command command=stackNode.command;

if(command==Command.COUT){

System.out.println(stackNode.node.e);

}else{

if(n.right!=null){

stack.push(new StackNode(Command.GO,n.right));

}

stack.push(new StackNode(Command.COUT,n));

if(n.left!=null){

stack.push(new StackNode(Command.GO,n.left));

}

}

}

}

后序遍历

//BST的后序遍历(非递归方式)

public void postOrderNR(){

postOrderNR(root);

}

private void postOrderNR(Node node){

if(node==null){

return;

}

Stack stack=new Stack<>();

stack.push(new StackNode(Command.GO,node));

while(!stack.isEmpty()){

StackNode stackNode=stack.pop();

Node n=stackNode.node;

Command command=stackNode.command;

if(command==Command.COUT){

System.out.println(stackNode.node.e);

}else{

stack.push(new StackNode(Command.COUT,n));

if(n.right!=null){

stack.push(new StackNode(Command.GO,n.right));

}

if(n.left!=null){

stack.push(new StackNode(Command.GO,n.left));

}

}

}

}

层序遍历

//BST的层序遍历

public void levelOrder(){

Queue q=new LinkedList<>();

q.add(root);

while(!q.isEmpty()){

Node node=q.remove();

System.out.println(node.e);

if(node.left!=null){

q.add(node.left);

}

if(node.right!=null){

q.add(node.right);

}

}

}

删除元素

删除最小值和最大值

寻找 BST 中的最小元素和最大元素

//寻找BST中的最小元素

public E min(){

if(size==0){

throw new IllegalArgumentException("BST is emmpty");

}

return min(root).e;

}

private Node min(Node node){

if(node.left==null){

return node;

}

return min(node.left);

}

//寻找BST中的最大元素

public E max(){

if(size==0){

throw new IllegalArgumentException("BST is emmpty");

}

return max(root).e;

}

private Node max(Node node){

if(node.right==null){

return node;

}

return max(node.right);

}

删除BST中的最大值和最小值

情况一:最小值是叶子节点。直接删除该节点

2fe44744e4dd90cbfd6dad7af7c55f1c.png

461c526976d085fc208413d5dc0edb96.png

情况二:最小值有右子树。删除该节点,再将该节点的右子树连接到原来的 BST 中

251641e065fbcb9eb9b2305771d5a398.png

46b798b157785a1fd70eb1cee91f1215.png

代码:

//删除BST中的最小值

public E delMin(){

E res=min();

delMin(root);

return res;

}

//删除以node为根结点的BST中的最小值元素

//这里考虑最小值只有右子树的情况,因为叶子节点是其特例

private Node delMin(Node node){

if(node.left==null){

Node nodeRight=node.right;

node.right=null;

size--;

return nodeRight;

}

node.left=delMin(node.left);

return node;

}

//删除BST中的最大值

public E delMax(){

E res=max();

delMax(root);

return res;

}

//删除以node为根结点的BST中的最大值元素

private Node delMax(Node node){

if(node.right==null){

Node nodeLeft=node.left;

node.left=null;

size--;

return nodeLeft;

}

node.right=delMax(node.right);

return node;

}

删除任意元素

删除只有左孩子的节点。直接删除即可

7dc017c0587bda0209507739b8eeaa88.png

8fbb222c8cb2149b703f747fb3e0e61f.png

删除只有右孩子的节点。直接删除即可

35eaba7f0bb4221d20cb96bf3a498363.png

2fb47bfe7c5db901445c80f5f72f4d33.png

删除左右孩子都有的节点

db4a4149a2bb3286152efd9c550dcc50.png

Habbard-Deletion:

d 是待删除的节点,s 节点是 d 的后继,也就是 d 的右子树的最小值所在的节点。

注意:这里选择左子树的最大值所在的节点效果也是相同的。

bbc8a7b8459666dcfd580ceaa358629f.png

使用 s 代替 d

92ca4b4474cf5e507a2a53b07cbe7f9a.png

最后删除 d 节点

代码

//删除BST中任意元素

public void del(E e){

root=del(root,e);

}

private Node del(Node node,E e){

if(node==null){

return null;

}

if(e.compareTo(node.e)<0){

node.left=del(node.left,e);

return node;

}else if(e.compareTo(node.e)>0){

node.right=del(node.right,e);

return node;

}else{

//节点node就是要删除的节点

//该节点只右有子树

if(node.left==null){

Node rightNode=node.right;

node.right=null;

size--;

return rightNode;

}

//该节点只有左子树

if(node.right==null){

Node leftNode=node.left;

node.left=null;

size--;

return leftNode;

}

//删除既有左子树又有右子树的节点

Node s=min(node.right);

s.right=delMin(node.right);

s.left=node.left;

//删除node

node.left=node.right=null;

return s;

}

}

哈夫曼树

哈夫曼树的构造过程

首先生成一颗哈夫曼树,每次生成过程中选取频率最少的两个节点,生成一个新节点作为它们的父节点,并且新节点的频率为两个节点的和。选取频率最少的原因是,生成过程使得先选取的节点位于树的更低层,那么需要的编码长度更长,频率更少可以使得总编码长度更少。

生成编码时,从根节点出发,向左遍历则添加二进制位 0,向右则添加二进制位 1,直到遍历到叶子节点,叶子节点代表的字符的编码就是这个路径编码。

举例:

给定了四个结点a,b,c,d,权值分别为7,5,2,4。

bdd001ce97dc064c43a2d90eb411a3e7.png

找出现有权值中最小的两个,2 和 4 ,相应的结点 c 和 d 构建一个新的二叉树,树根的权值为 2 + 4 = 6,同时将原有权值中的 2 和 4 删掉,将新的权值 6 加入。

3c69ffae75ae95d36628202d608e5bd2.png

重复之前的步骤。

f39c2bc5c25a4f3d42cee06773bd6495.png

所有的结点构建成了一个全新的二叉树,这就是哈夫曼树。

c5b2e8fef8ff436a90e50cd1aa1d1c2d.png

代码

public class Huffman {

private class Node implements Comparable {

char ch;

int freq;

boolean isLeaf;

Node left, right;

public Node(char ch, int freq) {

this.ch = ch;

this.freq = freq;

isLeaf = true;

}

public Node(Node left, Node right, int freq) {

this.left = left;

this.right = right;

this.freq = freq;

isLeaf = false;

}

@Override

public int compareTo(Node o) {

return this.freq - o.freq;

}

}

public Map encode(Map frequencyForChar) {

PriorityQueue priorityQueue = new PriorityQueue<>();

for (Character c : frequencyForChar.keySet()) {

priorityQueue.add(new Node(c, frequencyForChar.get(c)));

}

while (priorityQueue.size() != 1) {

Node node1 = priorityQueue.poll();

Node node2 = priorityQueue.poll();

priorityQueue.add(new Node(node1, node2, node1.freq + node2.freq));

}

return encode(priorityQueue.poll());

}

private Map encode(Node root) {

Map encodingForChar = new HashMap<>();

encode(root, "", encodingForChar);

return encodingForChar;

}

private void encode(Node node, String encoding, Map encodingForChar) {

if (node.isLeaf) {

encodingForChar.put(node.ch, encoding);

return;

}

encode(node.left, encoding + '0', encodingForChar);

encode(node.right, encoding + '1', encodingForChar);

}

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/556491.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python模块导入_python模块导入

不同的执行方式&#xff1a; 从IDE中执行&#xff0c;python程序由IDE设置环境决定。 从系统中执行&#xff0c;python程序由环境变量中的系统变量path决定&#xff0c;从上往下选择。 模块导入顺序&#xff1a; 系统包优先级最高 > 同目录 > sys.path&#xff0c;之所以…

再也不怕SVN冲突:轻松解决SVN冲突

什么时候容易出现冲突&#xff1f; 多个人同时修改了同个文件中的同一行代码 无法进行对比的二进制文件&#xff0c;比如图片等 如何解决冲突&#xff1f; 如上图&#xff0c;test_conflict.py文件发生了冲突&#xff0c;并且多出了几个文件&#xff0c;其中.mine是我本地修…

手机型号大全_2020值得入手的三款手机。每个优秀,选择哪一个?励志故事名言视频...

如今&#xff0c;手机等数码产品更新很快。各种新的技能&#xff0c;让用户真正体验到科技的力量&#xff0c;它可以被描述为“具有多种功能的一个装置。”然而&#xff0c;这么多车型&#xff0c;难免有些人不知道如何选择。当4G和5G手机**的对峙&#xff0c;很多朋友也问小中…

AspectJ

Aspectj与Spring AOP比较 XML配置方式 <aop:aspect>&#xff1a; 定义切面, 包括通知和切点. 是一般的bean//定义切面 public class SleepHelperAspect{public void beforeSleep(){System.out.println("睡觉前要脱衣服&#xff01;");}public void afterSleep…

aixs1 生成java代码_通过axis1.4 来生成java客户端代码

1.首先下载axis-1.4所有的jar包&#xff0c;2.我是直接打开cmd&#xff0c;进入到该jar包的目录下&#xff0c;3.直接运行命令(运行这个命令之前要确定java的环境变量都已配置好)&#xff1a;java -Djava.ext.dirs${lib的目录} org.apache.axis.wsdl.WSDL2Java -o${代码输出路径…

windows分屏_windows内到底藏了多少好东西?

恭喜!点开这篇文章&#xff0c;你将解锁 WIN10 系统内那些不为人知的高效的冷知识&#xff01;相信所有的职场人都会搜索过这样的问题&#xff1a;有哪些高效的办公神器&#xff1f;在之前的文章中&#xff0c;我分享过很多高效神器&#xff0c;如果你感兴趣的话&#xff0c;点…

@Aspect中@Pointcut 12种用法

本文主要内容&#xff1a;掌握Pointcut的12种用法。 Aop相关阅读 阅读本文之前&#xff0c;需要先掌握下面3篇文章内容&#xff0c;不然会比较吃力。 Spring系列第15篇&#xff1a;代理详解&#xff08;java动态代理&CGLIB代理)Spring系列第30篇&#xff1a;jdk动态代理…

asp.net接受表单验证格式后再提交数据_看滴普科技大前端如何玩转el-form-renderer 表单渲染器1.14.0

DEEPEXI 大前端常人道&#xff0c;一入开发深似海&#xff0c;技术学习无止境。在新技术层出不穷的前端开发领域&#xff0c;有一群身怀绝技的开发&#xff0c;他们在钻研前沿技术的同时&#xff0c;也不忘分享他们的成果&#xff0c;回馈社区。下面&#xff0c;就由小水滴带大…

测试用例设计方法_黑盒测试——测试用例设计方法

黑盒测试也称为功能测试或数据驱动测试。通过软件的外部表现来发现其缺陷和错误。在测试时&#xff0c;把被测程序视为一个不能打开的盒子&#xff0c;在完全不考虑程序内部逻辑结构和内部特性的情况下进行。它是在已知产品所应具有的功能前提下&#xff0c;通过测试来检测每个…

SpringAop @Pointcut(“@annotation“)\@Aspect练习

切面记录日志 切面类 Slf4j Aspect Component public class AspectForFeign {Pointcut("execution(public * com.keke.remote..*Feign.*(..))")public void pointcut() {}Around("pointcut()")public Object around(ProceedingJoinPoint joinPoint) thro…

Mybatis缓存机制详解与实例分析

前言&#xff1a; 本篇文章主要讲解Mybatis缓存机制的知识。该专栏比较适合刚入坑Java的小白以及准备秋招的大佬阅读。 如果文章有什么需要改进的地方欢迎大佬提出&#xff0c;对大佬有帮助希望可以支持下哦~ 小威在此先感谢各位小伙伴儿了&#x1f601; 以下正文开始 Mybat…

delphi语言转为汇编语言_每天5分钟,轻松建立技术图谱 编程语言黑历史

阿T课堂开播啦&#xff01;这里只有干货干锅&#xff0c;没有水坑没有套路&#xff01;计算机编程语言的发展&#xff0c;也是随着计算机本身发展而发展。人类不断的提高科技的同时&#xff0c;也必须使工具的使用越来越简化&#xff0c;从而提高整个社会效率&#xff0c;这其中…

水系图一般在哪里找得到_进展 | 水系钠离子电池研究取得重要进展

水系钠离子电池兼具钠资源储量丰富和水系电解液本质安全的双重优势被视为一种理想的大规模静态储能技术。此前&#xff0c;我们针对这水系钠离子电池体系做了一些探索(Nature Communications 2015, 6, 6401&#xff1b;Advanced Energy Materials 2015, 5, 1501005&#xff1b;…

@Around简单使用示例——SpringAOP增强处理

Around的作用 既可以在目标方法之前织入增强动作&#xff0c;也可以在执行目标方法之后织入增强动作&#xff1b;可以决定目标方法在什么时候执行&#xff0c;如何执行&#xff0c;甚至可以完全阻止目标目标方法的执行&#xff1b;可以改变执行目标方法的参数值&#xff0c;也…

python numpy逆_Python使用numpy计算矩阵特征值、特征向量与逆矩阵

原标题&#xff1a;Python使用numpy计算矩阵特征值、特征向量与逆矩阵 Python扩展库numpy.linalg的eig()函数可以用来计算矩阵的特征值与特征向量&#xff0c;而numpy.linalg.inv()函数用来计算可逆矩阵的逆矩阵。 >>> importnumpy as np >>> x np.matrix([…

Mysql索引数据结构有多个选择,为什么一定要是B+树呢?_面试 (MySQL 索引为啥要选择 B+ 树)

Mysql索引数据结构 下面列举了常见的数据结构 二叉树红黑树Hash表B-Tree&#xff08;B树&#xff09; Select * from t where t.col5我们在执行一条查询的Sql语句时候&#xff0c;在数据量比较大又不加索引的情况下&#xff0c;逐行查询并进行比对&#xff0c;每次需要从磁盘…

一篇搞懂mysql中的索引(大白话版)

容易来说&#xff0c;索引的出现其实就是为了提升数据查询的效率&#xff0c;就像书的目录一样。一本 500 页的书&#xff0c;如果你想快速找到其中的某一个知识点&#xff0c;在不借助目录的情况下&#xff0c;那我估计你可得找一会儿。同样&#xff0c;对于数据库的表而言&am…

sqlite插入时间字段_sqlite 获取最后插入id

(点击上方公众号&#xff0c;可快速关注)SQLite数据库中的表均有一个特殊的rowid字段&#xff0c;它是一个不重复的64位有符号整数&#xff0c;默认起始值为1。rowid别名为oid或_rowid_&#xff0c;但在创建表的SQL声明语句中只能使用rowid作为关键字。如果在创建表的时候设置了…

Dubbo与SpringCloud的架构与区别

Dubbo与SpringCloud的架构与区别 Dubbo架构图 SpringCloud 架构图 总结 框架DubboSpringCloud服务注册中心ZookeeperSpring Cloud Netfix Eureka(nacos)服务调用方式RPCREST API服务监控Dubbo-monitorSpring Boot Admin熔断器不完善Spring Cloud Netflix Hystrix服务网关无Sp…

matlab求微分数值,用MATLAB语言求微积分方程的数值解.(xd^2y)/dx^2-5dy/dx+y=0y(0)=0y'(0)=0...

function dymyfun03(x,y)dyzeros(3,1) %初始化变量dydy(1)y(2); %dy(1)表示y的一阶导数,其等于y的第二列值dy(2)5/x*y(3)-y(1); %dy(2)表示y的二阶导数%ex0808 用ode23 ode45 ode113解多阶微分方程clear,clc[x23,y23]ode23(myfun03,[1,10],[1 10 30]);[x45,y45]ode45(myfun03,[…