• Java中的TreeSet


    TreeSet特点:

    保证元素唯一
    可以给元素进行排序
    没有索引,不能用普通for循环,
    查找效率高
    结构为二叉排序树(也叫二叉查找树)

    自然排序Comparable:

    自然排序要求类实现了自然排序接口



    这里出现异常的原因是我们的Aniki并没有实现Comparable接口,TreeSet并不知道如何排序所以出问题。


    我们给Aniki类添加Comparable接口,必须重写compareTo方法,当return 0的时候:

    输出只有:
    这是因为第一个元素添加的时候是不需要跟别人比较的,第二个元素的时候是需要跟别人比较的,会按照我们给的排序规则比较,
    而我们给的规则是compareTo方法给的是0,当返回值是0的时候,会认为第二个元素与第一个元素是同一个元素。所以第二个元素添加失败,
    同理第三个也失败。
    当compareTo方法return 1(正数)的时候,会按照输入的顺序输出,也就是认为我们输入的顺序是升序。
    当compareTo方法return -1(负数)的时候,会按照与输入相反的顺序输出,也就是降序。

    compareTo的自定义重写

    我们要想要按照Age排列,要这样写


    是为什么!?

    原因:

    分析:

    注意:以下文字中会出现二叉树,左儿子,右儿子,前序遍历等词汇,请各位同学在《数据结构》中查找含义。

    以上图为例,我们的"99"是第三个要传入的元素,

    "99"进入add(),被调入put,然后我们的"99"赋值给K类型的key,

    然后我们看到了建立root的过程,root就是二叉树的根,指向树根的指针是t(熟悉的C语言表示形式)。

    然后是判断t是否为空,也就是判断根位置有没有元素,如果是空的就把当前的key作为根,

    t以后会作为当前位置指针在循环里继续重复这种判断。

    可想而知第一个add进来的"100",在这里被作为了根root,(这一步非常关键,

    "100"也是之后每次新元素添加的时候,调用下面compareTo(参数)中do——while循环的第一轮的参数,

    第一轮循环回让新元素判为处于根节点左子树或者右子树中,第二轮循环会让根节点左儿子或者右儿子

    为根,再以新的根持续判断,但是唯一不变的是每个元素第一次进来都会先与整个二叉树的根root作比较)

    与我们"99"无关,我们是第三个进来的元素,

    然后看到了一个conparator构造器的判断语句,毫无疑问我们并没有new一个构造器,我们使用了无参的构造方法,

    因此我们的构造器cpr是null,不进入这个if语句。

    来到了else部分,判断了我们存储"99"的key是否是null,是的话抛出空指针异常,我们的key不空。

    之后是一个类型转换,我们的key元素是K类型,K类型继承不继承Comparable不知道,但是我们add(99)中使用的类型

    毫无疑问是implements Comparable的,否则我们会出现之前的异常,原因是:Aniki并没有实现Comparable接口

    因此我们可以强制类型转换为Comparable的k,这里是多态的向上转型。

    然后我们就进入了构建二叉树的do-while循环(循环内的含义参考《数据结构》)这颗二叉树的元素从左往右依次增大。

    我们先与根节点比较,再次提一下这个根节点是我们第一个add的"100",当前的t是指向root根的指针,t.key就是100,

    这里二叉树构建的依据是,compareTo(100)方法的返回值int类型的cmp,而我们就可以在这里做手脚。

    k是我们转换好的key,"99",是我们的"99"调用的compareTo(100),因此this指的是新传入值为"99"的变量。

    在我们的哲♂学例子,更改compareTo(参数)方法,来概变二叉树的真实构建情况,
    Age从小到大如图:

    我们比较Aniki年龄来排序的时候,

    “k”是我们的第二个元素Bili的Age——"50",而根root,也就是“t.key”,是我们第一个元素Van的Age——"51"。

    “k”调用compareTo(51),那么“k”就是this,this.Age就是Bili的Age——"50"

    aniki.Age就是Van的Age——"51"

    return this.Age - aniki.Age;这句话的意思就成了

    return Bili.Age - Van.Age;结果是-1,负数,Bili进入根Van的左儿子,前序遍历的结果是年龄从小到大。

    因此如果我们想从大到小遍历,那么我们写:

    return aniki.Age - this.Age;结果是:+1,Bili被放到Van的右儿子,前序遍历的结果是从大到小。

    Age从大到小:

    Comparator比较器排序:

    我们在新建TreeSet的时候,可以不执着于使用自然排序(实现Comparable接口然后重写compareTo方法),我们可以使用
    Comparator比较器来完成排序(此时就不会使用自然排序)。

    Comparator比较器使用方法:


    我们新建一个TreeSet并且这样new一个Comparator比较器,然后使用匿名内部类重写compare方法,


    简单的修改参数含义,第一个参数是新元素,第二个元素就是我们之前提过的(t指针)元素,它在do——while第一轮中是根节点。
    一开始默认return 0,与compareTo(){return 0;}的时候是一样的。

    我们来讲解下原理:


    根据上图的例子来讲,
    我们new出一个Comparator之后,这个Comparator会被传入final最终态的comparator变量中,之后我们通过
    这个comparator变量来使用比较器。

    我们以("王五",15)这个元素来看整个过程,("王五",15)被add方法调用的put方法中,值传递给了K类型的key。

    然后与之前一样,第一个add进TreeSet来的元素("张三",13)被当作根节点。只不过这次我们判断有没有comparator时,

    我们具备了comparator。于是进入do——while循环,这里构造器调用compare(key,t.key),

    第一个位置是当前传入的元素,第二个位置是指针指向的节点,与之前的compareTo(参数)方法大同小异。

    只不过compareTo是k(值为key的变量)调用的,k.compareTo(t.key)方法中,

    this.key就是compare(key,t.key)中的key,t.key的话两个方法里指的都是当前被比较的节点。

    因此我们:


    含义相同,结果都是:

  • 相关阅读:
    归并排序的java实现
    Hanoi问题java解法
    j2ee之Filter使用实例(页面跳转)
    java工具类之Graphics
    java程序设计之循环链表
    Java程序设计求出岁数
    Java程序设计之链表结构
    CENTOS 6 通过YUM升级GCC到4.7/4.8
    Object c的NSString的使用,创建,拼接和分隔,子string,substring
    Let’s Encrypt 最近很火的免费SSL 使用教程
  • 原文地址:https://www.cnblogs.com/maomaodesu/p/11880193.html
Copyright © 2020-2023  润新知