Java 中的 TreeSet

TreeSet 是一種可有序存放元素的集合,HashSet 是 value 爲固定值的 HashMap,TreeSet 是 value 爲固定值得 TreeMap。

TreeMap

       ┌───┐
       │Map│
       └───┘
         ▲
    ┌────┴─────┐
    │          │
┌───────┐ ┌─────────┐
│HashMap│ │SortedMap│
└───────┘ └─────────┘
               ▲
               │
          ┌─────────┐
          │ TreeMap │
          └─────────┘

HashMap 利用了 hashCode,TreeMap 則利用了樹,一個二叉樹。

Treemap Internal Working

比較 Key

TreeMap 的有序通過比較 key 來實現,無法利用 hashCode 來比較,它需要有一個比較 key 的規則。可通過 Key 繼承 Comparable 接口或設置 Comparator 來提供。

Integer

public final class Integer extends Number implements Comparable<Integer> {
    ...
    public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }
    // 根據元素大小比較,-1 小於 anotherInteger,0 等於,1 大於
    public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }
    ...
}

String

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ...
    public int compareTo(String anotherString) {
        byte v1[] = value;
        byte v2[] = anotherString.value;
        if (coder() == anotherString.coder()) {
            return isLatin1() ? StringLatin1.compareTo(v1, v2)
                              : StringUTF16.compareTo(v1, v2);
        }
        return isLatin1() ? StringLatin1.compareToUTF16(v1, v2)
                          : StringUTF16.compareToLatin1(v1, v2);
     }
    ...
}

final class StringLatin1 {
    ...
    public static int compareTo(byte[] value, byte[] other, int len1, int len2) {
        // 只比較短的部分
        int lim = Math.min(len1, len2);
        for (int k = 0; k < lim; k++) {
            // 返回不同的字符的 assic 差值
            if (value[k] != other[k]) {
                return getChar(value, k) - getChar(other, k);
            }
        }
        // 當短部分都相同時,比較長度
        return len1 - len2;
    }
    ...
    public static char getChar(byte[] val, int index) {
        return (char)(val[index] & 0xff);
    }
    ...
}

自定義類

使用 Comparator

// A class to represent a student. 
class Student { 
    int rollno; 
    String name, address; 
  
    // Constructor 
    public Student(int rollno, String name, 
                   String address) 
    { 
        this.rollno = rollno; 
        this.name = name; 
        this.address = address; 
    } 
  
    // Used to print student details 
    // in main() 
    public String toString() 
    { 
        return this.rollno + " "
            + this.name + " "
            + this.address; 
    } 
} 
  
// Comparator implementattion 
class Sortbyroll 
    implements Comparator<Student> { 
  
    // Used for sorting in ascending order of 
    // roll number 
    public int compare(Student a, Student b) 
    { 
        return a.rollno - b.rollno; 
    } 
} 
  
public class TreeMapImplementation { 
  
    static void Example2ndConstructor() 
    { 
        // Creating an empty TreeMap 
        TreeMap<Student, Integer> tree_map 
            = new TreeMap<Student, Integer>(new Sortbyroll()); 
  
        // Mapping string values to int keys 
        tree_map.put(new Student(111, "bbbb", 
                                 "london"), 
                     2); 
        tree_map.put(new Student(131, "aaaa", 
                                 "nyc"), 
                     3); 
        tree_map.put(new Student(121, "cccc", 
                                 "jaipur"), 
                     1); 
  
        // Displaying the TreeMap 
        System.out.println("TreeMap: "
                           + tree_map); 
    } 
  
    public static void main(String[] args) 
    { 
  
        System.out.println("TreeMap using "
                           + "TreeMap(Comparator)"
                           + " constructor:\n"); 
        Example2ndConstructor(); 
    } 
} 

輸出

TreeMap using TreeMap(Comparator) constructor:

TreeMap: {121 cccc jaipur=1, 131 aaaa nyc=3, 141 bbbb london=2}

使用 Comparable

class Student implements Comparable<Student>{
    int rollno;
    String name, address;

    // Constructor
    public Student(int rollno, String name,
                   String address)
    {
        this.rollno = rollno;
        this.name = name;
        this.address = address;
    }

    public int compareTo(Student anotherStudent)
    {
        return this.rollno - anotherStudent.rollno;
    }

    // Used to print student details
    // in main()
    public String toString()
    {
        return this.rollno + " "
                + this.name + " "
                + this.address;
    }
}

如何排序

添加時,跟 root 節點比較,小於放到左邊,大於放到右邊。

    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        // 如果構造函數設置了 comparator,cpr 將不爲 null
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked") // 獲取 key 的 Comparable
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0)
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }

時間複雜度

針對二叉樹,4 層的總節點:2^0^ + 2^1^ + 2^2^ + 2^3^ -> 2^4^,如果樹節點數爲 n,樹的高度爲 log(n)。

所以 TreeMap 的查找和新增時間複雜度爲 O(log(n))。

vs LinkedHashMap

LinkedHashMap 使用一個額外雙向鏈表,記錄插入順序,所以它是根據插入順序排序。

延伸閱讀

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章