TreeMap和TreeSet

[学习目标]

  1. 掌握二叉搜索树的模拟实现
  2. 掌握TreeMap和TreeSet类的使用
  3. 小结
  4. OJ练习

1.搜索树

1.1概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

1.2操作--查找
  • 当前节点不为空;
  • 将要查找的数字key,与当面节点地key比较,相等那就是找到了,返回true;
  • 若不相等,如果待查找的数字比当前节点大,那就往节点的右树找;
  • 如果待查找的数字比当前节点小,那就往节点的左树找;
  • 如此循环,直到当前节点为空为止,返回false;

1.3操作--插入

1.如果是空树,即是root==null,直接插入; 

2.如果不是空树,就是按照逻辑查找该元素节点应该插入的位置

 

1.4操作--删除(难点)

设待删除节点为cur,它的双亲节点为parent

1.待删除节点的左树为空,右树不确定(cur.left==null)

      1.1cur==root  ------>root=cur.right

      1.2cur==parent.left  ------>parent.left=cur.right

      1.3cur==parent.right ------>parent.right==cur.right

2.待删除节点的右树为空,左树不确定(cur.right==null)

      2.1 cur==root  ------>root=cur.left

      2.2cur==parent.left  ------>parent.left=cur.left

      2.3cur==parent.right  ------>parent.right=cur.left

3.待删除节点左右树不为空

     这里用替换法作为方法,即找到cur左子树的最大值或者右子树的最小值p节点,将p值赋给           cur,删除掉p, 这里选择找左子树的最大值作为分享

那么这个最大值就有可能两种情况,   记tp为p的是双亲节点

     

    3.1  p==tp.right,这里选择删除3,左子树最大值就是2(这里由于2是最大的,所以2不会有右子树,但     是可以有左子树)

cur.key=p.key

tp.right=p.left;

    3.2  p==tp.left 比如删除7,左子树的最大值就是6

cur.key=p.key

tp.left=p.elft

1.5 实现
public class BinarySearchTree {
    public static class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;

        public TreeNode(int val){
            this.val=val;
        }
    }

    public TreeNode root;

    public boolean search(int val){
        TreeNode cur=root;
        if(cur==null){
            return false;
        }
        while(cur!=null){
            if(cur.val<val) {
                cur = cur.right;
            }else if(cur.val>val){
                cur=cur.left;
            }else{
                return true;
            }
        }
        return false;
    }

    public void insert(int val){
        TreeNode cur=root;
        TreeNode pcur=null;
        TreeNode newNode=new TreeNode(val);
        if(root==null){
            root=newNode;
            return;
        }
        while(cur!=null){
            if(cur.val>val){
                pcur=cur;
                cur=cur.left;
            }else{
                pcur=cur;
                cur=cur.right;
            }
        }
        if(pcur.val>val){
            pcur.left=newNode;
        }else{
            pcur.right=newNode;
        }
    }

    public void remove(int val){
         TreeNode cur=root;
         TreeNode parent=null;
         while(cur!=null){
             if(cur.val<val){
                 parent=cur;
                 cur=cur.right;
             }else if(cur.val>val){
                 parent=cur;
                 cur=cur.left;
             }else{
                 removeNode(parent,cur);
                 return;
             }
         }
    }
    private void removeNode(TreeNode parent,TreeNode cur){
        if(cur.left==null){
            if(cur==root){
                root=cur.right;
            }else if(parent.left==cur){
                parent.left=cur.right;
            }else{
                parent.right=cur.right;
            }
        }else if(cur.right==null){
            if(cur==root){
                root=cur.left;
            }else if(parent.left==cur){
                parent.left=cur.left;
            }else{
                parent.right=cur.left;
            }
        }else{
            TreeNode tp=cur;
            TreeNode t=cur.right;
            while(t.right!=null){
                tp=t;
                t=t.right;
            }
            cur.val=t.val;
            if(tp.left==t){
                tp.left=t.left;
            }else{
                tp.right=t.left;
            }
        }
    }

}

2.1模型

一般把搜索的数据称为关键字(Key),和关键字对应的称为值(Value),将其称之为Key-value的键值对,所以 模型会有两种:

1. 纯 key 模型,比如:

  • 有一个英文词典,快速查找一个单词是否在词典中
  • 快速查找某个名字在不在通讯录中

2. Key-Value 模型,比如:

  • 统计文件中每个单词出现的次数,统计结果是每个单词都有与其对应的次数:
  • 梁山好汉的江湖绰号:每个好汉都有自己的江湖绰号

而Map中存储的就是key-value的键值对,Set中只存储了Key。

2.2 Map

2.2.1 关于Map的说明

Map是一个接口类,不继承于Collections接口,存储的是Key-Value的键值对,Key的唯一的,不可重复的

2.2.2 关于Map.Entry<>的说明

Map.Entry<>是Map的内部类,就像链表的节点的一样,只不过存储的Key-Value两个值的键值对,该内部类中主要提供了 的获取,value的设置以及Key的比较方式。

方法说明
K getKey()返回Key的值
V getValue()返回Value的值
V setValue(V value)将键值对中指定的Value换成value

2.2.3 Map的常用方法

方法说明
V get(Object key)

返回key对应的Value

没有对应的key返回null

V getOrDefault(Object key,V objectValue)

返回key对应的Value

找不到key返回objectValue

V put(Object key,V value)

添加键值对,key重复就修改value

V remove(Object key)删除key键值对
boolean containsKey(Object key)判断是否包含key
boolean containsValue(V value)判断是否包含value
Set<Map.Entry<Key,Vlaue>> entrySet返回所有Map的键值对关系,用于获取所有的键值对,常用于遍历
Set<Key> Keyset

返回所有的Key集合

Collection<V> values()返回所有 value 的可重复集合
注意:
  1. Map是一个接口,不能直接实例化,必须实例化它的实现类HashMap和TreeMap;
  2. Map存放的键值对Key不可重复,Value可以重复
  3. key不可以修改,只能删除,重新添加,value可以修改
  4. Map没有继承Collections,不能遍历,所以要借助Map中的Set<Map.Entry<K, V>> entrySet(),获取  Map中所有的键值对;

2.2.4 TreeMap的使用案例

//实例化一个TreeMap,key是一个String类型,Value是Integer类型,切记要使用包装类
         Map<String,Integer> treeMap=new TreeMap<>();
         //增加
        treeMap.put("zhangSan",23);
        treeMap.put("liSi",24);
        treeMap.put("wangWu",25);
        treeMap.put("zhaoLiu",26);
        //删除
        treeMap.remove("liSi");
        //修改
        treeMap.put("zhangSan",24);
        System.out.println("===============================================");
        //查找
        System.out.println(treeMap.get("zhangsan"));//null;
        System.out.println(treeMap.getOrDefault("zhangsan",0));//0
        System.out.println(treeMap.get("zhangSan"));//24
        System.out.println("===============================================");
        //判断是否包含
        System.out.println(treeMap.containsKey("zhangsan"));//false
        System.out.println(treeMap.containsKey("zhangSan"));//true
        System.out.println(treeMap.containsValue(23));//false
        System.out.println(treeMap.containsValue(24));//true
        System.out.println("===============================================");
        //获取key集合
        for(String x:treeMap.keySet()){
            System.out.println(x);
            ///wangWu
            //zhangSan
            //zhaoLiu
        }
        System.out.println("===============================================");
        //获取value集合
        for(int x:treeMap.values()){
            System.out.println(x);
            //25
            //24
            //26
        }
        System.out.println("===============================================");
        //Key-Value集合
        for(Map.Entry<String,Integer> entry:treeMap.entrySet()){
            System.out.println("Key-->"+entry.getKey()+"  "+"Value-->"+entry.getValue());
            //Key-->wangWu  Value-->25
            //Key-->zhangSan  Value-->24
            //Key-->zhaoLiu  Value-->26
        }
执行结果:

2.3 Set

2.3.1 关于Set的说明

set是继承于Collections,只存储Key,Key不重复;

2.3.2 Set的常用方法说明

方法说明
boolean add(E)增加key
void clear()清空所有key
boolean contains(Object)判断是否包含key
boolean isEmpty()判断Set是否为空
Iterator<E> iterator()返回迭代器
boolean remove(Object)删除节点
int size()返回set中元素的个数
Object[] toArray()将set中的元素转换为数组返回
boolean containsAll(Collection c)集合c中的元素是否在set中全部存在,是返回true,否则返回 false
boolean addAll(Collection c)将集合c中的元素添加到set中,可以达到去重的效果
注意
  1. Set底层使用Map实现的,将key和Object作为键值对插入Map中
  2. Set的Key不能重复,Set结构最大的用处就是去重

2.3.3 TreeSet的使用案例

import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

public class demo2 {
    public static void main(String[] args) {
        //实例化一个TreeSet,key是一个String类型,切记要使用包装类
        Set<String> treeSet=new TreeSet<>();

        //增加key
        System.out.println(treeSet.add("zhangSan"));//true
        treeSet.add("Lisi");
        treeSet.add("wangWu");
        treeSet.add("zhaoLiu");
        System.out.println("======================================");

        //返回set中元素的个数
        System.out.println(treeSet.size());//3
        System.out.println("======================================");

        //清空所有key
        //treeSet.clear();

        //判断是否包含key
        System.out.println(treeSet.contains("zhangSan"));//true
        System.out.println("======================================");

        //判断Set是否为空
        System.out.println(treeSet.isEmpty());
        System.out.println("======================================");

        //返回迭代器
        Iterator<String> iterator=treeSet.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println("======================================");

        //删除节点
        System.out.println(treeSet.remove("zhangSan"));//true
        System.out.println(treeSet.remove("zhangsan"));//false
        System.out.println("======================================");



        //将set中的元素转换为数组返回
        Object[] ret=treeSet.toArray();
        for (Object x:ret) {
            System.out.println(x);
            //Lisi
            //wangWu
            //zhaoLiu
        }
    }
}
执行结果:

3.小结

相信通过上面的学习,你已经发现TreeMap和TreeSet所使用的数据结构正是二叉搜索树(二叉排列树);自己先去模拟实现一下二叉搜索树,再去使用一下TreeSet和TreeMap,熟练掌握这个数据结构是很有必要的;

4.OJ练习

1.只出现一次的数字

2.宝石与石头

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a添砖Java

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值