T樹索引的學習與實現(二)
阿新 • • 發佈:2019-01-22
原始碼:
ttree.h原始碼:
ttree.cpp原始碼:/* * ttree.h: header file * * T樹的結構 * */ #ifndef TTREE #define TTREE #include <QString> #include <QMap> /* * 為了保持空間的利用率,每一個內部節點都需要包含一個最小數目的鍵值 */ enum { ttPageSize = 10, ttminSize = ttPageSize - 2 }; typedef QString ElementKey; typedef QMap<QString, QString>* ElementData; typedef struct TTREENODE //樹節點的結構 { TTREENODE *left; //節點的左子樹指標 TTREENODE *right; //節點的右子樹指標 unsigned short int nItems; //節點中鍵值的數目 ElementKey key[ttPageSize]; //key值陣列 ElementData data[ttPageSize]; //對應資料行的指標 因為需要保證每個內部節點的 //最小數目鍵值,所以用陣列,不用map之類的容器 int balance; //balance(平衡因子),其絕對值不大於1,balance =右子樹高度-左子樹高度; } TTREENODE; class TTree //T樹的結構 { public: TTree(); virtual ~TTree(); public: //向T樹中插入一條資料 void insertRecord(ElementKey key, ElementData data); //刪除T樹中對應的key的資料 void deleteRecord(ElementKey key); //查詢對應key值得資料指標 ElementData findRecord(ElementKey key); //返回T樹中的節點數 int getNodeSize(); //返回T樹的深度 int depth(TTREENODE *pNode) const; //比較兩個key值得大小,1 : key1 > key2, 0 : key1 = key2, -1 : key1 < key2 virtual int keyCompare(ElementKey key1, ElementKey key2); //清除樹節點 void clear(); //如果樹為空返回TRUE, 不為空返回FALSE bool isEmpty() const; //T樹的前序遍歷 void PreOrderTraverse(TTREENODE *pNode) const; private: //從記憶體中為node分配空間 TTREENODE *mallocNode(); //釋放節點佔用的記憶體 void freeNode(TTREENODE *pNode); //遞迴清除每個節點 void _earse(TTREENODE *pNode); //插入的時候需要遞迴遍歷整棵樹,為了方便,額外增加一個方法 *& 表示一個指標的引用 bool _insert(TTREENODE *&pNode, ElementKey key, ElementData data); //內部刪除資料的方法 int _delete(TTREENODE *&pNode, ElementKey key); private: //獲取該節點的平衡因子 int getBalance(TTREENODE *pNode) const; //LL型別旋轉 右旋 TTREENODE *singleRotateLeft(TTREENODE *pNode); //RR型別旋轉 左旋 TTREENODE *singleRotateRight(TTREENODE *pNode); //LR型別旋轉,節點的左子樹先左旋,然後節點右旋 TTREENODE *doubleRotateLeftRight(TTREENODE *pNode); //RL型別旋轉,節點的右子樹先右旋,然後節點左旋 TTREENODE *doubleRotateRightLeft(TTREENODE *pNode); //平衡右子樹分支 int balanceRightBranch(TTREENODE *&pNode); //平衡左子樹分支 int balanceLeftBranch(TTREENODE *&pNode); //平衡右子樹分支:節點隔層呼叫底層的節點的資料,需要另外一種平衡 int balanceRightBranchInterlayer(TTREENODE *&pNode); //平衡左子樹分支:節點隔層呼叫底層的節點的資料,需要另外一種平衡 int balanceLeftBranchInterlayer(TTREENODE *&pNode); public: TTREENODE* root; //T樹的根節點 int nodeSize; //T樹中的節點數 }; #endif // TTREE
/* * ttree.cpp: source file */ #include "ttree.h" #include <math.h> #include <QString> #include <QDebug> TTree::TTree() { root = NULL; nodeSize = 0; // qDebug() << "in TTree()"; } TTree::~TTree() { clear(); root = NULL; nodeSize = 0; } TTREENODE *TTree::mallocNode() { TTREENODE *pNode = new TTREENODE; /* * void *memset(void *s, int ch, size_t n); * 函式解釋:將s中前n個位元組替換為ch並返回s; * memset:作用是在一段記憶體塊中填充某個給定的值,它是對較大的結構體或陣列進行清零操作的一種最快方法。 */ // memset(pNode, 0, sizeof(TTREENODE)); //如果不註釋,會導致程式異常 pNode->nItems = 0; pNode->balance = 0; pNode->balance = 0; pNode->left = pNode->right = NULL; nodeSize ++; return(pNode); } void TTree::freeNode(TTREENODE *pNode) { if(pNode) { delete pNode; pNode = NULL; nodeSize --; } } void TTree::_earse(TTREENODE *pNode) { if(pNode == NULL) { return; } _earse(pNode->left); _earse(pNode->right); freeNode(pNode); } void TTree::clear() { _earse(root); } int TTree::getNodeSize() { return nodeSize; } ElementData TTree::findRecord(ElementKey key) { TTREENODE *pNode = root; while(pNode != NULL) { int n = pNode->nItems; ElementKey minKey = pNode->key[0]; ElementKey maxKey = pNode->key[n > 0 ? n - 1 : 0]; int nDiff1 = keyCompare(key, minKey); int nDiff2 = keyCompare(key, maxKey); if(nDiff1 >= 0 && nDiff2 <= 0) { int l = 0, r = n - 1; //二分查詢演算法 while(l <= r) { int i = (r - l) / 2 + l; ElementKey itemKey = pNode->key[i]; int nDiff = keyCompare(key, itemKey); if(nDiff == 0) { return pNode->data[i]; } else if(nDiff > 0) { l = i + 1; } else { r = i - 1; } } break; } else if(nDiff1 < 0) { pNode = pNode->left; } else if(nDiff2 > 0) { pNode = pNode->right; } } return NULL; } /*int TTree::getBalance(TTREENODE *pNode) const //第三次修改BUG,此處需要遞迴,你個辣雞!!! { int l, r; TTREENODE *p1, *p2; l = r = 0; p1 = p2 = pNode; if(p1 != NULL) { while(p1->left != NULL) { p1 = p1->left; l++; } } if(p2 != NULL) { while(p2->right != NULL) { p2 = p2->right; r++; } } qDebug() << "key: " << pNode->key[0] << " l : " << l << " r : " <<r; return (r - l); } */ int TTree::getBalance(TTREENODE *pNode) const { return depth(pNode->right) - depth(pNode->left); } int TTree::depth(TTREENODE *pNode) const { if(pNode == NULL) { return 0; } return depth(pNode->left) > depth(pNode->right) ? depth(pNode->left) + 1 : depth(pNode->right) + 1; } //LL型別的旋轉需要右旋一次,然後返回新的根節點 TTREENODE *TTree::singleRotateLeft(TTREENODE *pNode) { TTREENODE *k = pNode->left; pNode->left = k->right; k->right = pNode; pNode->balance = getBalance(pNode); k->balance = getBalance(k); // qDebug() << "右旋完畢:pNode: " << pNode->key[0] << " pNode->bf: " << pNode->balance; // qDebug() << "右旋完畢:k: " << k->key[0] << " k->bf: " << k->balance; return k; } //RR型別的旋轉需要左旋一次,然後返回新的根節點 TTREENODE *TTree::singleRotateRight(TTREENODE *pNode) { TTREENODE *k = pNode->right; pNode->right = k->left; k->left = pNode; pNode->balance = getBalance(pNode); k->balance = getBalance(k); return k; } //LR型別的旋轉需要根節點的左子樹先左旋一次,變成LL型別,然後根節點再右旋一次,然後返回新的根節點 TTREENODE *TTree::doubleRotateLeftRight(TTREENODE *pNode) { pNode->left = singleRotateRight(pNode->left); pNode->balance = getBalance(pNode); return singleRotateLeft(pNode); } //RL型別的旋轉需要根節點的右子樹先右旋一次,變成RR型別,然後根節點再左旋一次,返回根節點 TTREENODE *TTree::doubleRotateRightLeft(TTREENODE *pNode) { pNode->right = singleRotateLeft(pNode->right); pNode->balance = getBalance(pNode); // leverlOrderTraverse(root); return singleRotateRight(pNode); } //插入一條資料記錄的時候需要先判斷樹中是否有節點,沒有節點需要建立節點插入,如果有 //節點,需要遍歷T樹查詢相應的位置插入 void TTree::insertRecord(ElementKey key, ElementData data) { // qDebug() << "in insertRecord(), key = " << key << " data = " << data; if(root == NULL) { root = mallocNode(); root->balance = 0; root->key[0] = key; root->data[0] = data; // qDebug()<< "i got here "; // qDebug() << "ROOT " << root->key[0] << root->data[0]; root->nItems = 1; root->left = NULL; root->right = NULL; } else { // qDebug() << "ROOT is not NULL"; TTREENODE *pNode = root; bool bRet = _insert(pNode, key, data); /* * 插入資料可能導致插入節點,導致T樹不平衡開始旋轉,導致root改變 */ if(pNode != root) { root = pNode; } } } /* * 在一個節點中插入資料的時候: * (1)比最小的key值小 * (2)比最大的key值大 * (3)處於這個節點的key值範圍內 */ bool TTree::_insert(TTREENODE *&pNode, ElementKey key, ElementData data) { int n = pNode->nItems; ElementKey minKey = pNode->key[0]; ElementKey maxKey = pNode->key[n > 0 ? n-1 : 0]; int nDiff = keyCompare(key, minKey); /* * 如果key值小於這個節點中最小的key值 * (1)如果這個節點中儲存的值個數小於pageSize(節點儲存的最大個數)&&(沒有左子樹||key等於節點中的最小值) * 把節點中的陣列向後移一位,把當前key插入到最前面 * (2)如果不滿足(1),也就是當前節點儲存滿了,並且沒有左子樹,需要新增新的節點 * (3)如果(1)(2)都不滿足,說明需要向子節點中插入,需要遞迴,一直到插入為止 * 當插入新的節點的時候,需要重新計算平衡因子,看是否需要旋轉 */ if(nDiff == 0) //因為每次只返回一條資料,所以如果key值相同,說明是同一條資料,就不能再次插入 { return false; } if(nDiff < 0) { TTREENODE *pLeftId = pNode->left; /* * 疑惑!!!!!!!! * 如果key值相同怎麼還插入了,雖然返回false * 查詢的時候findRecord只返回一條資料的地址 */ //if(pLeftId == NULL && pNode->nItems < pageSize) if(pNode->nItems < ttPageSize) //第三次修改BUG { TTREENODE * pleftId = pNode->left; if(pleftId != NULL) { while(pleftId->right) { pleftId = pleftId->right; } } if(pNode->left == NULL || keyCompare(key, pleftId->key[pleftId->nItems-1]) > 0) { for(int i = n; i > 0; i--) { pNode->key[i] = pNode->key[i-1]; pNode->data[i] = pNode->data[i-1]; } pNode->key[0] = key; pNode->data[0] = data; pNode->nItems += 1; return false; } } if(pLeftId == NULL) { // qDebug() << "開闢新的左子節點"; pLeftId = mallocNode(); pLeftId->key[0] = key; pLeftId->data[0] = data; pLeftId->nItems += 1; pNode->left = pLeftId; } else { TTREENODE *pChildId = pLeftId; bool issuccess = _insert(pChildId, key, data); if(pChildId != pLeftId) { pNode->left = pLeftId = pChildId; } if(!issuccess) { return false; } } /* * 由於某些插入導致遞迴查詢插入,下層節點旋轉平衡節點後, * 再重新遞迴上層看是否旋轉,一直到root,完成整棵樹的旋轉 */ if(pNode->balance > 0) { pNode->balance = 0; return false; } else if(pNode->balance == 0) { pNode->balance = -1; return true; //true或false用來控制是否需要遞迴旋轉,當多出一層節點的時候才會導致樹失衡 } //當旋轉完畢,多出的那一層就會剪掉,整棵樹已經平衡,沒必要繼續遞迴檢測 else { if(pLeftId->balance < 0) { pNode = singleRotateLeft(pNode); } else { pNode = doubleRotateLeftRight(pNode); } return false; } } /* * 第二種情況,大於最大的key值,與上面類似 */ nDiff = keyCompare(key, maxKey); // qDebug() << "keyCompare(key, maxKey) : " << key << maxKey << nDiff; if(nDiff == 0) { return false; } if(nDiff > 0) { // qDebug()<<"新插入資料>節點最大key值"; TTREENODE *pRightId = pNode->right; // if(pRightId == NULL && pNode->nItems < pageSize) //當節點中的資料只有一個並且引起旋轉導致節點上升,導師pRightId!=NULL if(pNode->nItems < ttPageSize) { //然而再次插入的時候,隨然當前節點資料量<PageSize,但是也不能插入當前結點 // qDebug() << "當前結點未滿,插入當前節點"; //T樹效率影響點之一,可以動手修改點 TTREENODE * prightId = pNode->right; //第三次修改BUG:成功避免節點因旋轉導致空間利用率低下問題 if(prightId != NULL) { while(prightId->left) { prightId = prightId->left; } } if(pNode->right == NULL || keyCompare(key, prightId->key[0]) < 0) { pNode->key[n] = key; pNode->data[n] = data; pNode->nItems ++; return false; } } if(pRightId == NULL) { // qDebug() << "需要開闢新節點"; pRightId = mallocNode(); pRightId->key[0] = key; pRightId->data[0] = data; pRightId->nItems = 1; pNode->right = pRightId; } else { // qDebug() << "進入下一級節點"; TTREENODE *pChildId = pRightId; bool bGrow = _insert(pChildId, key, data); if(pChildId != pRightId) { pNode->right = pRightId = pChildId; } if(!bGrow) { return false; } } // qDebug() << "pNode->balance : " << pNode->balance; if(pNode->balance < 0) { pNode->balance = 0; return false; } else if(pNode->balance == 0) { pNode->balance = 1; return true; //增加一個新節點,返回true,進入現在程式碼,進行重置節點中的平衡點 } else { // qDebug() << "in rotate"; if(pRightId->balance > 0) { // qDebug() << "in RR: pNode->bf: " << pNode->balance << " pRightId->bf : " << pRightId->balance; pNode = singleRotateRight(pNode); //RR型別, 左旋 // qDebug() << "after RR : " <<pNode->balance; } else if(pRightId->balance < 0) { // PreOrderTraverse(root); // qDebug() << "int RL: pNode->bf: " << pNode->balance << " pRightId->bf : " << pRightId->balance; pNode = doubleRotateRightLeft(pNode); //RL型別,先右旋,再左旋 // qDebug() << "out RL"; } // qDebug() << "out rotate"; return false; } } /* * 第三種情況,key值在節點的key值範圍內 * (1)如果該節點中的資料還沒有滿,只需找到合適的位置插入即可 * (2)如果節點中資料已滿,只需要考慮把節點往前擠掉一個,還是往後擠掉一個 * ((1))如果節點的平衡點>0,為了避免插入可能帶來的增加節點導致旋轉引起 * 效率下降,需要往前擠掉,把最前面的節點重新往左子樹插 * ((2))如果節點的balance<0,同理往後擠 */ // qDebug() << "確定位置為節點中間位置"; int l = 0, r = n -1; while(l < r) //利用二分查詢找到需要插入位置r { int i = (r - l) / 2 + l; ElementKey itemKey = pNode->key[i]; nDiff = keyCompare(key, itemKey); if(nDiff > 0) { l = i + 1; } else { r = i; if(nDiff == 0) { return false; } } } if(n < ttPageSize) { // qDebug() << "節點中資料未滿,直接插入中間合適的位置 r: " << r; for(int i = r; i < n; i++) { pNode->key[i+1] = pNode->key[i]; pNode->data[i+1] = pNode->data[i]; } pNode->key[r] = key; pNode->data[r] = data; pNode->nItems ++; // qDebug() << "插入完畢 nItems: " << pNode->nItems; return false; } else { ElementKey reInsertKey; ElementData reInsertData; if(pNode->balance >= 0) { reInsertKey = pNode->key[0]; reInsertData = pNode->data[0]; for(int i = 0; i < r - 1; i++) { pNode->key[i] = pNode->key[i+1]; pNode->data[i] = pNode->data[i+1]; } pNode->key[r-1] = key; pNode->data[r-1] = data; return _insert(pNode, reInsertKey, reInsertData); } else { reInsertKey = pNode->key[n-1]; reInsertData = pNode->data[n-1]; for(int i = n -1 ; i > r; i--) { pNode->key[i] = pNode->key[i-1]; pNode->data[i] = pNode->data[i-1]; } pNode->key[r] = key; pNode->data[r] = data; return _insert(pNode, reInsertKey, reInsertData); } } } /* * deleteRecord * 對外公開的delete方法 */ void TTree::deleteRecord(ElementKey key) { if(root != NULL) //刪除BUG地方之一,已修改 { TTREENODE *pNode = root; int h = _delete(pNode, key); //assert(h >= 0); if(pNode != root) { root = pNode; } } } /* * 內部刪除的方法 * * 如果key值不再當前結點的key值範圍內,則遞迴呼叫本方法在子節點中刪除key值對應的資料 * 如果key值在範圍內: * (1)如果當前結點的nItems>minSize,則只需要刪除節點中的資料,nItems-1即可 * (2)如果當前結點的nItems=minSize,需要呼叫一個子節點中的資料新增到本節點中。 * 至於呼叫子節點中小於當前結點最小值的最大值,還是大於當前結點的最大值的最小值 * 需要看當前結點的balance,儘可能避免刪除帶來的旋轉 * (3)因為子節點的資料需要向上補充,所以有的子節點中的資料可能小於minSize,當節點中 * 的資料=1,並且需要刪除的時候,本節點也需要刪除,因此可能導致T樹不平衡,導致旋轉 * * T樹與AVL樹刪除的不同的是,由於再刪除中間節點的時候,子節點中的資料需要向上補充,所以 * 刪除的一直是子節點。 * * return -1:沒有刪除成功 1:刪除一個節點 0:沒有刪除節點,但刪除資料成功 */ int TTree::_delete(TTREENODE *&pNode, ElementKey key) { int n = pNode->nItems; ElementKey minKey = pNode->key[0]; ElementKey maxKey = pNode->key[n > 0 ? n - 1 : 0]; int nDiff = keyCompare(key, minKey); // qDebug() << "keyCompare(key, minKey); key:" << key << " minKey:" << minKey << " nDiff:" << nDiff; if(nDiff < 0) { TTREENODE *pLeftId = pNode->left; if(pLeftId != 0) { TTREENODE *pChildId = pLeftId; // qDebug() << "!!!!!!!!!!!!!!!!!!!!!!!!!!!! pChildId: " << pChildId << " pLeftId: " << pLeftId; int h = _delete(pChildId, key); //h表示刪除的節點數,不是資料數 // qDebug() << "pChildId: " << pChildId; if(pChildId != pLeftId) { pNode->left = pChildId; } if(h > 0) { return balanceLeftBranch(pNode); //h>0 表示有刪除的節點,需要平衡 } else if(h == 0) { return 0; } } return -1; } nDiff = keyCompare(key, maxKey); if(nDiff > 0) { TTREENODE *pRightId = pNode->right; if(pRightId != 0) { TTREENODE *pChildId = pRightId; int h = _delete(pChildId, key); if(pChildId != pRightId) { pNode->right = pChildId; } if(h > 0) { return balanceRightBranch(pNode); } else if(h == 0) { return 0; } } return -1; } for(int i = 0; i < n; i++) { if(keyCompare(pNode->key[i],key) == 0) { if(n == 1 && pNode->left == NULL && pNode->right == NULL) //刪除的一直是子節點 { freeNode(pNode); pNode = NULL; return 1; } TTREENODE *pLeftId = pNode->left, *pRightId = pNode->right; TTREENODE *pre = pNode; if(n <= ttminSize) { if(pLeftId != NULL && pNode->balance <= 0) //在調入子樹節點資料的時候,儘量避免失衡,所以判斷是從左子樹呼叫還是從右子樹 { bool isInterlayer = false; while(pLeftId->right != NULL) { isInterlayer = true; pre = pLeftId; pLeftId = pLeftId->right; } while(--i >= 0) //刪除資料,從左子樹中調入最大資料 { pNode->key[i+1] = pNode->key[i]; pNode->data[i+1] = pNode->data[i]; } pNode->key[0] = pLeftId->key[pLeftId->nItems - 1]; pNode->data[0] = pLeftId->data[pLeftId->nItems - 1]; TTREENODE *pChildId = pLeftId; int h = _delete(pChildId, pNode->key[0]); // qDebug() << "pChildId : " << pChildId; if(pChildId != pLeftId) { if(pre == pNode) { pre->left = pLeftId = pChildId; } else { pre->right = pLeftId = pChildId; //警告,資料能報錯點 } } if(h > 0) { if(isInterlayer) { h = balanceRightBranchInterlayer(pNode->left); if(h > 0) { h = balanceLeftBranch(pNode); } } else { h = balanceLeftBranch(pNode); } } // qDebug() << "h: " << h; return h; } else if(pNode->right != NULL) { // qDebug() << "in pNode->right"; bool isInterlayer = false; while(pRightId->left != NULL) { isInterlayer = true; pre = pRightId; pRightId = pRightId->left; } while(++i < n) { pNode->key[i-1] = pNode->key[i]; pNode->data[i-1] = pNode->data[i]; } pNode->key[n-1] = pRightId->key[0]; pNode->data[n-1] = pRightId->data[0]; TTREENODE *pChildId = pRightId; int h = _delete(pChildId, pNode->key[n-1]); // qDebug() << "pChildId : " << pChildId << " pRightId:" <<pRightId ; if(pChildId != pRightId) { if(pre == pNode) { pre->right = pLeftId = pChildId; } else { pre->left = pLeftId = pChildId; } } // qDebug() << "pChildId : " << pChildId << " pRightId:" <<pRightId << pNode->right->left ; if(h > 0) { // qDebug() << "是時候起飛旋轉了"; if(isInterlayer) { // qDebug() << "開始in balanceLBI"; h = balanceLeftBranchInterlayer(pNode->right); // qDebug() << "h: " << h; if(h > 0) { h = balanceRightBranch(pNode); } } else { h = balanceRightBranch(pNode); //沒有隔層呼叫子節點中的資料 } } // qDebug() << "h: " << h; return h; } } while(++i < n) { pNode->key[i-1] = pNode->key[i]; pNode->data[i-1] = pNode->data[i]; } pNode->nItems -= 1; return 0; } } return -1; } /* * 平衡左子樹 * 由於呼叫直接子節點資料,導致子節點刪除的情況 * */ int TTree::balanceLeftBranch(TTREENODE *&pNode) { if(pNode->balance < 0) { pNode->balance = 0; //至於返不返回1,還需要看是不是(祖祖)祖父節點呼叫最下層節點的情況,(決定重新寫一個這種情況下的平衡方法) return 1; //h>=1,證明子節點一層全部刪除,pNode左子樹少一層,可能導致旋轉,返回1 } else if(pNode->balance == 0) { pNode->balance = 1; return 0; //刪除了pNode的左子樹,但是還有右子樹支撐平衡 } else { TTREENODE *pRightId = pNode->right; int prightbf = pRightId->balance; if(prightbf >= 0) // { pNode = singleRotateRight(pNode); if(prightbf == 0) //第二次尋BUG:用prightbf代替pRightId->balance,因為是指標,在旋轉後,balance變化,導致前後不一致 { pNode->balance = -1; pNode->left->balance = 1; return 0; } else { pNode->balance = 0; pNode->left->balance = 0; return 1; //旋轉平衡導致pNode的深度-1,可能對上層造成影響 } } else { pNode = doubleRotateRightLeft(pNode); return 1; } } return 0; } /* * 平衡右子樹 * 由於呼叫直接子節點資料,導致子節點刪除的情況 * 由於刪除可能導致旋轉,所以需要判斷,刪除之前樹肯定是平衡的 */ int TTree::balanceRightBranch(TTREENODE *&pNode) { if(pNode->balance > 0) { pNode->balance = 0; return 1; } else if(pNode->balance == 0) { pNode->balance = -1; return 0; } else { TTREENODE *pLeftId = pNode->left; int pleftbf = pLeftId->balance; if(pleftbf <= 0) { pNode = singleRotateLeft(pNode); if(pleftbf == 0) { pNode->balance = 1; pNode->right->balance = -1; return 0; } else { pNode->balance = 0; pNode->right->balance = 0; return 1; } } else { pNode = doubleRotateLeftRight(pNode); return 1; } } return 0; } /* * 平衡右子樹分支 * 由於刪除的另一種情況,節點跨層呼叫底層節點的資料,導致節點刪除,可能需要平衡旋轉 */ int TTree::balanceRightBranchInterlayer(TTREENODE *&pNode) { int h; if(pNode->right == NULL) { if(pNode->left == NULL) //第二次BUG:兩種情況,這種是雖然隔代調入刪除節點,但是還有一層直接調入刪除。見亂畫本 { return 1; } h = balanceRightBranch(pNode); return h; } TTREENODE *tempNode = pNode->right; if((h = balanceRightBranchInterlayer(tempNode)) > 0) { h = balanceRightBranch(pNode); } else { return 0; } return h; } /* * 平衡左子樹分支 * 由於刪除的另一種情況,節點跨層呼叫底層節點的資料,導致節點刪除,可能需要平衡旋轉 * 需要遞迴從最底層開始檢視是否需要旋轉,如果最底層高度不變,則對整棵樹的平衡不會造成影響 */ int TTree::balanceLeftBranchInterlayer(TTREENODE *&pNode) { int h; //qDebug() << "pNode->left: " << pNode->left; if(pNode->left == NULL) { // qDebug() << "最底層,開始balanceLeftBranch"; if(pNode->right == NULL) //第二次修改:兩種情況,這種是雖然隔代刪除節點,但是還有一層刪除。見本 { return 1; } h = balanceLeftBranch(pNode); return h; } TTREENODE *tempNode = pNode->left; if((h = balanceLeftBranchInterlayer(tempNode)) > 0) { h = balanceLeftBranch(pNode); //第二次修改 } else { return 0; } return h; } bool TTree::isEmpty() const { return root == NULL; } int TTree::keyCompare(ElementKey key1, ElementKey key2) { if(key1.size() < key2.size()) { return -1; } else if(key1.size() > key2.size()) { return 1; } else{ return QString::compare(key1, key2, Qt::CaseSensitive); } } void TTree::PreOrderTraverse(TTREENODE *pNode) const { if (pNode != NULL) { int nSize = pNode->nItems; qDebug()<<"bf: " << pNode->balance << " nItems: " << pNode->nItems; for (int i = 0; i < nSize; i++) { //printf("%02d ", pNode->item[i]); qDebug() << pNode->key[i] << " "; } qDebug()<<"||"; PreOrderTraverse(pNode->left); PreOrderTraverse(pNode->right); } }