日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

【查找結構 2】二叉查找樹 [BST]

 靜聽沙漏 2012-01-08

當所有的靜態(tài)查找結構添加和刪除一個數(shù)據(jù)的時候,整個結構都需要重建。這對于常常需要在查找過程中動態(tài)改變數(shù)據(jù)而言,是災難性的。因此人們就必須去尋找高效的動態(tài)查找結構,我們在這討論一個非常常用的動態(tài)查找樹——二叉查找樹

二叉查找樹的特點

下面的圖就是兩棵二叉查找樹,我們可以總結一下他的特點:

(1) 若它的左子樹不空,則左子樹上所有結點的值均小于它的根結點的值

(2) 若它的右子樹不空,則右子樹上所有結點的值均大于它的根結點的值
(3) 它的左、右子樹也分別為二叉查找樹

 
 

我們中序遍歷這兩棵樹發(fā)現(xiàn)一個有序的數(shù)據(jù)序列: 【1 2 3 4 5 6 7 8 】

二叉查找樹的操作

插入操作:

現(xiàn)在我們要查找一個數(shù)9,如果不存在則,添加進a圖。我們看看二叉查找樹動態(tài)添加的過程:

1). 數(shù)9和根節(jié)點4比較(9>4),則9放在節(jié)點4的右子樹中。

2). 接著,9和節(jié)點5比較(9>5),則9放在節(jié)點5的右子樹中。

3). 依次類推:直到9和節(jié)點8比較(9>8),則9放在節(jié)點8的右子樹中,成為節(jié)點8的右孩子。

這個過程我們能夠發(fā)現(xiàn),動態(tài)添加任何一個數(shù)據(jù),都會加在原樹結構的葉子節(jié)點上,而不會重新建樹。由此可見,動態(tài)查找結構確實在這方面有巨大的優(yōu)勢。

刪除操作:

如果二叉查找樹中需要刪除的結點左、右子樹都存在,則刪除的時候需要改變一些子樹結構,但所需要付出的代價很小。

具體的插入,刪除算法請參加《數(shù)據(jù)結構算法與應用——搜索樹》P5-8。[該章節(jié)已經(jīng)上傳到《查找結構專題(6):動態(tài)查找樹比較》中]。

二叉查找樹的效率分析

那么我們再來看看二叉查找樹的效率問題

很顯然,在a,b兩圖的二叉查找樹結構中查找一個數(shù)據(jù),并不需要遍歷全部的節(jié)點元素,查找效率確實提高了。但是有一個很嚴重的問題:我們在a圖中查找8需要比較5次數(shù)據(jù),而在B圖中只需要比較3次。更為嚴重的是:如果按有序序列[1 2 3 4 5 6 7 8]建立一顆二叉查找樹,整棵樹就退化成了一個線性結構(如c輸入圖:單支樹),此時查找8需要比較8次數(shù)據(jù),和順序查找沒有什么不同。

總結一下:最壞情況下,構成的二叉排序樹蛻變?yōu)閱沃?,樹的深度為n,其查找時間復雜度與順序查找一樣O(N)。最好的情況是二叉排序樹的形態(tài)和折半查找的判定樹相同,其平均查找長度和log2(N)成正比 (O(log2(n)))。

這說明:同樣一組數(shù)據(jù)集合,不同的添加順序會導致查找樹的結構完全不一樣,直接影響了查找效率。

那么如何解決這個問題呢? 我們會在下面的專題中:《平衡二叉樹》中來解決。

下面是java實現(xiàn)的二叉查找樹(說句老實話,沒有指針的java實現(xiàn)數(shù)據(jù)結構真是復雜):

Java代碼
  1. package net.hr.algorithm.search;
  2. import java.util.ArrayList;
  3. /**
  4. * 二叉樹節(jié)點結構
  5. * @author heartraid
  6. */
  7. class BSTNode<E extends Comparable<E>>{
  8. /**結點關鍵字*/
  9. E key=null;
  10. /**直接父親結點*/
  11. BSTNode<E> parent=null;
  12. /**結點左子樹的根節(jié)點*/
  13. BSTNode<E> lchild=null;
  14. /**結點右子樹的根節(jié)點*/
  15. BSTNode<E> rchild=null;
  16. BSTNode(E k){
  17. this.key=k;
  18. }
  19. }
  20. /**
  21. * 二叉查找樹 Binary Search Tree(BST)
  22. * @author heartraid
  23. *
  24. */
  25. public class BST<E extends Comparable<E>> {
  26. /**樹根*/
  27. private BSTNode<E> root=null;
  28. public BST(){
  29. }
  30. /**
  31. * BST 查詢關鍵字
  32. * @param key 關鍵字
  33. * @return 查詢成功/true, 查詢失敗/false
  34. */
  35. public boolean search(E key){
  36. System.out.print("搜索關鍵字["+key+"]:");
  37. if(key==null||root==null){
  38. System.out.println("搜索失敗");
  39. return false;
  40. }
  41. else{
  42. System.out.print("搜索路徑[");
  43. if(searchBST(root,key)==null){
  44. return false;
  45. }
  46. else return true;
  47. }
  48. }
  49. /**
  50. * BST插入關鍵字
  51. * @param key 關鍵字
  52. * @return 插入成功/true, 插入失敗/false
  53. */
  54. public boolean insert(E key){
  55. System.out.print("插入關鍵字["+key+"]:");
  56. if(key==null) return false;
  57. if(root==null){
  58. System.out.println("插入到樹根。");
  59. root=new BSTNode<E>(key);
  60. return true;
  61. }
  62. else{
  63. System.out.print("搜索路徑[");
  64. return insertBST(root,key);
  65. }
  66. }
  67. public boolean delete(E key){
  68. System.out.print("刪除關鍵字["+key+"]:");
  69. if(key==null||root==null){
  70. System.out.println("刪除失敗");
  71. return false;
  72. }
  73. else{
  74. System.out.print("搜索路徑[");
  75. //定位到樹中待刪除的結點
  76. BSTNode<E> nodeDel=searchBST(root,key);
  77. if(nodeDel==null){
  78. return false;
  79. }
  80. else{
  81. //nodeDel的右子樹為空,則只需要重接它的左子樹
  82. if(nodeDel.rchild==null){
  83. BSTNode<E> parent=nodeDel.parent;
  84. if(parent.lchild.key.compareTo(nodeDel.key)==0)
  85. parent.lchild=nodeDel.lchild;
  86. else
  87. parent.rchild=nodeDel.lchild;
  88. }
  89. //左子樹為空,則重接它的右子樹
  90. else if(nodeDel.lchild==null){
  91. BSTNode<E> parent=nodeDel.parent;
  92. if(parent.lchild.key.compareTo(nodeDel.key)==0)
  93. parent.lchild=nodeDel.rchild;
  94. else
  95. parent.rchild=nodeDel.rchild;
  96. }
  97. //左右子樹均不空
  98. else{
  99. BSTNode<E> q=nodeDel;
  100. //先找nodeDel的左結點s
  101. BSTNode<E> s=nodeDel.lchild;
  102. //然后再向s的右盡頭定位(這個結點將替代nodeDel),其中q一直定位在s的直接父親結點
  103. while(s.rchild!=null){
  104. q=s;
  105. s=s.rchild;
  106. }
  107. //換掉nodeDel的關鍵字為s的關鍵字
  108. nodeDel.key=s.key;
  109. //重新設置s的左子樹
  110. if(q!=nodeDel)
  111. q.rchild=s.lchild;
  112. else
  113. q.lchild=s.lchild;
  114. }
  115. return true;
  116. }
  117. }
  118. }
  119. /**
  120. * 遞歸查找關鍵子
  121. * @param node 樹結點
  122. * @param key 關鍵字
  123. * @return 查找成功,返回該結點,否則返回null。
  124. */
  125. private BSTNode<E> searchBST(BSTNode<E> node, E key){
  126. if(node==null){
  127. System.out.println("]. 搜索失敗");
  128. return null;
  129. }
  130. System.out.print(node.key+" —>");
  131. //搜索到關鍵字
  132. if(node.key.compareTo(key)==0){
  133. System.out.println("]. 搜索成功");
  134. return node;
  135. }
  136. //在左子樹搜索
  137. else if(node.key.compareTo(key)>0){
  138. return searchBST(node.lchild,key);
  139. }
  140. //在右子樹搜索
  141. else{
  142. return searchBST(node.rchild,key);
  143. }
  144. }
  145. /**
  146. * 遞歸插入關鍵字
  147. * @param node 樹結點
  148. * @param key 樹關鍵字
  149. * @return true/插入成功,false/插入失敗
  150. */
  151. private boolean insertBST(BSTNode<E> node, E key){
  152. System.out.print(node.key+" —>");
  153. //在原樹中找到相同的關鍵字,無需插入。
  154. if(node.key.compareTo(key)==0)
  155. {
  156. System.out.println("]. 搜索有相同關鍵字,插入失敗");
  157. return false;
  158. }
  159. else{
  160. //搜索node的左子樹
  161. if(node.key.compareTo(key)>0){
  162. //如果當前node的左子樹為空,則將新結點key node插入到左孩子處
  163. if(node.lchild==null) {
  164. System.out.println("]. 插入到"+node.key+"的左孩子");
  165. BSTNode<E> newNode=new BSTNode<E>(key);
  166. node.lchild=newNode;
  167. newNode.parent=node;
  168. return true;
  169. }
  170. //如果當前node的左子樹存在,則繼續(xù)遞歸左子樹
  171. else return insertBST(node.lchild, key);
  172. }
  173. //搜索node的右子樹
  174. else{
  175. if(node.rchild==null){
  176. System.out.println("]. 插入到"+node.key+"的右孩子");
  177. BSTNode<E> newNode=new BSTNode<E>(key);
  178. node.rchild=newNode;
  179. newNode.parent=node;
  180. return true;
  181. }
  182. else return insertBST(node.rchild,key);
  183. }
  184. }
  185. }
  186. /**
  187. * 得到BST根節(jié)點
  188. * @return BST根節(jié)點f
  189. */
  190. public BSTNode<E> getRoot(){
  191. return this.root;
  192. }
  193. /**
  194. * 非遞歸中序遍歷BST
  195. */
  196. public void InOrderTraverse(){
  197. if(root==null)
  198. return;
  199. BSTNode<E> node=root;
  200. ArrayList<BSTNode<E>> stack=new ArrayList<BSTNode<E>>();
  201. stack.add(node);
  202. while(!stack.isEmpty()){
  203. while(node.lchild!=null){
  204. node=node.lchild;
  205. stack.add(node);
  206. }
  207. if(!stack.isEmpty()){
  208. BSTNode<E> topNode=stack.get(stack.size()-1);
  209. System.out.print(topNode.key+" ");
  210. stack.remove(stack.size()-1);
  211. if(topNode.rchild!=null){
  212. node=topNode.rchild;
  213. stack.add(node);
  214. }
  215. }
  216. }
  217. }
  218. /**
  219. * 測試
  220. */
  221. public static void main(String[] args) {
  222. BST<Integer> tree=new BST<Integer>();
  223. tree.insert(new Integer(100));
  224. tree.insert(new Integer(52));
  225. tree.insert(new Integer(166));
  226. tree.insert(new Integer(74));
  227. tree.insert(new Integer(11));
  228. tree.insert(new Integer(13));
  229. tree.insert(new Integer(66));
  230. tree.insert(new Integer(121));
  231. tree.search(new Integer(11));
  232. tree.InOrderTraverse();
  233. tree.delete(new Integer(11));
  234. tree.InOrderTraverse();
  235. }
  236. }
 

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多