红魔咖啡馆

头发越掉越多,头发越掉越少

0%

【CS61B】Lab6-BSTMap

Lab6 - BSTMap

实现以二叉搜索树为底层的Map

  • 该类需要有两个泛型K, V表示键值对
  • 其中键K要是Comparable的实现

创建类

创建实现文件并修改开头

1
public class BSTMap<K extends Comparable<K>, V> implements Map61B<K, V>

即BSTMap要是Map61B的实现,且泛型K是Comparable的extends

因为要实现一个二叉搜索树,故我们需要嵌套一个节点类用于储存数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private class BSTNode{
    K key;
    V val;
    BSTNode l_child;
    BSTNode r_child;
    int size;
    BSTNode(K k, V v){
        key = k;
        val = v;
        l_child = null;
        r_child = null;
        size = 0;
    }
}

同时写构造函数

1
2
3
4
5
6
public BSTMap(){
    root = null;
}
...
private BSTNode root;
private int size = 0;

size()与clear()

1
2
3
4
5
6
7
8
9
10
@Override
public int size() {
    return size;
}

@Override
public void clear() {
    size = 0;
    root = null;
}

size即返回树的size,clear将树根置为null,且要把size置零

put()

实现放入节点

这里我们需要在根节点的基础上使用递归向下添加节点,具体可以创建一个递归辅助函数,传入每次准备插入的节点进行递归

  • 若传入节点为空节点,则创建一个新节点并传入键值,并将size+1表示多一个节点
  • 若不为空,则比较当前节点的键与传入的键的大小
    • 若小于,则按照BST的性质应该作为该节点的左子树,因此向左递归
    • 若大于,则按照BST的性质应该作为该节点的右子树,因此向右递归
    • 若相等,则更新键处的值为最新
  • 返回更新的节点

注意:这里的键不能直接比较,需要使用CompareTo函数,这也是继承Comparable类的作用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
public void put(K key, V value) {
    root = put_assist(key, value, root);
}

private BSTNode put_assist(K key, V value, BSTNode N){
    if (N==null){
        size++;
        return new BSTNode(key, value);
    }
    if (key.compareTo(N.key)<0){
        N.l_child = put_assist(key, value, N.l_child);
    }
    else if (key.compareTo(N.key)>0){
        N.r_child = put_assist(key, value, N.r_child);

    }
    else{
        N.val = value;
    }
    //N.size = 1+ size(N.l_child) +size(N.r_child);
    return N;
}

get()

同理,我们创建一个递归辅助函数,通过递归方法遍历左右子树,将给定的key与途径的节点比较

  • 若小于则向左子树递归查找
  • 若大于则向右子树递归查找
  • 若相等则说明找到对应键,返回对应值即可
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public V get(K key) {
    return get_assist(key, root);
}

private V get_assist(K key, BSTNode N){
    if (N==null) {
        return null;
    }
    if (key.compareTo(N.key)<0) return get_assist(key, N.l_child);
    else if (key.compareTo(N.key)>0) return get_assist(key, N.r_child);
    else return N.val;
}

ContainsKey()

判断map中是否含有指定的键

一开始的想法是直接调用get,但是问题是是否包含键与值没有关系,当键存在而值为空时,get返回的仍为null,会造成误判

故我们仿照get重新写一个辅助函数,但是返回布尔值,若键为空返回false,若找到对应键返回true

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public boolean containsKey(K key) {
    return contains(key, root);
}

private boolean contains(K key, BSTNode N){
    if (N==null){
        return false;
    }
    if (key.compareTo(N.key)<0) return contains(key, N.l_child);
    else if (key.compareTo(N.key)>0) return contains(key, N.r_child);
    else return true;

}

Iterator()

实现按序迭代的功能

我们可以单独把键存入一个列表,并且返回该列表自带的迭代器

这时需要一个辅助函数遍历BST并取出所有键,而若要实现按序,我们采用中序遍历,这样可以保证比节点小的先放入,比节点大的后放入,实现有序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public Iterator<K> iterator() {
    List<K> iterKeys = new ArrayList<>();
    iterMid(root, iterKeys);
    return iterKeys.iterator();
}
private void iterMid(BSTNode N, List<K> Array){
    if (N==null){
        return ;
    }
    iterMid(N.l_child, Array);
    Array.add(N.key);
    iterMid(N.r_child, Array);

}

KeySet()

暂时搁置

remove()

删除操作,仍采用递归逻辑

首先判断是否含有对应key,若没有直接返回null

若有则记录旧值,进行删除操作并返回旧值

删除通过辅助递归函数来进行:

  • 若进入函数已经是空节点,则返回null
  • 否则比较传入键与每个节点(注意,这里要将return的节点赋值到对应子树上,这样才能算连上)
    • 小于则向左边递归
    • 大于则向右边递归
    • 若等于则发现待删除节点
  • 找到后分情况进行删除
    • 若本身为叶子节点,则直接置空
    • 若有左孩子或有孩子,将其左孩子或右孩子直接替代当前节点即可
    • 若都有,我们可以让一个辅助节点cur指向待删除节点左孩子,往下递归找到左孩子的最大节点,让要删除节点的右子树放在此时cur的右子树上,删除待删除节点,这样既能保持BST的性质又能实现删除
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
@Override
public V remove(K key) {
    if (!containsKey(key)){
        return null;
    }
    V oldValue = get(key);
    root = remove_assist(key, root);
    size--;
    return oldValue;
}
private BSTNode remove_assist(K key, BSTNode N){
    if (N == null) {
        return null;
    }
    if (key.compareTo(N.key)<0){
        N.l_child = remove_assist(key, N.l_child);
    }
    else if (key.compareTo(N.key)>0){
        N.r_child = remove_assist(key, N.r_child);
    }
    else if (key.compareTo(N.key)==0){
        if (N.l_child==null && N.r_child==null){
            return null;
        }
        else if (N.l_child == null){
            return N.r_child;
        }
        else if (N.r_child == null){
            return N.l_child;
        }
        else{
            BSTNode cur = N.l_child;
            while(cur.r_child!=null){
                cur = cur.r_child;
            }
            cur.r_child = N.r_child;
            return cur;
        }
    }
    return N;
}