1.本篇博客所研究的內容爲平衡二叉樹,平衡二叉樹的特性爲利用快速的利用二分法進行查找數據,數據結構如下圖所示:
在上圖中,節點4爲TreeMap的根節點,根節點爲我們進行尋址所使用的最初的節點,每個節點都具有左右兩個節點的引用,左側節點引用的對象是一個在比較時小於節點本身的值,而右邊則是比節點本身要大的值。而如其名稱,在該樹的每一個節點上,都是一個單獨的平衡二叉樹結構。
2.每個平衡二叉樹的節點上都具有一個平衡因子,這個平衡因子的值只能夠爲 -2,-1,0,1,2五個值。當任何一個節點的平衡因子的值爲-2或者2時,則這顆二叉樹就失去了平衡。平衡因子如下圖所示:
在上圖中最上方節點則爲這棵平衡二叉樹的根節點,我們可以看到圖中有兩個節點的平衡因子值爲-2,在這時,這棵樹的平衡就是被打破的,處於一種失衡狀態。
3.在這裏先不對這個樹的失衡進行處理,而是來分析一下這個樹的平衡因子的計算方式:樹的平衡因子,是使用樹結構兩邊的深度差來進行計算的,計算方式爲:左節點深度 - 右節點深度。節點深度圖如下圖所示
我們可以看到一個節點的深度,是通過這個節點的左右兩個子節點的深度進行計算的,如果子節點不存在,則視該子節點的深度值爲0,子節點存在時,該節點的深度,則爲左右兩個子節點深度大的節點的深度 +1。
4.當我們理解了樹的深度概念以後,對於二叉樹的平衡因子的計算方式就會簡單很多了。此時,這棵平衡二叉樹中是存在失衡現象的,因此我們需要對這個樹的節點進行旋轉。在對樹的節點進行旋轉時,分爲左旋與右旋兩種形式。
右側圖片爲進行左旋操作時的引用變化:
右旋操作爲左旋操作的逆操作:
當我們對上圖中的深度爲3的節點進行左旋操作後,可以得到如下圖所示的
左側爲原本深度爲3的節點進行左旋操作以後的深度分部,而右側爲左旋操作以後的平衡因子值變化。
我們在進行左旋操作以後,樹的深度就需要進行一次重新的處理,而平衡因子也需要在樹的深度重新計算完成以後,也重新進行一次計算,這樣在進行旋轉操作以後仍未達到理想狀態,可以進行多次的旋轉操作。
先來看代碼,然後針對代碼進行分析:
package volatileStudy.model;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class BalanceTreeMap<K, V> implements Map<K, V>{
/**
* 樹結構中的根節點
*/
private Entry<K,V> rootEntry;
/**
* 這個Map中已有鍵值對的數量
*/
private int size = 0;
public BalanceTreeMap() {
}
@Override
public int size() {
return this.size;
}
/**
* 如果該Map中不存在鍵值映射,則返回true
*/
@Override
public boolean isEmpty() {
return this.rootEntry == null;
}
/**
* 向平衡二叉樹中添加一個鍵值對
*/
@Override
public V put(K key, V value) {
if(this.rootEntry == null){
this.rootEntry = new Entry<K, V>(key, value);
this.rootEntry.balance = 0;
this.rootEntry.deepness = 1;
this.size ++ ;
return value;
}
Entry<K, V> newEntry = new Entry<K, V>(key,value);
Entry<K, V> superEntry = rootEntry;
while(true){
if(key.hashCode() > superEntry.getKey().hashCode()){
if(superEntry.rightEntry != null){
superEntry = superEntry.rightEntry;
}else{
superEntry.rightEntry = newEntry;
newEntry.superEntry = superEntry;
break ;
}
}else if(key.hashCode() < superEntry.getKey().hashCode()){
if(superEntry.leftEntry != null){
superEntry = superEntry.leftEntry;
}else{
superEntry.leftEntry = newEntry;
newEntry.superEntry = superEntry;
break ;
}
}else{
superEntry.setValue(value);
return value;
}
}
this.checkDeepness(newEntry);
this.adjustLoadFactor(superEntry);
this.checkBalance(newEntry);
this.size ++ ;
return value;
}
/**
* 矯正一個節點及其所有父節點的平衡因子
* @param entry
*/
private void checkDeepness(Entry<K, V> entry){
entry.deepness = 1;
for(int i = 1; entry.superEntry != null && i == entry.superEntry.deepness ; i++ ){
entry.superEntry.deepness = i+1;
entry = entry.superEntry;
}
}
/**
* 如果在這個Map中包含指定key的映射關係,則返回true
*/
@Override
public boolean containsKey(Object key) {
// TODO Auto-generated method stub
return false;
}
/**
* 如果在鍵值映射中,存在該值,則返回true
*/
@Override
public boolean containsValue(Object value) {
// TODO Auto-generated method stub
return false;
}
/**
* 將一個Map中的數據全部存入這個Map中
*/
@Override
public void putAll(Map m) {
}
/**
* 移除該對象中的所有的鍵值關係
*/
@Override
public void clear() {
this.rootEntry = null;
this.size = 0;
}
/**
* 返回該Map中的鍵的set集合
*/
@Override
public Set<K> keySet() {
// TODO Auto-generated method stub
return null;
}
/**
* 返回該Map中的值的集合
*/
@Override
public Collection<V> values() {
List<Entry<K, V>> list = new LinkedList<Entry<K, V>>();
this.addEntryToList(this.rootEntry, list);
List<V> values = new LinkedList<V>();
for(Entry<K, V> entry : list){
values.add(entry.getValue());
}
return values;
}
/**
* 從一個節點開始,從最小的節點開始添加到List中
*/
private void addEntryToList(Entry<K, V> entry, List<Entry<K, V>> list){
if(entry.leftEntry != null){
this.addEntryToList(entry.leftEntry, list);
}
list.add(entry);
if(entry.rightEntry != null){
this.addEntryToList(entry.rightEntry, list);
}
}
/**
* 返回鍵值關係的映射視圖
*/
@Override
public Set<Map.Entry<K, V>> entrySet() {
// TODO Auto-generated method stub
return null;
}
/**
* 根據key的值來獲取value
*/
@Override
public V get(Object key) {
return getEntry(key).getValue();
}
/**
* 根據key的值來移除一個節點
*/
@Override
public V remove(Object key) {
return null;
}
/**
* 通過Key來獲取對應的Entry鍵值對
*/
private Entry<K, V> getEntry(Object key){
Entry<K, V> entry = null;
for(entry = this.rootEntry ;entry == null || entry.getKey().hashCode() != key.hashCode() ; ){
if(entry.getKey().hashCode() > key.hashCode()){
entry = entry.leftEntry;
}else{
entry = entry.rightEntry;
}
}
return entry;
}
/**
* 對節點進行左旋
*/
private Entry<K, V> leftRotate(Entry<K, V> entry){
Entry<K, V> superEntry = entry.superEntry;
Entry<K, V> rightEntry = entry.rightEntry;
if(superEntry == null){
this.rootEntry = rightEntry;
}else if(superEntry.rightEntry == entry){
superEntry.rightEntry = rightEntry;
}else{
superEntry.leftEntry = rightEntry;
}
entry.rightEntry = rightEntry.leftEntry;
entry.superEntry = rightEntry;
rightEntry.leftEntry = entry;
rightEntry.superEntry = superEntry;
if(entry.rightEntry == null){
if(rightEntry.rightEntry == null){
entry.balance += 1;
}else{
entry.balance += 2;
}
}else{
entry.balance += 1;
}
rightEntry.balance += 1;
this.updateDeepness(entry);
this.adjustLoadFactor(rightEntry);
this.checkBalance(rightEntry);
return rightEntry;
}
/**
* 對接點進行右旋
*/
private Entry<K, V> rightRotate(Entry<K, V> entry){
Entry<K, V> superEntry = entry.superEntry;
Entry<K, V> leftEntry = entry.leftEntry;
if(superEntry == null){
this.rootEntry = leftEntry;
}if(superEntry.leftEntry == entry){
superEntry.leftEntry = leftEntry;
}else{
superEntry.rightEntry = leftEntry;
}
entry.leftEntry = leftEntry.rightEntry;
entry.superEntry = leftEntry;
leftEntry.rightEntry = entry;
leftEntry.superEntry = superEntry;
if(entry.leftEntry == null){
if(leftEntry.leftEntry == null){
entry.balance -= 1;
}else{
entry.balance -= 2;
}
}else{
entry.balance -= 1;
}
leftEntry.balance -= 1;
this.updateDeepness(entry);
this.adjustLoadFactor(leftEntry);
this.checkBalance(leftEntry);
return leftEntry;
}
/**
* 對一個節點的所有子節點進行深度修改操作
*/
private void updateDeepness(Entry<K, V> entry){
for(; entry != null ; entry = entry.superEntry){
int left = entry.leftEntry == null ? 0 : entry.leftEntry.deepness;
int right = entry.rightEntry == null ? 0 : entry.rightEntry.deepness;
entry.deepness = (left > right ? left : right )+1;
}
}
/**
* 調整一個節點的所有父節點及其自己的加載因子數
* @param entry
*/
private void adjustLoadFactor(Entry<K, V> entry){
for(;entry != null; entry = entry.superEntry){
if(entry.leftEntry != null && entry.rightEntry != null){
entry.balance = entry.leftEntry.deepness - entry.rightEntry.deepness;
}else if(entry.leftEntry == null && entry.rightEntry != null){
entry.balance = 0 - entry.rightEntry.deepness;
}else if(entry.leftEntry != null && entry.rightEntry == null){
entry.balance = entry.leftEntry.deepness - 0;
}else if(entry.leftEntry == null && entry.rightEntry == null){
entry.balance = 0;
}
}
}
/**
* 驗證一個節點的所有父節點是否是平衡的,如果不是平衡的則進行旋轉調整
* @param entry
*/
private void checkBalance(Entry<K, V> entry){
for(;entry != null; entry = entry.superEntry){
if(entry.balance > 1){
if(entry.leftEntry.balance > 0){
this.rightRotate(entry);
}else if(entry.leftEntry.balance < 0){
this.leftRotate(entry.leftEntry);
}
break ;
}else if(entry.balance < -1){
if(entry.rightEntry.balance > 0){
this.rightRotate(entry.rightEntry);
}else if(entry.rightEntry.balance < 0){
this.leftRotate(entry);
}
break ;
}
}
}
class Entry<K, V> implements Map.Entry<K, V>{
/**
* 該節點的父節點,可以爲空
*/
public Entry<K, V> superEntry;
/**
* 節點的key值
*/
public K key;
/**
* 節點的Value
*/
public V value;
/**
* 平衡因子
*/
public int balance;
/**
* 樹節點深度
*/
public int deepness;
/**
* 樹節點所掛載的左側節點對象
*/
public Entry<K, V> leftEntry;
/**
* 樹節點所掛載的右側節點對象
*/
public Entry<K, V> rightEntry;
public Entry(K key, V value){
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return this.key;
}
@Override
public V getValue() {
return this.value;
}
@Override
public V setValue(V value) {
this.value = value;
return value;
}
}
}
在這段個類中,首先我們實現了Map接口,同時在內部使用一個內部類Entry類,Entry類同樣也實現了Map.Entry接口。代碼中並沒有將所有的方法都進行實現,目前只是實現了主要方法。
這段代碼中的核心代碼即爲put(key,value)方法,我們對這個方法進行分析:
/**
* 向平衡二叉樹中添加一個鍵值對
*/
@Override
public V put(K key, V value) {
if(this.rootEntry == null){
this.rootEntry = new Entry<K, V>(key, value);
this.rootEntry.balance = 0;
this.rootEntry.deepness = 1;
this.size ++ ;
return value;
}
Entry<K, V> newEntry = new Entry<K, V>(key,value);
Entry<K, V> superEntry = rootEntry;
while(true){
if(key.hashCode() > superEntry.getKey().hashCode()){
if(superEntry.rightEntry != null){
superEntry = superEntry.rightEntry;
}else{
superEntry.rightEntry = newEntry;
newEntry.superEntry = superEntry;
break ;
}
}else if(key.hashCode() < superEntry.getKey().hashCode()){
if(superEntry.leftEntry != null){
superEntry = superEntry.leftEntry;
}else{
superEntry.leftEntry = newEntry;
newEntry.superEntry = superEntry;
break ;
}
}else{
superEntry.setValue(value);
return value;
}
}
this.checkDeepness(newEntry);
this.adjustLoadFactor(superEntry);
this.checkBalance(newEntry);
this.size ++ ;
return value;
}
1.判斷的是這個類的對象是否已經具有了一個根節點對象,如果沒有,則將這個K-V鍵值對存放到 根節點中,這個節點的初始化平衡因子是0,深度爲1。
2.如果已經具有了一個根節點對象,則創建一個新的Entry對象,然後根據所傳入的key去尋找到應該存放該entry對象的位置。如果這個entry對象key是已經存在的,則將value的值替換爲新的value值,並結束put方法,否則,運行下面的驗證方法。
3.下方代碼塊中的三個方法的作用分別是修正一個節點的所有父節點的深度,修正加載因子,檢查樹的加載因子是否處於平衡狀態。這個順序是不能進行修改的,因爲我們需要先修正深度以後,才能根據深度修正加載因子,在最後根據加載因子來檢查樹是否是平衡的。
this.checkDeepness(newEntry);
this.adjustLoadFactor(superEntry);
this.checkBalance(newEntry);
4.這裏我們重點看一下檢查樹的平衡:
/**
* 驗證一個節點的所有父節點是否是平衡的,如果不是平衡的則進行旋轉調整
* @param entry
*/
private void checkBalance(Entry<K, V> entry){
for(;entry != null; entry = entry.superEntry){
if(entry.balance > 1){
if(entry.leftEntry.balance > 0){
this.rightRotate(entry);
}else if(entry.leftEntry.balance < 0){
this.leftRotate(entry.leftEntry);
}
break ;
}else if(entry.balance < -1){
if(entry.rightEntry.balance > 0){
this.rightRotate(entry.rightEntry);
}else if(entry.rightEntry.balance < 0){
this.leftRotate(entry);
}
break ;
}
}
}
檢查樹的平衡時,與其他地方致都是向上進行遍歷父節點,直到不存在父節點爲止。當發現一個節點的平衡因子值 > 1時,這個節點則是一個左傾的節點,此時就需要對該節點的左側節點進行判斷,是否大於0,即是左傾還是右傾。如果左傾,則直接對原節點進行右旋即可,否則需要對原節點的左節點進行左旋操作。當原節點的平衡因子<1時,操作相反。
5.我們再來看一下樹的左右旋操作:
/**
* 對節點進行左旋
*/
private Entry<K, V> leftRotate(Entry<K, V> entry){
Entry<K, V> superEntry = entry.superEntry;
Entry<K, V> rightEntry = entry.rightEntry;
if(superEntry == null){
this.rootEntry = rightEntry;
}else if(superEntry.rightEntry == entry){
superEntry.rightEntry = rightEntry;
}else{
superEntry.leftEntry = rightEntry;
}
entry.rightEntry = rightEntry.leftEntry;
entry.superEntry = rightEntry;
rightEntry.leftEntry = entry;
rightEntry.superEntry = superEntry;
if(entry.rightEntry == null){
if(rightEntry.rightEntry == null){
entry.balance += 1;
}else{
entry.balance += 2;
}
}else{
entry.balance += 1;
}
rightEntry.balance += 1;
this.updateDeepness(entry);
this.adjustLoadFactor(rightEntry);
this.checkBalance(rightEntry);
return rightEntry;
}
在對節點進行左右旋操作時, 要先對接點的引用進行修改,在修改完節點的引用後,爲修改進行旋轉操作的節點的平衡因子,由規律可得: if(entry.rightEntry == null){
if(rightEntry.rightEntry == null){
entry.balance += 1;
}else{
entry.balance += 2;
}
}else{
entry.balance += 1;
}
此時,我們在完成了旋轉操作以後,還需要對這個節點的深度,平衡因子,平衡性進行再次的修改。
到此時,我們已經完成了對一個平衡二叉樹的put操作,對該二叉樹的遍歷則使用遞歸的方式進行,詳情可查看values方法。
如果這邊博客對大家有所幫助,歡迎點贊,如果存在錯誤,更加歡迎在評論區指出。