通常。Tree是Tree,List是List,兩者不太可能混在一起。但apache-commons庫卻用tree實現了實現了List的接口,也就是TreeList類。與標准的LinkedList相比。TreeList略微浪費一點空間,但經常使用操作的時間復雜度均減少到了O(log N),值得在開發中權衡利弊、合理應用。
內部數據結構
TreeList內部包括了一個Thread AVL Tree。AVL Tree非經常見了,是一種典型的Balanced Binary Tree,但以下簡介下Thread Binary Tree。
Thread Binary Tree對Binary Tree添加了下面特性:(1)假設一個節點X沒有左子樹。則把本來應指向左子樹的指針,指向中序遍歷的前節點。(2)假設一個節點X沒有右子樹,則把本來應指向右子樹的指針,指向中序遍歷的后節點。
下圖就是一個Thread Binary Tree,以節點5為例:(1) 節點5沒有左子樹,但節點5的中序遍歷的前節點是4;(2) 節點5沒有右子樹,但節點5的中序遍歷的后節點是6。
這兩個特性提高了二叉樹依序訪問的速度。
下面是TreeList中AVL樹節點的定義
static class AVLNode<E> { /** 左子樹或者中序遍歷的前節點.*/ private AVLNode<E> left; /** true表示left字段是左子樹;false表示left字段是中序遍歷的前節點 */ private boolean leftIsPrevious; /** 右子樹或者中序遍歷的后節點 */ private AVLNode<E> right; /** true表示right字段是右子樹;false表示right字段是中序遍歷的后節點 */ private boolean rightIsNext; /** How many levels of left/right are below this one. */ private int height; /** 在List中的索引相對於父節點索引的偏移量。根節點就是根節點的索引*/ private int relativePosition; /** 節點所保存的有效荷載 */ private E value; }
在邏輯上。TreeList中的節點是依據節點在List中的索引來比較大小的。在實現上,AVLNode類保存的是當前節點的索引相對於父節點的偏移量,也就是relativePosition這個字段。這樣做的長處是,當向List中間插入一個節點時。插入點之后的全部節點的索引值都變大了,但由於AVLNode保存的是相對值。因此僅僅須要改動特定子樹的根節點的relativePosition值,整個子樹全部節點的索引值都會發生變化。
時空復雜度
TreeList既然是用AVL樹實現,則其在特定位置進行插入、刪除和get操作的時間復雜度都是O(log N),另外還要加上較大的時間常量。
LinkedList是採用雙向鏈表實現的。其在特定位置進行插入、刪除和get操作的時間復雜度都是O(N)。
空間復雜度
首先看一下LinkedList中每一個節點的定義:
private static class Entry<E> { E element; Entry<E> next; Entry<E> previous; }
依據以上定義。在32位Hostspot虛擬機下,每一個Entry對象占用6*4=24個byte(這包含8個byte的對象頭、12個byte的真實字段和4個byte的對齊填充)。
依據AVLNode的定義,每一個AVLNode節點占用8*4=32個byte(包含8個byte的對象頭、22個byte的真實字段和2個byte的對齊填充)。
因此,TreeList的每一個節點比LinkedLists多占領8個byte。