演算法筆記十一 二叉樹之搜尋二叉樹

2021-06-27 14:48:43 字數 3877 閱讀 3672

二叉樹

每個節點,除了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 若右子樹不空,則右子...