[数据结构]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 ...
随机推荐
- c++stack容器介绍
c++stack(堆栈)是一个容器的改编,它实现了一个先进后出的数据结构(FILO) 使用该容器时需要包含#include<stack>头文件: 定义stack对象的示例代码如下: sta ...
- [js]js设计模式-工厂模式
// 定义一个人 var p1 = { name: 'wxb', age: 22, writejs: function () { console.log(this.name + ' can sing. ...
- Python3.6安装使用tesserocr文件时遇到问题
本机运行环境: Win 10 version 1709; Python 3.6.3 (v3.6.3:2c5fed8, Oct 3 2017, 18:11:49) [MSC v.1900 64 bit ...
- 如何消除img默认的间距
方案一:div{font-size:0};方案二:img{ display:block};方案三:img{vertical-align:top;}方案四:div{ margin-bottom:-3px ...
- CSS实现经典的三栏布局
实现效果: 左右栏定宽,中间栏自适应 (有时候是固定高度) 1 . 绝对定位布局:position + margin <div class="container"> & ...
- Vue系列之 => Watch监视路由地址改变
第一种方式实现监听 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...
- http返回状态码含义
http返回状态码 http状态码 200 2开头的都表示这个请求发送成功,最常见的就是200,就代表这个请求是ok的,服务器也返回了. 300 3开头的代表重定向,最常见的是302,把这个请求重定向 ...
- 在java中,将String类型字符串s赋值为null后,将字符串与其他字符串拼接后得到结果出现了null字符串与其他字符连接的样式
String s = null; s += "hello"; System.out.println(s); 结果为:nullhello 原因: 先应用String.valueOf ...
- C++反射实现(转)
参考了wxWidgets的相关代码,并进行简化,同时借鉴了网上的相关内容,改进之处在于,只需要继承基本类Object,在类的头文件中定义宏,DECLARE_CLASS(类名),在类的实现文件中定义宏, ...
- Java笔记 #05# Java Native Interface
参考资料:http://www.ntu.edu.sg/home/ehchua/programming/java/javanativeinterface.html(很详细) 看jdk源代码时,经常会看到 ...