[数据结构]P2.1 二叉搜索树
二叉树就是每个节点最多有两个分叉的树。这里我们写一写一个典型的例子二叉搜索树,它存在的实际意义是什么呢?
在P1.1链表中,我们清楚了链表的优势是善于删除添加节点,但是其取值很慢;数组的优势是善于取值,但是不利于删除添加节点。
而二叉搜索树,正是两者的折中方案。首先,它是树状结构,因此它便于插入和删除节点,需要花费LogN级别的时间,其次,它
在每一个节点上都满足`左子树 <= 当前节点 <= 右子树`,因此便于使用二叉搜索,所以查找数据和取数据也是LogN级别的。
时间比较 | 链表 | 二叉搜索树 | 数组 |
取值 | N | LogN | C |
查找 | N | LogN | 有序数组:LogN 无序数组:N |
插入和删除 | C | LogN | N |
常见的二叉搜索树操作有:
1.插入新节点
2.按值查找节点
3.删除节点
4.三种遍历方式
5.二叉搜索树的宽度
6.二叉搜索树最大距离
TODO : 待解析
Java代码:
package ds4.binaryTree; import java.util.ArrayDeque;
import java.util.Queue; /**
* 二叉搜索树
*/
public class BinarySearchTree { static class Node {
long data;
Node left;
Node right; public Node(long data) {
this.data = data;
} @Override
public String toString() {
return "N["+data+"]";
}
} static class Tree{
Node root;
Tree(){} /**
* 添加节点: 时间消耗LogN
* @param node
*/
public void addNode(Node node){
if(node == null){
return;
} if(this.root == null){
this.root = node;
return;
}
Node current = root;
Node parent = current;
while(current != null){
parent = current;
if(current.data >= node.data){
current = current.left;
}else{
current = current.right;
}
} if(parent.data >= node.data){
parent.left = node;
}else{
parent.right = node;
}
} /**
* 查找节点
* 时间消耗LogN
* @param data
* @return
*/
public Node fineNodeByVal(long data){
Node result = null;
if(this.root == null){
return result;
} Node current = this.root;
while(current != null){
if(current.data > data){
current = current.left;
}else if(current.data < data){
current = current.right;
}else{
result = current;
break;
}
}
return result;
} /**
* 删除某个节点
* @param node
*/
public void removeNode(Node node){
if(this.root == null){
return;
} if(node == null){
return;
} if(this.root == node){
this.root = null;
node.left = node.right = null;
return;
} // 寻找其父节点
Node current = root;
Node parent = current;
while(current != null){
if(current == node){
break;
}
parent = current;
if(current.data < node.data){
current = current.right;
}else{
current = current.left;
}
}
if(current == null){
System.out.println("没有找到这个节点!");
return;
} if(node.left == null && node.right == null){ //左右都为空
// 找到其父节点直接删除即可
if(parent.left == null){
parent.left = null;
}else{
parent.right = null;
}
return;
}else if(node.left != null && node.right == null){ // 左子树不为空,而右子树为空
// 找到其父节点直接嫁接
if(parent.left == null){
parent.left = node.left;
}else{
parent.right = node.left;
} }else if(node.right != null && node.left == null){ // 右子树不为空,左子树为空
//找到父节点直接嫁接
if(parent.right == node){
parent.right = node.right;
}else{
parent.left = node.right;
}
}else{ // 左右子树都不为空
//找到左枝最靠右节点,删除;并找到带删除父节点,将待删除节点用左枝最右节点替代
Node current1 = node;
Node parent1 = node;
while (current1.right != null){
parent1 = current1;
current1 = current1.right;
}
if(parent1.right == current1){
parent1.right = null;
}else{
parent1.left = null;
}
// 到这里current1为最右左节点,parent1为current1的父节点,已经解除了父子关系
current1.left = node.left;
current1.right = node.right; if(parent.right == current){
parent.right = current1;
}else{
parent.left = current1;
} node.left = null;
node.right = null;
}
} /**
* 先根遍历
*/
public void firstRootIndex(Node root){
if(root != null){
System.out.println(root.data);
if(root.left != null){
firstRootIndex(root.left);
} if(root.right != null){
firstRootIndex(root.right);
}
}
} /**
* 后根遍历
* @param root
*/
public void lastRootIndex(Node root){
if(root != null){
if(root.left != null){
lastRootIndex(root.left);
}
if(root.right != null){
lastRootIndex(root.right);
}
System.out.println(root.data);
}
} /**
* 中序遍历
* @param root
*/
public void midRootIndex(Node root){
if(root != null){
if(root.left != null){
lastRootIndex(root.left);
}
System.out.println(root.data);
if(root.right != null){
lastRootIndex(root.right);
}
}
} /**
* 层序遍历
* @param root
*/
public void layerIndex(Node root){
// 1.当前层
Queue<Node> currentLayer = new ArrayDeque<Node>();
// 2.临时存储下一层节点
Queue<Node> sonLayer = new ArrayDeque<Node>(); if(this.root == null){
return;
}else{
currentLayer.add(this.root);
} while(currentLayer.size() != 0){
// 1.当前层节点出队列下一层入队列
sonLayer.clear();
while(currentLayer.size() != 0){
Node node = currentLayer.poll();
// 2.遍历本层节点
System.out.print(node); if(node.left != null){
sonLayer.add(node.left);
}
if(node.right != null){
sonLayer.add(node.right);
}
}
System.out.println();
// 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
currentLayer = sonLayer;
sonLayer = new ArrayDeque<Node>();
} } /**
* 计算树的宽度
* 也就是树中节点数最多的层次对应的节点数
* 思路1: 进行一次树的遍历,也就是过一遍树的所有节点,将每一层的数据存储在Map数据结构中
* 思路2: 设置队列进行层序遍历,将这层的节点数量计算一下,然后出队列,下一层的节点入队列
*
* 思路2提供了除去以上三种遍历方式以外的第三种遍历方式
*/
public int computeWidth(){
int maxSize = 0;
// 1.当前层
Queue<Node> currentLayer = new ArrayDeque<Node>();
// 2.临时存储下一层节点
Queue<Node> sonLayer = new ArrayDeque<Node>(); if(this.root == null){
return maxSize;
}else{
currentLayer.add(this.root);
} while(currentLayer.size() != 0){
// 1. 计算当前层size
int currentSize = currentLayer.size();
maxSize = maxSize>currentSize?maxSize:currentSize;
// 2.当前层节点出队列下一层入队列
sonLayer.clear();
while(currentLayer.size() != 0){
Node node = currentLayer.poll();
if(node.left != null){
sonLayer.add(node.left);
}
if(node.right != null){
sonLayer.add(node.right);
}
}
// 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
currentLayer = sonLayer;
sonLayer = new ArrayDeque<Node>();
} return maxSize;
} /**
* 计算二叉树中两点的最大距离
* 思路1: 左枝树长 + 右枝树长 算法:节省时间,占空间
* 思路2: 迭代 算法:节省空间,占用时间
*/
public int computeMaxDistance(){
if(this.root == null){
return 0;
} return computeDepth(this.root.left) + computeDepth(this.root.right);
} /**
* 计算二叉树的深度
* 和刚才计算宽度的思路一样,只是用深度来计算
* @return
*/
public int computeDepth(Node root){
int depth = 0;
// 1.当前层
Queue<Node> currentLayer = new ArrayDeque<Node>();
// 2.临时存储下一层节点
Queue<Node> sonLayer = new ArrayDeque<Node>(); if(root == null){
return depth;
}else{
currentLayer.add(root);
} while(currentLayer.size() != 0){
// 1. 计算当前层depth
depth ++;
// 2.当前层节点出队列下一层入队列
sonLayer.clear();
while(currentLayer.size() != 0){
Node node = currentLayer.poll();
if(node.left != null){
sonLayer.add(node.left);
}
if(node.right != null){
sonLayer.add(node.right);
}
}
// 3.将sonLayer赋给currentLayer,sonLayer重新创建对象
currentLayer = sonLayer;
sonLayer = new ArrayDeque<Node>();
} return depth;
} } public static void main(String[] args) {
Tree tree = new Tree();
tree.addNode(new Node(100));
tree.addNode(new Node(200));
tree.addNode(new Node(20));
tree.addNode(new Node(177));
Node nodeToRemove = new Node(88);
tree.addNode(nodeToRemove);
tree.addNode(new Node(90));
tree.addNode(new Node(60));
// 层序遍历
System.out.println("层次遍历");
tree.layerIndex(tree.root);
// 先根遍历
System.out.println("先根遍历");
tree.firstRootIndex(tree.root);
// 计算宽度与深度
tree.addNode(new Node(188));
System.out.println("width:"+tree.computeWidth());
System.out.println("depth:"+tree.computeDepth(tree.root));
// 删除节点
tree.removeNode(nodeToRemove);
// 层序遍历
System.out.println("层序遍历");
tree.layerIndex(tree.root);
// 后根遍历
System.out.println("后根遍历");
tree.lastRootIndex(tree.root);
System.out.println("最大距离:"+tree.computeMaxDistance()); }
}
result:
层次遍历
N[100]
N[20]N[200]
N[88]N[177]
N[60]N[90]
先根遍历
100
20
88
60
90
200
177
width:3
depth:4
层序遍历
N[100]
N[20]N[200]
N[90]N[177]
N[60]N[188]
后根遍历
60
90
20
188
177
200
100
最大距离:6
[数据结构]P2.1 二叉搜索树的更多相关文章
- javascript数据结构——写一个二叉搜索树
二叉搜索树就是左侧子节点值比根节点值小,右侧子节点值比根节点值大的二叉树. 照着书敲了一遍. function BinarySearchTree(){ var Node = function(key) ...
- PTA 数据结构——是否完全二叉搜索树
7-2 是否完全二叉搜索树 (30 分) 将一系列给定数字顺序插入一个初始为空的二叉搜索树(定义为左子树键值大,右子树键值小),你需要判断最后的树是否一棵完全二叉树,并且给出其层序遍历的结果. 输入格 ...
- [数据结构]——二叉树(Binary Tree)、二叉搜索树(Binary Search Tree)及其衍生算法
二叉树(Binary Tree)是最简单的树形数据结构,然而却十分精妙.其衍生出各种算法,以致于占据了数据结构的半壁江山.STL中大名顶顶的关联容器--集合(set).映射(map)便是使用二叉树实现 ...
- 【算法与数据结构】二叉搜索树的Java实现
为了更加深入了解二叉搜索树,博主自己用Java写了个二叉搜索树,有兴趣的同学可以一起探讨探讨. 首先,二叉搜索树是啥?它有什么用呢? 二叉搜索树, 也称二叉排序树,它的每个节点的数据结构为1个父节点指 ...
- 数据结构之二叉搜索树、AVL自平衡树
前言 最近在帮公司校招~~ 所以来整理一些数据结构方面的知识,这些知识呢,光看一遍理解还是很浅的,看过跟动手做过一遍的同学还是很容易分辨的哟~ 一直觉得数据结构跟算法,就好比金庸小说里的<九阳神 ...
- 自己动手实现java数据结构(六)二叉搜索树
1.二叉搜索树介绍 前面我们已经介绍过了向量和链表.有序向量可以以二分查找的方式高效的查找特定元素,而缺点是插入删除的效率较低(需要整体移动内部元素):链表的优点在于插入,删除元素时效率较高,但由于不 ...
- 用Python实现数据结构之二叉搜索树
二叉搜索树 二叉搜索树是一种特殊的二叉树,它的特点是: 对于任意一个节点p,存储在p的左子树的中的所有节点中的值都小于p中的值 对于任意一个节点p,存储在p的右子树的中的所有节点中的值都大于p中的值 ...
- 【Java】 大话数据结构(11) 查找算法(2)(二叉排序树/二叉搜索树)
本文根据<大话数据结构>一书,实现了Java版的二叉排序树/二叉搜索树. 二叉排序树介绍 在上篇博客中,顺序表的插入和删除效率还可以,但查找效率很低:而有序线性表中,可以使用折半.插值.斐 ...
- hdu 3791:二叉搜索树(数据结构,二叉搜索树 BST)
二叉搜索树 Time Limit : 2000/1000ms (Java/Other) Memory Limit : 32768/32768K (Java/Other) Total Submiss ...
随机推荐
- Linux 从源码编译安装 OpenSSH
https://blog.csdn.net/bytxl/article/details/46639073 Linux 从源码编译安装 OpenSSH以及各问题解决 2015年06月25日 17:37: ...
- SQL[Err]ORA-00932: inconsistent datatypes: expected NUMBER got CHAR:
ORA-00932: inconsistent datatypes: expected NUMBER got CHAR: 获取的目标类型与源类型不一致,多出现在case when 语句中,when的结 ...
- μCOS-II移植 - 基于CortexM3
μCOS-II是一个经典的RTOS. 任务切换对于RTOS来说是最基本也是最核心的部分,除此之外还有任务调度算法. 先来看看基于stm32f107的任务切换代码: ;***************** ...
- 2019.04.13 python基础
第一节 主要讲python背景 没什么要注意的 了解记住概念就好 python官网 python.org 自带shell 可以运行python代码 在IDLE中怎么运行代码 新建文本 ...
- Ch02 控制结构和函数 - 练习
1. 一个数字如果为正数,则它的signum为1:如果是负数,则signum为-1:如果是0,则signum为0.编写一个函数来计算这个值. scala> def signum(x:Int):I ...
- gitlab重置root的密码
环境:gitlab 忘记了root密码,无法登陆gitlab 解决: gitlab-ctl start 保证gitlab处于启动状态,&保证redis处于启动状态 gitlab-rails c ...
- SpringBoot项目启动时链接数据库很慢
SpringBoot项目启动时链接数据库很慢 springboot项目在启动时候,如下图所示,链接数据库很慢 解决方法:在mysql 的配置文件中 配置 skip-name-resolve
- 原生侧边栏sidebar
创建侧栏导航 html: <a href="#" class="btn">点我啊</a> <div class="sid ...
- HTML 鼠标坐标和元素坐标
在这一篇文章中,将会介绍鼠标坐标.元素坐标以及鼠标在指定元素内的坐标. 1. 鼠标坐标 在触发鼠标相关事件时(如:click.mousemove),可以通过事件对象获取当前鼠标的坐标. 获取的坐标可分 ...
- TLS握手、中断恢复与证书中心的原因
在双方都拿到随机数A.B.C后,将会使用这三个随机数生成一个对话密钥,然后使用该对话密钥进行对称加密通信,这种方式我们可以看到,安全性取决于随机数C的加密,前面的几个都是明文传的,这里就取决于服务器的 ...