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的話兩個方法里指的都是當前被比較的節點。

因此我們:


含義相同,結果都是:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM