翻部落格的時候突然發現,我原來已經有一年多沒有寫過部落格了,懶得不行,最近要開始找工作才發現自己有好多地方欠缺,準備查漏補缺了,多寫部落格激勵自己,不要再懶下去了
很久沒有用過指標,有些生疏了
二叉搜尋樹有下列三個特點:
根據上述的特點,可以構造出乙個包含三個資料物件的結構體:
struct bsttree
;
每個結點都是乙個bsttree型別
接下來我們考慮怎麼建樹:(都以動態建樹為例)
建樹的過程實際上就是往樹上挨個加結點
以下列的樹為例:
如果要往樹上新增乙個值為13的結點:
首先將13跟根結點比較
結點往10的右子樹走遇到結點18,並與之比較
結點再往18的左子樹走,發現18的左子樹為空
到這裡乙個結點就已經插入到了樹上
不斷的重複這個過程,直到把所有的點都加到樹上,建樹完畢
如果建樹的時候沒有初始值,可以先令根的值為0,當有值輸入的時候再去修改根的值
對root動態分配空間後,判斷是否為空是為了看記憶體是否已分配完,如果為空的話就不能進行後面的操作了
bsttree*
build_tree()
root -> val =0;
root -> leftsubtree =
null
; root -> rightsubtree =
null
;return root;
}
如果我們已有輸入,建一棵有n個數值的bst,首先,可以將輸入全放入乙個動態分配長度為n的陣列a中,以a[0]為樹根,再遍歷陣列建樹,如果當前的值大於根就將它歸到根的右子樹區間去,如果小於根的值將它歸到根的左子樹區間去,然後再與右子樹或左子樹的根的值做比較,一直比較到當前樹的某個結點node,該結點左子樹或右子樹為空,如果當前的值》node -> val就插入乙個結點到node作為右子樹,反之作為左子樹插入,即每一次遍歷為當前建的bst新增乙個葉子結點
bst建樹的非遞迴實現:
//非遞迴實現
bsttree*
build_tree
(int n)
int* a =
(int*)
malloc
(n *
sizeof
(int))
;for
(int i =
0; i < n; i++
) root -> val = a[0]
; root -> leftsubtree =
null
; root -> rightsubtree =
null
; bsttree* current;
for(
int i =
1; i < n; i++
)else current = current -> rightsubtree;
}else
current = current -> leftsubtree;}}
/*錯誤寫法
// while (current == null)
// else
// }
// current = node;
*/}free
(a);
return root;
}
注釋的部分是一開始寫錯了的,由於current是臨時變數,當current為null時,把node賦值給current沒有意義,因為樹中的每個結點都是bsttree型的,而current也是bsttree型的,當current的值為null後,current就相當於乙個沒有連在樹上的孤立結點,把node賦值給current,就是把動態生成的乙個有值的孤立結點再賦給乙個沒有值的孤立結點,node並沒有連在樹上
bst樹的遞迴實現:
void
build_tree
(bsttree*
&root,
int val)
root -> val = val;
root -> leftsubtree =
null
; root -> rightsubtree =
null
;return;}
if(val > root -> val)
else
}
由於對於一棵bst的每個結點,它的左右子樹分別是一棵bst,它的相同子結構讓我們可以用遞迴來實現建樹遞迴要考慮的問題就是結束條件,以及要怎樣遞迴呼叫自身,這裡主要需要解決的問題就是結束條件,是遍歷到結點是空的時候就賦值返回嗎?如果是這樣的話,就會遇到上面提到的一樣的問題。
那要怎麼判斷呢?
用指標就好了,乙個指向結點bsttree*型別的指標,如果該指標為空,就把結點放上去
如果我們希望能夠列印出乙個有序的陣列,就可以用到bst的中序遍歷
中序遍歷的順序:左子樹 根 右子樹
模擬下面這棵樹的列印過程:
從根開始,由於根結點不等於null,我們將根結點壓入棧中,,然後遍歷10的左子樹,訪問5,結點5也不為空,壓入棧中,然後在遍歷5的左子樹,訪問4,結點4也不為空,壓入棧中,再訪問4的左子樹,發現為空了,結點4可以出棧並列印,再訪問4的右子樹,為空,返回,結點5出棧,並列印,訪問結點5的右子樹,一直這樣下去,直到整棵樹訪問完畢
由於bst左子樹都小於根,右子樹都大於根的特性,每次最先列印出來的都是當前的最小值
void
print
(bsttree* root)
print
(root -> leftsubtree)
;printf
("%d "
, root -> val)
;print
(root -> rightsubtree)
;}
前序遍歷和後序遍歷同理,只是根列印的順序有所不同
在bst中插入某個數,跟建樹的過程一樣
void
insert
(bsttree*
&root,
int val)
root -> val = val;
root -> leftsubtree =
null
; root -> rightsubtree =
null
;return;}
if(val > root -> val)
else
}
由於我們是動態建樹,在不需要這棵樹之後就需要考慮怎麼釋放分配出去的空間了
void
destroy_tree
(bsttree* root)
destroy_tree
(root -> leftsubtree)
;destroy_tree
(root -> rightsubtree)
;free
(root)
;}
要先free掉左右子樹再free掉根,這樣的順序跟後序遍歷一樣 二叉搜尋樹BST
在二叉搜尋樹b中查詢x的過程為 1.若b是空樹,則搜尋失敗,否則 2.若x等於b的根結點的資料域之值,則查詢成功 否則 3.若x小於b的根結點的資料域之值,則搜尋左子樹 否則 4.查詢右子樹 指標parent指向proot的父節點,其初始呼叫值為null 若查詢成功,指標ptarget指向目標節點,...
二叉搜尋樹(BST)
二叉搜尋樹 bst bst 或者是一棵空樹,或者對於任何乙個結點,設其值為k,則該結點的左子樹的值小於k,右結點的值大於k。二叉搜尋樹按照中根遍歷將各個結點列印,將得到按照大到小的順序排列。bsg示意圖 二叉搜尋樹的效率在於檢索,將演算法複雜度從2 k減少到log n 檢索方式 從根結點開始,如果等...
BST二叉搜尋樹
深入學習理解bst include using namespace std typedef struct bitnodebitnode,bitree 二叉樹的插入操作 void insert bitnode root,int x 因為當對空樹插入時,相當於改變了樹的根節點的指向,因此需要用到指標或引...