二叉樹:
每個節點,除了key之外,還有3個屬性:父親、左孩子、右孩子,其中,父親一定大於左孩子,一定小於(等於)右孩子
搜尋二叉樹:
利用二叉樹的這些性質,我們可以得出,在這樣乙個結構下,插入、刪除、查詢的步驟,最多不會超過樹的高度。注意哈,這裡的二叉樹,並不是平衡的,所以,其樹的高度並不等於lgn,所以,最壞情況下,這個樹的插入、查詢、刪除的最差效能,等於樹的節點樹n,跟鍊錶的效能相等
空間上,每個資料,都會多乙個父親、左右孩子這三個資料的指標空間
哨兵節點:
就是定義乙個特殊的節點,這個節點用來表示null節點,其目的是,能夠大大簡化我們**的簡潔度(null不是乙個節點,而哨兵節點是乙個節點,其也有父親、孩子)
插入和查詢操作:
每次插入,都將資料插入到葉子節點上,方法就是:從根節點開始,如果比當前值大,就比較其右孩子,否則,比較其左孩子,直到遇到空節點(衛星節點),那麼這個衛星節點的位置,就是需要插入的位置。這種情況有乙個例外,那就是如果當前節點的值等於待插入的值,這時候就不要再和孩子們比較,而是做統一處理,加入到這個節點的右孩子節點上,查詢操作,思路也一樣的,唯一需要約定的就是,總是返回第乙個被找到的值相等的節點,而不是返回其右孩子節點(如果右孩子節點的值也相等的話)
刪除操作:
刪除操作,分為3種情況:
1、待刪除的節點,沒有左孩子
這種情況,簡單的將右孩子替換掉當前節點的位置,就可以了(不會破壞「父親一定大於左孩子,一定小於(等於)右孩子」的性質)
2、待刪除的節點,沒有右孩子
跟第一種情況一樣,簡單的將左孩子替換掉當前節點的位置就可以了
3、又有左孩子,又有右孩子
這種情況是最複雜的一種,這時需要我們好好想下,我們的目標是什麼:節點刪除後,不破壞二叉樹的性質,即「父親一定大於左孩子,一定小於(等於)右孩子」
由於右孩子可能與當前待刪除節點的值相等,情況更複雜,所以,我們考慮從左孩子中,找出乙個節點,替換掉待刪除節點。
那麼找哪個節點比較合適呢?前驅!原因很簡單,前驅是最接近待刪除節點的那個節點,也就是在左樹中,這個節點的值是最大的,而且這個值一定沒有右孩子(它最大嘛),所以將這個值放到待刪除節點的位置上,一來可以保證其左孩子都比它小(不破壞二叉樹的性質),二來前驅節點的右孩子一定為空,方便直接將其右孩子節點指向待刪除節點的右孩子!
這裡需要注意的是,前驅不一定就是待刪除節點的左孩子哦,簡單的例子:如果左孩子還有乙個右孩子,顯然其前驅是這個右孩子,待刪除節點是前驅的父親的父親
中序遍歷:
先輸出左孩子,在輸出節點本身,再輸出右孩子(節點本身的輸出位置位於左右孩子的中間,這個就叫做中序遍歷,很容易就得出什麼是前序遍歷和後續遍歷)。
由二叉樹的性質:「父親一定大於左孩子,一定小於(等於)右孩子」,得出中序遍歷出來的結果,就是乙個從小到大的已排好序的陣列
先給出節點類的定義,該類在後續都會用到,以後將不會再重複帖**:
//
// treenode.h
// p1
//// created by minerguo on 14/11/25.
//#ifndef __p1__treenode__
#define __p1__treenode__
#include class treenode;
class treenode
};//哨兵,表示空元素
static treenode * treenodenil = new treenode(-1);
#endif /* defined(__p1__treenode__) */
再給出二叉樹的**實現:
//
// searchtree.h
// p1
//二叉搜尋樹
//// created by minerguo on 14/11/24.
//#ifndef __p1__searchtree__
#define __p1__searchtree__
#include #include "treenode.h"
#include "arraygenerater.h"
#include class searchtree
treenode * getroot()
//插入乙個元素
void insert(int key)else if(_item->_key < _current_item->_key)else
}_itemcount++;
_item->_parent = _tmp;
if (_tmp == treenodenil) else if(_tmp ->_key == key)else if (_item->_key > _tmp->_key)else
}//中序遍歷:先遍歷左子節點,再遍歷當前節點,再遍歷右子節點
//這樣遍歷出來的結果,就是乙個有序的陣列
int * readbymiddleorder(int * result,treenode * currentitem)
if(currentitem->_leftchild != treenodenil)
*(result + _readindex) = currentitem->_key;
_readindex++;
if(currentitem->_rightchild != treenodenil)
//釋放資源
delete currentitem;
return result;
}//搜尋
treenode * search(treenode * node,int key)
if(key < node->_key)
return search(node->_rightchild,key);
}//獲取最小值
treenode * getminimun(treenode * node)
return node;
}//獲取最大值
treenode * getmaxmun(treenode * node)
return node;
}//輔助方法:用 x 替換 y,這裡只是將父節點的子節點的指標給改了,並未修改節點x的孩子節點,這個留給呼叫者處理
void trasplant(treenode * root,treenode * x,treenode * y)else if(y == y->_parent->_rightchild)else
if(x != treenodenil)
}//刪除乙個節點
void deletenode(treenode * node)else if (node->_rightchild == treenodenil)else
//這時,我們已經將要替換到待刪除節點位置的節點,給扣出來了,而且因為是前驅,其右孩子位置一定是空著的
//將前驅放到帶刪除的位置上來
trasplant(_root,firstsmallinchild,node);
//將待刪除節點的右孩子,放到前驅的右孩子位置上,替換完成
firstsmallinchild->_rightchild = node->_rightchild;
firstsmallinchild->_rightchild->_parent = firstsmallinchild;}}
//測試刪除元素,以下是測試**,_arraygenerater類的實現,這裡沒給出,讀者自己實現吧,其實就是生成隨機的數而已
void testdelete(int deletecount,int maxnumber)}}
};#endif /* defined(__p1__searchtree__) */
二叉樹 還原二叉樹 二叉搜尋樹
先序遍歷的特點 先遍歷根結點,再遍歷左子樹,最後再遍歷右子樹 中序遍歷的特點 先遍歷左子樹,再遍歷根結點,最後再遍歷右子樹 後序遍歷的特點 先遍歷左子樹,再遍歷右子樹,最後再遍歷根結點 舉例 先序遍歷 a b d f g h i e c 中序遍歷 f d h g i b e a c 如上,根據先序遍...
排序二叉樹or搜尋二叉樹or查詢二叉樹
排序二叉樹,搜尋二叉樹,查詢二叉樹都是乙個意思,只是叫法不同而已。下面的文章中我們統稱為排序二叉樹。本文主要是針對高中資訊學,因此其中不涉及到指標,所有需要用指標的地方都直接使用陣列進行模擬。排序二叉樹定義 1 若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值 2 若右子樹不空,則右子...
排序二叉樹or搜尋二叉樹or查詢二叉樹
排序二叉樹,搜尋二叉樹,查詢二叉樹都是乙個意思,只是叫法不同而已。下面的文章中我們統稱為排序二叉樹。本文主要是針對高中資訊學,因此其中不涉及到指標,所有需要用指標的地方都直接使用陣列進行模擬。排序二叉樹定義 1 若左子樹不空,則左子樹上所有結點的值均小於或等於它的根結點的值 2 若右子樹不空,則右子...