二分搜索樹




插入操作:

與根節(jié)點比較相等則覆蓋其值县袱,若小于則與左節(jié)點比較浑娜,若大與則與右節(jié)點比較,若根節(jié)點為空則插入這個位置即可式散。遞歸重復(fù)即可筋遭。

查找操作

與根節(jié)點比較相等,若根節(jié)點為空暴拄,則返回false漓滔,若大于根節(jié)點則與右節(jié)點比較

若小于則與左節(jié)點比較,若等于則返回節(jié)點的值乖篷,以此遞歸即可响驴。

遍歷


深度優(yōu)先遍歷:

中序遍歷出來的是排序好的隊列

后序遍歷是用來刪除節(jié)點用的

廣度優(yōu)先遍歷

以上的算法實現(xiàn)


package bobo.algo;

// 二分搜索樹

// 由于Key需要能夠進行比較,所以需要extends Comparable

public class BST, Value> {

// 樹中的節(jié)點為私有的類, 外界不需要了解二分搜索樹節(jié)點的具體實現(xiàn)

? ? private class Node {

private Key key;

? ? ? ? private Value value;

? ? ? ? private Node left, right;

? ? ? ? public Node(Key key, Value value) {

this.key = key;

? ? ? ? ? ? this.value = value;

? ? ? ? ? ? left = right =null;

? ? ? ? }

}

private Node root;? // 根節(jié)點

? ? private int count;? // 樹種的節(jié)點個數(shù)

? ? // 構(gòu)造函數(shù), 默認構(gòu)造一棵空二分搜索樹

? ? public BST() {

root =null;

? ? ? ? count =0;

? ? }

// 返回二分搜索樹的節(jié)點個數(shù)

? ? public int size() {

return count;

? ? }

// 返回二分搜索樹是否為空

? ? public boolean isEmpty() {

return count ==0;

? ? }

// 向二分搜索樹中插入一個新的(key, value)數(shù)據(jù)對

? ? public void insert(Key key, Value value){

root = insert(root, key, value);

? ? }

// 查看二分搜索樹中是否存在鍵key

? ? public boolean contain(Key key){

return contain(root, key);

? ? }

// 在二分搜索樹中搜索鍵key所對應(yīng)的值撕蔼。如果這個值不存在, 則返回null

? ? public Value search(Key key){

return search( root, key );

? ? }

// 二分搜索樹的前序遍歷

? ? public void preOrder(){

preOrder(root);

? ? }

// 二分搜索樹的中序遍歷

? ? public void inOrder(){

inOrder(root);

? ? }

// 二分搜索樹的后序遍歷

? ? public void postOrder(){

postOrder(root);

? ? }

//********************

? ? //* 二分搜索樹的輔助函數(shù)

? ? //********************

? ? // 向以node為根的二分搜索樹中, 插入節(jié)點(key, value), 使用遞歸算法

? ? // 返回插入新節(jié)點后的二分搜索樹的根

? ? private Node insert(Node node, Key key, Value value){

if( node ==null ){

count ++;

? ? ? ? ? ? return new Node(key, value);

? ? ? ? }

if( key.compareTo(node.key) ==0 )

node.value = value;

? ? ? ? else if( key.compareTo(node.key) <0 )

node.left = insert( node.left, key, value);

? ? ? ? else? ? // key > node->key

? ? ? ? ? ? node.right = insert( node.right, key, value);

? ? ? ? return node;

? ? }

// 查看以node為根的二分搜索樹中是否包含鍵值為key的節(jié)點, 使用遞歸算法

? ? private boolean contain(Node node, Key key){

if( node ==null )

return false;

? ? ? ? if( key.compareTo(node.key) ==0 )

return true;

? ? ? ? else if( key.compareTo(node.key) <0 )

return contain( node.left, key );

? ? ? ? else // key > node->key

? ? ? ? ? ? return contain( node.right, key );

? ? }

// 在以node為根的二分搜索樹中查找key所對應(yīng)的value, 遞歸算法

? ? // 若value不存在, 則返回NULL

? ? private Value search(Node node, Key key){

if( node ==null )

return null;

? ? ? ? if( key.compareTo(node.key) ==0 )

return node.value;

? ? ? ? else if( key.compareTo(node.key) <0 )

return search( node.left, key );

? ? ? ? else // key > node->key

? ? ? ? ? ? return search( node.right, key );

? ? }

// 對以node為根的二叉搜索樹進行前序遍歷, 遞歸算法

? ? private void preOrder(Node node){

if( node !=null ){

System.out.println(node.key);

? ? ? ? ? ? preOrder(node.left);

? ? ? ? ? ? preOrder(node.right);

? ? ? ? }

}

// 對以node為根的二叉搜索樹進行中序遍歷, 遞歸算法

? ? private void inOrder(Node node){

if( node !=null ){

inOrder(node.left);

? ? ? ? ? ? System.out.println(node.key);

? ? ? ? ? ? inOrder(node.right);

? ? ? ? }

}

// 對以node為根的二叉搜索樹進行后序遍歷, 遞歸算法

? ? private void postOrder(Node node){

if( node !=null ){

postOrder(node.left);

? ? ? ? ? ? postOrder(node.right);

? ? ? ? ? ? System.out.println(node.key);

? ? ? ? }

}

// 測試二分搜索樹

? ? public static void main(String[] args) {

int N =1000000;

? ? ? ? // 創(chuàng)建一個數(shù)組豁鲤,包含[0...N)的所有元素

? ? ? ? Integer[] arr =new Integer[N];

? ? ? ? for(int i =0 ; i < N; i ++)

arr[i] =new Integer(i);

? ? ? ? // 打亂數(shù)組順序

? ? ? ? for(int i =0 ; i < N; i ++){

int pos = (int) (Math.random() * (i+1));

? ? ? ? ? ? Integer t = arr[pos];

? ? ? ? ? ? arr[pos] = arr[i];

? ? ? ? ? ? arr[i] = t;

? ? ? ? }

// 由于我們實現(xiàn)的二分搜索樹不是平衡二叉樹秽誊,

? ? ? ? // 所以如果按照順序插入一組數(shù)據(jù),我們的二分搜索樹會退化成為一個鏈表

? ? ? ? // 平衡二叉樹的實現(xiàn)琳骡,我們在這個課程中沒有涉及锅论,

? ? ? ? // 有興趣的同學可以查看資料自學諸如紅黑樹的實現(xiàn)

? ? ? ? // 以后有機會,我會在別的課程里向大家介紹平衡二叉樹的實現(xiàn)的:)

? ? ? ? // 我們測試用的的二分搜索樹的鍵類型為Integer楣号,值類型為String

? ? ? ? // 鍵值的對應(yīng)關(guān)系為每個整型對應(yīng)代表這個整型的字符串

? ? ? ? BST bst =new BST();

? ? ? ? for(int i =0 ; i < N; i ++)

bst.insert(new Integer(arr[i]), Integer.toString(arr[i]));

? ? ? ? // 對[0...2*N)的所有整型測試在二分搜索樹中查找

? ? ? ? // 若i在[0...N)之間最易,則能查找到整型所對應(yīng)的字符串

? ? ? ? // 若i在[N...2*N)之間,則結(jié)果為null

? ? ? ? for(int i =0 ; i <2*N; i ++){

String res = bst.search(new Integer(i));

? ? ? ? ? ? if( i < N )

assert res.equals(Integer.toString(i));

else

? ? ? ? ? ? ? ? assert res ==null;

? ? ? ? }

}

}



廣度優(yōu)先遍歷就是先建造一個隊列(先進先出)

首先將根節(jié)點加入隊列炫狱,移除根節(jié)點(遍歷)的同時加入左右節(jié)點藻懒,遍歷左節(jié)點將左節(jié)點的左右子節(jié)點加入到隊列,再遍歷右節(jié)點同時將右節(jié)點的左右子節(jié)點加入隊列视译,以此類推嬉荆。

// 二分搜索樹的層序遍歷

public void levelOrder(){

// 我們使用LinkedList來作為我們的隊列

? ? Queue q =new LinkedList();

? ? q.add(root);

? ? while( !q.isEmpty() ){

Node node = q.remove();

? ? ? ? System.out.println(node.key);

? ? ? ? if( node.left !=null )

q.add( node.left );

? ? ? ? if( node.right !=null )

q.add( node.right );

? ? }

}

刪除二分搜索樹的最小值和最大值

先找到最小值,最小值一定是最左邊的節(jié)點憎亚,一直遍歷左子樹员寇,最后一個非空即為最小節(jié)點,要防止的是其還有右子節(jié)點第美,如果沒有右子節(jié)點直接刪除即可蝶锋,有的話將其右節(jié)點放在它原來的位置即可。刪除最大節(jié)點要防止其有左節(jié)點什往,若有的話扳缕,將其左節(jié)點放在原來它的位置即可,若無别威,則直接刪除即可躯舔。

// 返回刪除節(jié)點后新的二分搜索樹的根

private Node removeMin(Node node){

if( node.left ==null ){

Node rightNode = node.right;

? ? ? ? node.right =null;

? ? ? ? count --;

? ? ? ? return rightNode;

? ? }

node.left = removeMin(node.left);

? ? return node;

}

// 刪除掉以node為根的二分搜索樹中的最大節(jié)點

// 返回刪除節(jié)點后新的二分搜索樹的根

private Node removeMax(Node node){

if( node.right ==null ){

Node leftNode = node.left;

? ? ? ? node.left =null;

? ? ? ? count --;

? ? ? ? return leftNode;

? ? }

node.right = removeMax(node.right);

? ? return node;

}


刪除隨便一個節(jié)點

找出右子樹中最小的數(shù),替換原來的位置省古。



// 刪除掉以node為根的二分搜索樹中鍵值為key的節(jié)點, 遞歸算法

// 返回刪除節(jié)點后新的二分搜索樹的根

Node remove(Node node, Key key){

if( node ==null )

return null;

? ? if( key.compareTo(node.key) <0 ){

node.left = remove( node.left, key );

? ? ? ? return node;

? ? }

else if( key.compareTo(node.key) >0 ){

node.right = remove( node.right, key );

? ? ? ? return node;

? ? }

else{// key == node->key

? ? ? ? // 待刪除節(jié)點左子樹為空的情況

? ? ? ? if( node.left ==null ){

Node rightNode = node.right;

? ? ? ? ? ? node.right =null;

? ? ? ? ? ? count --;

? ? ? ? ? ? return rightNode;

? ? ? ? }

// 待刪除節(jié)點右子樹為空的情況

? ? ? ? if( node.right ==null ){

Node leftNode = node.left;

? ? ? ? ? ? node.left =null;

? ? ? ? ? ? count--;

? ? ? ? ? ? return leftNode;

? ? ? ? }

// 待刪除節(jié)點左右子樹均不為空的情況

? ? ? ? // 找到比待刪除節(jié)點大的最小節(jié)點, 即待刪除節(jié)點右子樹的最小節(jié)點

? ? ? ? // 用這個節(jié)點頂替待刪除節(jié)點的位置

? ? ? ? Node successor =new Node(minimum(node.right));

? ? ? ? count ++;

? ? ? ? successor.right = removeMin(node.right);

? ? ? ? successor.left = node.left;

? ? ? ? node.left = node.right =null;

? ? ? ? count --;

? ? ? ? return successor;

? ? }

}

二叉搜索樹在極端的情況下如0123456789下變成鏈表粥庄,所以采用平衡樹解決這個問題即可如紅黑樹

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市豺妓,隨后出現(xiàn)的幾起案子惜互,更是在濱河造成了極大的恐慌,老刑警劉巖琳拭,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件训堆,死亡現(xiàn)場離奇詭異,居然都是意外死亡白嘁,警方通過查閱死者的電腦和手機坑鱼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來絮缅,“玉大人鲁沥,你說我怎么就攤上這事呼股。” “怎么了画恰?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵卖怜,是天一觀的道長。 經(jīng)常有香客問我阐枣,道長,這世上最難降的妖魔是什么奄抽? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任蔼两,我火速辦了婚禮,結(jié)果婚禮上逞度,老公的妹妹穿的比我還像新娘额划。我一直安慰自己,他們只是感情好档泽,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布俊戳。 她就那樣靜靜地躺著,像睡著了一般馆匿。 火紅的嫁衣襯著肌膚如雪抑胎。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天渐北,我揣著相機與錄音阿逃,去河邊找鬼。 笑死赃蛛,一個胖子當著我的面吹牛恃锉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播呕臂,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼破托,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了歧蒋?” 一聲冷哼從身側(cè)響起土砂,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎疏尿,沒想到半個月后瘟芝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡褥琐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年锌俱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敌呈。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡贸宏,死狀恐怖造寝,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吭练,我是刑警寧澤诫龙,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站鲫咽,受9級特大地震影響签赃,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜分尸,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一锦聊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧箩绍,春花似錦孔庭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至卑吭,卻和暖如春芽淡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陨簇。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工吐绵, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人河绽。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓己单,卻偏偏與公主長得像,于是被迫代替她去往敵國和親耙饰。 傳聞我的和親對象是個殘疾皇子纹笼,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 如需轉(zhuǎn)載, 請咨詢作者, 并且注明出處.有任何問題, 可以關(guān)注我的微博: coderwhy, 或者添加我的微信: ...
    coderwhy閱讀 8,805評論 12 35
  • 1.HashMap是一個數(shù)組+鏈表/紅黑樹的結(jié)構(gòu),數(shù)組的下標在HashMap中稱為Bucket值苟跪,每個數(shù)組項對應(yīng)的...
    誰在烽煙彼岸閱讀 1,014評論 2 2
  • 二叉樹 定義:二叉樹是最多只有兩個節(jié)點的樹廷痘,二叉樹具有唯一根節(jié)點。 二叉樹的特點:1件已、二叉樹每個節(jié)點最多有兩個孩子...
    habit_learning閱讀 1,015評論 0 0
  • 高中的時候笋额,兩顆小虎牙一笑就能看見,再加我的六齡牙沒有好好保護篷扩,最后不得不拔掉兄猩。醫(yī)生建議最好校正,于是開...
    Midge莉莉閱讀 199評論 0 1
  • 一夜的春雨,還好清晨慢慢停了枢冤。不過還是留下了很多雨的痕跡鸠姨。
    阿正_fz閱讀 234評論 0 0