3.2 符号表之二叉查找树BST
一.插入和查找
1.二叉查找树(Binary Search Tree)是一棵二叉树,并且每个结点都含有一个Comparable的键,保证每个结点的键都大于其左子树中任意结点的键而小于其右子树的任意结点的键。
2.一个结点需要维持几个实数域,即键,值,左、右结点,还需要维持一个count值,用来表示该结点含有的子树的结点数(包括自己)
3.查找的实现:如果小于该节点,去该节点的左边找;如果大于该节点,去该节点的右边找,如果相等,则找到了该值。
插入的实现:key在BST中,重置value;key不在BST中,添加新的结点。
package com.cx.serch; import java.util.LinkedList;
import java.util.Queue; public class BST<Key extends Comparable<Key>,Value> {
private Node root;
/**
* 定义Node节点,用来存储键值对,以及左右的节点
*/
private class Node{
private Key key;
private Value value;
private Node left,right;
//添加一个count属性,这个属性用来记录以该节点为根的节点数(包括自己)
private int count; public Node(Key key,Value value,int count) {
this.key=key;
this.value=value;
this.count=count;
}
} /**
* 实现size()方法
*/
public int size() {
return size(root);
}
private int size(Node x) {
if(x==null) return 0;
return x.count;
} /**
* 实现插入键值对的方法
*/
public void put(Key key,Value value) {
//如果根节点是null,给它赋上key,value
//如果有根节点,根的合适位置添上该节点
root=put(root, key, value);
} public Node put(Node x,Key key,Value value) {
if(x==null) return new Node(key, value , 1);
int cmp=key.compareTo(x.key);
if(cmp<0)
x.left=put(x.left, key, value);
else if(cmp>0)
x.right=put(x.right, key, value);
else
x.value=value;
x.count=size(x.left)+size(x.right)+1;
return x;
}
/**
* 实现查找某个key的方法
*/
//比Node大,去右边找,比Node小去左边找
public Value get(Key key) {
Node x=root;
while(x!=null) {
int cmp = key.compareTo(x.key);
if (cmp<0) x=x.left;
else if(cmp>0) x=x.right;
else return x.value;
}
return null;
}
}
4.说明:
(1)可以看出插入和查找难度一样,均为lgN,即和树的高度有关。
(2)但是二叉查找树的算法的运行时间取决于树的形状,即键被插入的先后顺序。在最坏的情况下,性能可能非常糟糕,也就是当输入的key,已经被排好序的时候,树的高度为N,插入和查找的运行时间也需要N
(3)一般来说,如果N个随机键以随机的顺序插入到BST中,那么插入和查找的时间为~2lnN.
(4)我们无法保证输入是随机的,也就是说BST无法避免最坏的情况发生
二.找floor/ceiling值,max/min(对于键来说)
1.floor指的是小于给定k的最大的值,ceiling指大于给定k的最小值。floor和ceiling思路类似,仅举例floor
2.floor的思路:
(1)k等于根root的值,floor(k)为k
(2)k小于根root的值,floor(k)在根的左子树中
(3)k大于根root的值,如果根的右子树全都大于k,则floor(k)为根的值。只要根的右子树有一个小于k,则floor(k)在根的右子树中
这一步是有技巧的,可以直接找去根的右子树中找floor(k),如果找到了,则取该值,否则,取根。
3.min的思路:找以x为根的左子树,直到左边为null为止。
4.代码实现:
/**
* 实现查找key的floor的方法,即小于key的最大值
*/
public Key floor(Key key) {
Node x=floor(root,key);
//能找到,返回x,不能找到返回null
if (x==null) return null;
else return x.key;
}
//与x.key比较,求key的floor
public Node floor(Node x,Key key) {
if(x == null) return null;
//比较x.key和key
int cmp=key.compareTo(x.key);
//如果等,floor即为x
if(cmp==0) return x;
//如果k小于节点,去左边找
else if(cmp<0) return floor(x.left,key);
//如果k大于节点,去右边找floor
//如果右边全大于k,则为该节点
//换句话说,可以转换为在x的右边子树找floor,存在则为该值,否则为x
Node t=floor(x.right, key);
if(t == null) return x;
else return t;
}
/**
* 实现查找最小key的方法
*/
public Key min() {
return min(root).key;
}
//返回以x为根的子树的min
public Node min(Node x) {
if (x.left==null) return x;
//在左边的子树中寻找
return min(x.left);
}
三.找rank,即小于k的键的个数。k可以在二叉树里,也可以不在。
1.思路:分三种情况考虑
2.代码实现:
public int rank(Key key) {
return rank(key,root);
}
//一个错误的实现是size(x),但是以x为根的子树有大于它的部分
public int rank(Key key,Node x) {
if(x==null) return 0;
//key与x.key比较
int cmp=key.compareTo(x.key);
//相等
if(cmp==0) return size(x.left);
//key小于x.key,去左边找
else if(cmp<0) return rank(key, x.left);
//大于,去右边找,且要加上1和左边的size
else return 1+size(x.left)+rank(key, x.right);
}
四.将键按照中序遍历(inorder traversal)
1.中序遍历:指的是先遍历左子树,再遍历根,最后遍历右子树。
2.这里使用队列存储key
3.代码实现:
public Iterable<Key> keys(){
Queue<Key> queue=new LinkedList<Key>();
inorder(root,queue);
return queue;
}
public void inorder(Node x,Queue<Key> q) {
if(x==null) return;
inorder(x.left, q);
q.add(x.key);
inorder(x.right, q);
}
五.删除操作
1.删除最小值的思路:
(1)一直找左子树,直到一个结点不存在左子树为止
(2)使用右侧的子树替代它
(3)更新子树的count
2.删除指定node with key k的思路
(1)如果没有子结点,直接删除它,并将父结点指向空
(2)如果只有一个子结点,将父结点指向它的子结点
(3)如果有两个子结点:
i.找到t的右子树中最小的节点 min(t.right)
ii.删除该节点 deleteMin(t.right)
iii.用x替换t
3.代码实现:
/**
* 删除最小值
*/
public void deleteMin() {
root=deleteMin(root);
}
//去左边找,直到找到一个有着左侧为空的节点
//用该节点的右侧替换该节点
//更新子树的数目
public Node deleteMin(Node x) {
//如果左侧为空,返回该节点
if(x.left==null) return x.right;
x.left=deleteMin(x.left);
x.count=1+size(x.left)+size(x.right);
return x;
} /**
* 删除指定值
*/
public void delete(Key key) {
root=delete(root,key);
}
public Node delete(Node x,Key key) {
if(x == null) return null;
//寻找key对应的节点
int cmp=key.compareTo(x.key);
if(cmp<0) return delete(x.left, key);
else if(cmp>0) return delete(x.right, key);
//找到了该节点
else {
//只有一个child或没有child,用另一部分替代该节点
if(x.right==null) return x.left;
if(x.left==null) return x.right;
//有两个节点
Node t=x;
//1.找到t右子树的最小节点x
x=min(t.right);
//2.删除最小节点
//3.用x替代t
x.right=deleteMin(t.right);
x.left=t.left;
}
//更新count
x.count=1+size(x.left)+size(x.right);
return x;
}
六.总结
1.整个BST的实现代码如下:
package com.cx.serch; import java.util.LinkedList;
import java.util.Queue; public class BST<Key extends Comparable<Key>,Value> {
private Node root;
/**
* 定义Node节点,用来存储键值对,以及左右的节点
*/
private class Node{
private Key key;
private Value value;
private Node left,right;
//添加一个count属性,这个属性用来记录以该节点为根的节点数(包括自己)
private int count; public Node(Key key,Value value,int count) {
this.key=key;
this.value=value;
this.count=count;
}
} /**
* 实现size()方法
*/
public int size() {
return size(root);
}
private int size(Node x) {
if(x==null) return 0;
return x.count;
} /**
* 实现插入键值对的方法
*/
public void put(Key key,Value value) {
//如果根节点是null,给它赋上key,value
//如果有根节点,根的合适位置添上该节点
root=put(root, key, value);
} public Node put(Node x,Key key,Value value) {
if(x==null) return new Node(key, value , 1);
int cmp=key.compareTo(x.key);
if(cmp<0)
x.left=put(x.left, key, value);
else if(cmp>0)
x.right=put(x.right, key, value);
else
x.value=value;
x.count=size(x.left)+size(x.right)+1;
return x;
}
/**
* 实现查找某个key的方法
*/
//比Node大,去右边找,比Node小去左边找
public Value get(Key key) {
Node x=root;
while(x!=null) {
int cmp = key.compareTo(x.key);
if (cmp<0) x=x.left;
else if(cmp>0) x=x.right;
else return x.value;
}
return null;
} /**
* 实现查找key的floor的方法,即小于key的最大值
*/
public Key floor(Key key) {
Node x=floor(root,key);
//能找到,返回x,不能找到返回null
if (x==null) return null;
else return x.key;
}
//与x.key比较,求key的floor
public Node floor(Node x,Key key) {
if(x == null) return null;
//比较x.key和key
int cmp=key.compareTo(x.key);
//如果等,floor即为x
if(cmp==0) return x;
//如果k小于节点,去左边找
else if(cmp<0) return floor(x.left,key);
//如果k大于节点,去右边找floor
//如果右边全大于k,则为该节点
//换句话说,可以转换为在x的右边子树找floor,存在则为该值,否则为x
Node t=floor(x.right, key);
if(t == null) return x;
else return t;
}
/**
* 实现查找最小key的方法
*/
public Key min() {
return min(root).key;
}
//返回以x为根的子树的min
public Node min(Node x) {
if (x.left==null) return x;
//在左边的子树中寻找
return min(x.left);
} /**
* 实现rank()方法,也就是<k的数量
* 默认k在该BST中存在
*/
public int rank(Key key) {
return rank(key,root);
}
//一个错误的实现是size(x),但是以x为根的子树有大于它的部分
public int rank(Key key,Node x) {
if(x==null) return 0;
//key与x.key比较
int cmp=key.compareTo(x.key);
//相等
if(cmp==0) return size(x.left);
//key小于x.key,去左边找
else if(cmp<0) return rank(key, x.left);
//大于,去右边找,且要加上1和左边的size
else return 1+size(x.left)+rank(key, x.right);
} /**
* 中序遍历(inorder traversal),会按照自然顺序排序
* traverse left subtree
* enqueue key
* traverse right subtree
*/
public Iterable<Key> keys(){
Queue<Key> queue=new LinkedList<Key>();
inorder(root,queue);
return queue;
}
public void inorder(Node x,Queue<Key> q) {
if(x==null) return;
inorder(x.left, q);
q.add(x.key);
inorder(x.right, q);
} /**
* 删除最小值
*/
public void deleteMin() {
root=deleteMin(root);
}
//去左边找,直到找到一个有着左侧为空的节点
//用该节点的右侧替换该节点
//更新子树的数目
public Node deleteMin(Node x) {
//如果左侧为空,返回该节点
if(x.left==null) return x.right;
x.left=deleteMin(x.left);
x.count=1+size(x.left)+size(x.right);
return x;
} /**
* 删除指定值
*/
public void delete(Key key) {
root=delete(root,key);
}
public Node delete(Node x,Key key) {
if(x == null) return null;
//寻找key对应的节点
int cmp=key.compareTo(x.key);
if(cmp<0) return delete(x.left, key);
else if(cmp>0) return delete(x.right, key);
//找到了该节点
else {
//只有一个child或没有child,用另一部分替代该节点
if(x.right==null) return x.left;
if(x.left==null) return x.right;
//有两个节点
Node t=x;
//1.找到t右子树的最小节点x
x=min(t.right);
//2.删除最小节点
//3.用x替代t
x.right=deleteMin(t.right);
x.left=t.left;
}
//更新count
x.count=1+size(x.left)+size(x.right);
return x;
} }
测试代码如下:
package com.cx.serch; public class Test { public static void main(String[] args) {
BST<String,Integer> bst=new BST<String,Integer>();
bst.put("L", 11);
bst.put("P", 10);
bst.put("M", 9);
bst.put("X", 7);
bst.put("H", 5);
bst.put("C", 4);
bst.put("R", 3);
bst.put("A", 8);
bst.put("E", 12);
bst.put("S", 0); show(bst);
System.out.println(bst.min());
bst.deleteMin();
show(bst);
// System.out.println(bst.floor("O"));
System.out.println(bst.min());
}
public static void show(BST<String , Integer> bst) {
for(String s:bst.keys()) {
System.out.print(s+"-"+bst.get(s)+"-"+bst.rank(s)+" ");
}
System.out.println();
} }
2.说明:
(1)对于bst来说,删除操作需要√N,并且如果允许删除,插入和查找的性能也会变为√N
(2)对于最坏的情况(输入顺序或逆序)来说,三种操作都需要N。
(3)当输入是随机的时候,可以有lgN的性能保障,但是不像排序,我们无法保证输入是随机的,因此需要更好的算法,可以保证最坏情况向也能有lgN的性能。
3.2 符号表之二叉查找树BST的更多相关文章
- Symbol Table(符号表)
一.定义 符号表是一种存储键值对的数据结构并且支持两种操作:将新的键值对插入符号表中(insert):根据给定的键值查找对应的值(search). 二.API 1.无序符号表 几个设计决策: A.泛型 ...
- C/C++编译和链接过程详解 (重定向表,导出符号表,未解决符号表)
详解link 有 些人写C/C++(以下假定为C++)程序,对unresolved external link或者duplicated external simbol的错误信息不知所措(因为这样的错 ...
- ELF Format 笔记(七)—— 符号表
最是那一低头的温柔,像一朵水莲花不胜凉风的娇羞,道一声珍重,道一声珍重,那一声珍重里有蜜甜的忧愁 —— 徐志摩 ilocker:关注 Android 安全(新手) QQ: 2597294287 符号表 ...
- 二叉查找树(BST)
二叉查找树(BST):使用中序遍历可以得到一个有序的序列
- IDA 与VC 加载符号表
将Windbg路径下的symsrv.yes 拷贝到ida 的安装目录,重新分析ntoskrnl.exe, 加载本地的符号表 添加环境变量 变量名:_NT_SYMBOL_PATH变量值:SRV*{$P ...
- iOS 符号表恢复 & 逆向支付宝
推荐序 本文介绍了恢复符号表的技巧,并且利用该技巧实现了在 Xcode 中对目标程序下符号断点调试,该技巧可以显著地减少逆向分析时间.在文章的最后,作者以支付宝为例,展示出通过在 UIAlertVie ...
- 使用objdump objcopy查看与修改符号表
使用objdump objcopy查看与修改符号表动态库Linuxgccfunction 我们在 Linux 下运行一个程序,有时会无法启动,报缺少某某库.这时需要查看可执行程序或者动态库中的符 ...
- 符号表(Symbol Tables)
小时候我们都翻过词典,现在接触过电脑的人大多数都会用文字处理软件(例如微软的word,附带拼写检查).拼写检查本身也是一个词典,只不过容量比较小.现实生活中有许多词典的应用: 拼写检查 数据库管理应用 ...
- 符号表实现(Symbol Table Implementations)
符号表的实现有很多方式,下面介绍其中的几种. 乱序(未排序)数组实现 这种情况,不需要改变数组,操作就在这个数组上执行.在最坏的情况下插入,搜索,删除时间复杂度为O(n). 有序(已排序)数组实现 这 ...
随机推荐
- 转】Cassandra单集群实验2个节点
原博文出自于: http://blog.fens.me/category/%E6%95%B0%E6%8D%AE%E5%BA%93/page/3/ 感谢! Cassandra单集群实验2个节点 前言 A ...
- .net 环境下c# 通信
.net环境下通信主要掌握 通信协议(UDP&TCP). 网络抓包工具().:使用方法 点对点通信,IP组播,广播通信 c#中结构体转为字节流方式 c#结构体与c++结构体转换对应关系 开源的 ...
- ABP教程(二)- 将ABP在本地运行起来
上一篇 我们介绍了什么是ABP,这一篇我们通过原作者的”简单任务系统”例子,演示如何运用ABP开发项目 从模板创建空的web应用程序 ABP提供了一个启动模板用于新建的项目(尽管你能手动地创建项目并且 ...
- excel vba 高级过滤
excel vba 高级过滤 Sub shaixuan() Dim database As Range '定义数据区域 Dim criteria_range As Range '定义条件区域 Dim ...
- MySQL ORDER BY IF() 条件排序
源 在做sqlzoo的时候,碰到一个SQL的排序问题,他把符合条件的单独几行,可以放在查询结果的开始,或者查询结果的尾部 通过的方法就是IN语句(也可以通过IF语句) 自己做了个测试,如下,这个是表的 ...
- D2. Toy Train
D2. Toy Train time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...
- E. String Multiplication
E. String Multiplication time limit per test 2 seconds memory limit per test 256 megabytes input sta ...
- ssd训练自己的数据集
1.在ssd/caffe/data下创建VOC2007的目录,将ssd/caffe/data/VOC0712里的create_data.sh.create_list.sh和labelmap_voc.p ...
- vue之$mount
数据挂载 在实例化Vue的时候,两种方式挂载数据 方法一:最常用的方法 var app=new vue({ el:"#app", data(){} ````` }) 注:文档中最常 ...
- intellij idea集成github
IDEA配置github并上传项目 http://www.cnblogs.com/jinjiyese153/p/6796668.html github ssl验证 https://www.cnblog ...