[数据结构]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 ...
随机推荐
- GAN (Generative Adversarial Network)
https://www.bilibili.com/video/av9770302/?p=15 前面说了auto-encoder,VAE可以用于生成 VAE的问题, AE的训练是让输入输出尽可能的接近, ...
- Ubuntu 使用命令行连接无线网
一.查看可以使用的无线网: nmcli dev wifi 二.连接无线网: nmcli dev wifi connect ‘essid’(网络名称) password ‘password’(密码) 可 ...
- Docker入门6------生产力工具rancher
rancher的安装: https://blog.csdn.net/wh211212/article/details/80932264 安装 一行命令就可以安装 sudo docker run -d ...
- docker基本知识
- Vuex之理解Getters的用法
一.什么是getters在介绍state中我们了解到,在Store仓库里,state就是用来存放数据,若是对数据进行处理输出,比如数据要过滤,一般我们可以写到computed中.但是如果很多组件都使用 ...
- 一、iOS开发环境搭建
前置条件 1. 必要:一台装有Mac OS X操作系统的电脑:经济允许的话可以买一部Mac book:否则的话,可以试试黑苹果或虚拟机. 2.必要:一个有可用的Apple ID:免费,在Apple的官 ...
- 运行pytorch代码遇到的error解决办法
1.no CUDA-capable device is detected 首先考虑的是cuda的驱动问题,查看gpu显示是否正常,然后更新最新的cuda驱动: 第二个考虑的是cuda设备的默认参数是否 ...
- Android -- 从源码带你从EventBus2.0飚到EventBus3.0
1,最近看了不少的面试题,不管是百度.网易.阿里的面试题,都会问到EventBus源码和RxJava源码,而自己只是在项目中使用过,却没有去用心的了解它底层是怎么实现的,所以今天就和大家一起来学习学习 ...
- 分治法——归并排序(mergesort)
首先上代码. #include <iostream> using namespace std; int arr[11]; /*两个序列合并成一个序列.一共三个序列,所以用 3 根指针来处理 ...
- Dom4j基础
dom4j是一个非常非常优秀的Java XML API,用来读写XML文件,具有性能优异.功能强大和易于使用的特点,同时它也是一个开放源代码的软件,可以在SourceForge上找到它.对主流的Jav ...