树的入门
树是由n(n>=1)个有限结点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
二叉树就是度不超过2的树(每个结点最多有两个子结点)
二叉树的结点类
private class Node<Key,Value>{
//存储键
public Key key;
//存储值
private Value value;
//记录左子结点
public Node left;
//记录右子结点
public Node right;
public Node(Key key, Value value, Node left, Node right) {
this.key = key;
this.value = value;
this.left = left;
this.right = right;
}
}
1.获取树中元素的个数
public int size() {
return N;
}
2.向树中添加元素key-value
public void put(Key key, Value value) {
root = put(root, key, value);
}
3.向指定的树x中添加key-value,并返回添加元素后新的树
private Node put(Node x, Key key, Value value) {
//如果子树x为空
if (x == null) {
N++;
return new Node(key, value, null, null);
}
//如果子树x不为空
//比较x节点的键和key的大小:
int i = key.compareTo(x.key);
if (i > 0) {
//如果key大于x节点的键,则继续找x节点的右子树
x.right = put(x.right, key, value);
} else if (i < 0) {
//如果key小于x节点的键,则继续找x节点的左子树
x.left = put(x.left, key, value);
} else {
//如果key等于x节点的键,则替换x节点的值为value即可
x.value = value;
}
return x;
}
4.从指定的树x中,查找key对应的值
public Value get(Node x, Key key) {
if (x == null) {
return null;
}
int i = key.compareTo(x.key);
if (i > 0) {
return get(x.right, key);
} else if (i < 0) {
return get(x.left, key);
} else {
return x.value;
}
}
5.删除指定树x中的key对应的value,并返回删除后的新树
public Node delete(Node x, Key key) {
if (x == null) {
return null;
}
int cmp = key.compareTo(x.key);
if (cmp > 0) {
//新结点的key大于当前结点的key,继续找当前结点的右子结点
x.right = delete(x.right, key);
} else if (cmp < 0) {
//新结点的key小于当前结点的key,继续找当前结点的左子结点
x.left = delete(x.left, key);
} else {
//新结点的key等于当前结点的key,当前x就是要删除的结点
//个数-1
N--;
//得找到右子树中最小的节点
//1.如果当前结点的右子树不存在,则直接返回当前结点的左子结点
if (x.right == null) {
return x.left;
}
//2.如果当前结点的左子树不存在,则直接返回当前结点的右子结点
if (x.left==null){
return x.right;
}
//3.当前结点的左右子树都存在
//3.1找到右子树中最小的结点
Node minNode=x.right;
while (minNode.left!=null){
minNode=minNode.left;
}
//3.2删除右子树中最小的结点
Node n=x.right;
while (n.left!=null){
if (n.left.left==null){
n.left=null;
}else {
n=n.left;
}
}
//3.3让被删除结点的左子树称为最小结点minNode的左子树,
minNode.left=x.left;
// 让被删除结点的右子树称为最小结点minNode的右子树
minNode.right=x.right;
//3.4让被删除结点的父节点指向最小结点minNode
x = minNode;
}
return x;
}
6.找出指定树x中最小的键所在的结点
private Node min(Node x){
if (x.left!=null){
return min(x.left);
}else{
return x;
}
}
7.找出指定树x中最大键所在的结点
prpublic Node max(Node x){
if (x.right!=null){
return max(x.right);
}else{
return x;
}
}
8.使用前序遍历,把指定树x中的所有键放入到keys队列中
前序遍历:先访问根结点,然后再访问左子树,最后访问右子树
private void preErgodic(Node x,Queue<Key> keys){
if (x==null){
return;
}
//1.把当前结点的key放入到队列中;
keys.enqueue(x.key);
//2.找到当前结点的左子树,如果不为空,递归遍历左子树
if (x.left!=null){
preErgodic(x.left,keys);
}
//3.找到当前结点的右子树,如果不为空,递归遍历右子树
if (x.right!=null){
preErgodic(x.right,keys);
}
}
9.使用中序遍历,把指定树x中的所有键放入到keys队列中
中序遍历:先访问左子树,中间访问根节点,最后访问右子树
private void midErgodic(Node x,Queue<Key> keys){
if (x==null){
return;
}
//1.找到当前结点的左子树,如果不为空,递归遍历左子树
if (x.left!=null){
midErgodic(x.left,keys);
}
//2.把当前结点的key放入到队列中;
keys.enqueue(x.key);
//3.找到当前结点的右子树,如果不为空,递归遍历右子树
if (x.right!=null){
midErgodic(x.right,keys);
}
}
10.使用后序遍历,把指定树x中的所有键放入到keys队列中
后序遍历:先访问左子树,再访问右子树,最后访问根节点
private void afterErgodic(Node x,Queue<Key> keys){
if (x==null){
return;
}
//1.找到当前结点的左子树,如果不为空,递归遍历左子树
if (x.left!=null){
midErgodic(x.left,keys);
}
//2.找到当前结点的右子树,如果不为空,递归遍历右子树
if (x.right!=null){
afterErgodic(x.right,keys);
}
//3.把当前结点的key放入到队列中;
keys.enqueue(x.key);
}
11.计算指定树的最大深度
private int maxDepth(Node x) {
//1.如果根结点为空,则最大深度为0;
if (x == null) {
return 0;
}
int max = 0;
int maxL = 0;
int maxR = 0;
//2.计算左子树的最大深度;
if (x.left != null) {
maxL = maxDepth(x.left);
}
//3.计算右子树的最大深度;
if (x.right != null) {
maxR = maxDepth(x.right);
}
//4.当前树的最大深度=左子树的最大深度和右子树的最大深度中的较大者+1
max = maxL > maxR ? maxL + 1 : maxR + 1;
return max;
}