1、二叉搜索树的来历:array查询方便,但添加删除元素比较麻烦(元素个数超过容量需要扩容,添加删除操作可能也要不停来回复制)。linked list方便添加元素,但是查询比较困难(只能从头一个个往下找)。因此发明了二叉搜索树这种数据结构,它是一种linked list结构,但集合了array和linked list的优点:方便查询;方便添加元素。
2、二叉搜索树的结构:就是一种linked list结构(这种class的filed里面至少包含一个reference能够指向未来的其他object地址),里面含2个reference(可以分别叫做left和right),一个可以比较大小的Key,一个值Value,以及整数N(表示该节点下面所连接的所有节点个数,包括该节点本身)。
3、二叉搜索树的特点:对于任意一个节点,它的Key值一定大于left指向节点的Key值,小于right指向节点的Key值。
3、二叉搜索树的特点:对于任意一个节点,它的Key值一定大于left指向节点的Key值,小于right指向节点的Key值。
二叉搜索树至少要包括添加,遍历,查询和删除这四个基本方法,我的设计思路分别如下:
1、添加。用递归法(递归实际保存了每层所有信息)。
方案一,void:每次带着以下信息(argument list)进入下一层:下一层的reference(往哪儿去),这一层的reference,左连接还是右连接,Key值,Value值。在下一层的任务:检查带入的Key值和已有Key值。若覆写(Key值相等),就更新Value值,然后原路返回顶层(回去的路上顺带更新一下N值);若不覆写(Key值没对上号),继续带上新信息往下走,最终将进入一片空白区域,在此区域要完成Key值,Value值的录入,再根据上一层的reference,把本层的reference连接到其左侧或右侧,然后原路返回顶层(返回的路上顺便更新一下N值)。
方案二,带return:进入下一层时,不需要携带那么多信息,只带上:下一层的reference(往哪儿去),Key值和Value值。因为反正还要原路返回,只要返回的时候带上下一层的reference就可以。因此在每一层的任务是:检查带入的Key值和已有Key值是否相等。若相等,执行覆写操作,但注意原路返回的同时要记下下一层的reference,回到上一层把他关联给其left或right,并更新N值(尽管覆写操作这样返回没什么意义)。若不等,继续带上新信息往下走,最终将到达一片空白之地,在这里完成Key值和Value的录入,原路返回之前,记下本层reference,然后返回给上一层的left或right,顺便更新N值。
2、遍历(但大小顺序)。仍然用递归法。
方案一,void:遍历跟查找类似,不需要跟添加删除那样改变原有数据结构,因此所携带的信息非常少,只有一个root。假设从小到大遍历。我们首先一定会要找到最小值,方法是从root开始,一路左走。最终走到了最小值的左边,也就是null。这时需要停下来,返回上一层(最小值所在层),然后打印上一层信息。完成以后,我们要找比最小值稍大一点的数,也就是右侧最小值,若不为空,继续一路左走深入下去,若右侧为空,那么本层也结束了,应返回上一层,打印信息,再找上一层的右侧最小值...
方案二,带return:跟一差不多,只不过每层都要返回reference再关联第二遍。优点画蛇添足。
1、添加。用递归法(递归实际保存了每层所有信息)。
方案一,void:每次带着以下信息(argument list)进入下一层:下一层的reference(往哪儿去),这一层的reference,左连接还是右连接,Key值,Value值。在下一层的任务:检查带入的Key值和已有Key值。若覆写(Key值相等),就更新Value值,然后原路返回顶层(回去的路上顺带更新一下N值);若不覆写(Key值没对上号),继续带上新信息往下走,最终将进入一片空白区域,在此区域要完成Key值,Value值的录入,再根据上一层的reference,把本层的reference连接到其左侧或右侧,然后原路返回顶层(返回的路上顺便更新一下N值)。
方案二,带return:进入下一层时,不需要携带那么多信息,只带上:下一层的reference(往哪儿去),Key值和Value值。因为反正还要原路返回,只要返回的时候带上下一层的reference就可以。因此在每一层的任务是:检查带入的Key值和已有Key值是否相等。若相等,执行覆写操作,但注意原路返回的同时要记下下一层的reference,回到上一层把他关联给其left或right,并更新N值(尽管覆写操作这样返回没什么意义)。若不等,继续带上新信息往下走,最终将到达一片空白之地,在这里完成Key值和Value的录入,原路返回之前,记下本层reference,然后返回给上一层的left或right,顺便更新N值。
2、遍历(但大小顺序)。仍然用递归法。
方案一,void:遍历跟查找类似,不需要跟添加删除那样改变原有数据结构,因此所携带的信息非常少,只有一个root。假设从小到大遍历。我们首先一定会要找到最小值,方法是从root开始,一路左走。最终走到了最小值的左边,也就是null。这时需要停下来,返回上一层(最小值所在层),然后打印上一层信息。完成以后,我们要找比最小值稍大一点的数,也就是右侧最小值,若不为空,继续一路左走深入下去,若右侧为空,那么本层也结束了,应返回上一层,打印信息,再找上一层的右侧最小值...
方案二,带return:跟一差不多,只不过每层都要返回reference再关联第二遍。优点画蛇添足。
添加和遍历具体如下:
public class BST<Key extends Comparable<Key>, Value>
{
private Node root;
public Node getRoot() { return root; }
private class Node
{
Node left, right;
int N;
Key key;
Value val;
Node(Key key, Value val)
{
this.key = key;
this.val = val;
N = 1;
}
}
public int size() { return size(root); }
private int size(Node x)
{
if(x == null) return 0;
else return x.N;
}
public void addElement(Key key, Value val) { root = addElement(key, val, root); }
private Node addElement(Key key, Value val, Node x)
{
if(x == null) return new Node(key, val);
int cmp = key.compareTo(x.key);
if(cmp > 0) x.right = addElement(key, val, x.right);
else if(cmp < 0) x.left = addElement(key, val, x.left);
else x.val = val;
x.N = size(x.left) + size(x.right) + 1;
return x;
}
public void add(Key key, Value val) { add(key, val, root, null, 2); }
private void add(Key key, Value val, Node current, Node previous, int i)
{
if(current == null)
{
if(i == 0) previous.left = new Node(key, val);
else if(i == 1) previous.right = new Node(key, val);
else root = new Node(key, val);
}
else
{
int cmp = key.compareTo(current.key);
if(cmp < 0) add(key, val, current.left, current, 0);
else if(cmp > 0) add(key, val, current.right, current, 1);
else current.val = val;
current.N = size(current.left) + size(current.right) + 1;
}
}
public void iterate() { root = iterate(root);}
private Node iterate(Node x)
{
if(x != null)
{
x.left = iterate(x.left);
System.out.println(x.key + ": " + x.val);
x.right = iterate(x.right);
}
return x;
}
public void iterate2() { iterate2(root); }
private void iterate2(Node x)
{
if(x == null) return;
else
{
iterate2(x.right);
System.out.println(x.key + ": " + x.val);
iterate2(x.left);
}
}
}