針對二叉查詢樹的操作(增刪改查)的時間和樹的高度成正比,比如都有10個節點的乙個樹,樹高為4和樹高為10的操作時間肯定是不同的,這個時間實際上是o(lgn),二叉查詢樹的期望的樹高是lgn,從而基本動態集合的操作平均時間為θ(lgn)。
通常二叉查詢樹基於鍊錶實現,每個節點儲存左,右子節點,如果想更方便的實現前後查詢,可以增加個乙個父節點屬性。由於二叉查詢樹的特點,採用中序遍歷可以按照從小到大的順序將樹的所有元素輸出,一般的中序遍歷都是通過遞迴來實現。
下面自己實現乙個二叉查詢樹的插入和中序遍歷:
package tree;
/** * 二叉搜尋樹
* @author administrator
* */
public class binarysearchtree
else
}/**
* 中序遍歷
* @param n
*/public void midorderwalk(node n)
midorderwalk(n.left);
system.out.println(n.value);
midorderwalk(n.right); }
private void insertnotroot(int value)
cur = cur.left;
}//插入右子樹
else
cur = cur.right;
}} }
public node getroot()
public void setroot(node root)
//節點類,儲存左右子孩子和父類節點的索引
對於二叉查詢樹來說,刪除的動作比插入還要複雜一些,因為刪除需要考慮的情況更多些,現在就來實現刪除的動作,大概考慮下有哪些情況:
首先肯定如果我們刪除乙個節點,該節點沒有左孩子也沒有右孩子,則刪除它完全不會影響我們當前的二叉查詢樹,本來是一棵二叉查詢樹現在還會是一棵二叉查詢樹。比如上圖中的節點48。
其次,考慮要刪除的節點只有左孩子或者只有右孩子,比如節點37只有左孩子,由於只有乙個孩子,非左即右,左邊的都比該節點小,右邊的都不比該節點小,因此大小並沒有二義性,取該節點的左孩子或者右孩子節點取代該節點即可。
最麻煩的是有兩個孩子節點的情況,比如圖中的節點47,針對47的刪除,我們可以有下面的兩種情況:
考慮刪掉47我們可以拿哪個節點來替代47從而保證樹的連線性,考慮到47左邊的子樹節點都比它小,如果在左子樹找,則肯定要找左子樹中最大的節點,這個節點肯定需要沿著左子樹一直找右節點,左圖就是這樣一種情況。同理右圖,沿著右子樹一直找左節點。
先不急看刪除的**,先看下兩個概念,後繼和前驅,乙個節點的前驅是指小於該節點的最大節點,也就是中序遍歷該節點的前乙個節點,後繼就是指大於該節點的最小節點,也就是中序遍歷該節點的後乙個節點。比如,左圖,其實就是尋找47前驅的過程,右圖就是尋找47後繼的過程。
先不急寫尋找某個節點的前驅和後繼的**,尋找前驅和後繼其實就是尋找子樹最大和最小節點的過程,先來寫尋找一棵樹的最大和最小節點的實現,如下:
/**
* 尋找一棵樹的最大節點
* 不斷尋找右節點,直到該節點沒有右孩子節點
* @param root
* @return
*/public node getmaxnode(node root)
else
return maxnode;
} }public int getmaxvalue(node root)
/*** 尋找一棵樹的最小節點
* @param root
* @return
*/public node getminnode(node root)
else
return minnode;
} }public int getminvalue(node root)
邏輯不難,就不詳細講解了。有了上面的邏輯,再來看下找出乙個節點前驅和後繼的**:
/**
* 獲取乙個節點的後繼
* @param curnode
* @return
*/public node successor(node curnode)
else
else
return parent;
}} }
/*** 獲取乙個節點的前驅
* @param curnode
* @return
*/public node precessor(node curnode)
else
else
return parent;
}} }
在寫刪除邏輯之前再寫最後乙個方法,根據值獲取node節點:
/**
* 查詢元素
* @param value
* @return
*/public node searchnode(int value)
else
}return node;
}
好了,做了那麼多鋪墊,終於可以來完成刪除的邏輯了,實際上我們之前的**,基本完成了乙個搜尋二叉樹提供的功能(不考慮非法資料)。
/**
* 刪除操作,分三種情況
* @param value
*/public void remove(int value)
else
else
}//第二種情況
else if(null != node.left && null == node.right)
else
else}}
//第二種情況
else if(null == node.left && null != node.right)
else
else}}
else}}
刪除的**邏輯分支比較多,仔細想想也不是很難理解,只是需要考慮的場景多一些。我們跟著第一張圖來走一遍**,假設刪除的是頭結點62,進入刪除**,首先searchnode找出該節點,root左右孩子都有進入最後乙個else分支,查詢62的前驅,很明顯,進入precessor(62),左子樹不為空,則進入getmaxnode(58),58沒有右節點直接返回58,58就是62的直接前驅,呼叫remove(58),因為58只有左子樹,只需要將62的左孩子指標指向58的左孩子指標即可。此時將58刪除了,但是我們要刪除的是62頭結點,只需要將頭結點的value賦值為前驅的節點值即可。 資料結構之二叉查詢樹
二叉樹成為二叉查詢樹的性質是,對於樹種的每個節點x,他的左子樹中的所有關鍵字的值均小於x的關鍵字的值,而他的右子樹中的所有關鍵字的值均大於x的關鍵字的值。這意味著,該樹的所有元素均可以是用某種統一的方式排序。tree.h pragma once we define the binary tree.s...
資料結構之二叉查詢樹
二叉查詢樹 binary search tree 又被稱為二叉搜尋樹。設x為二叉查詢樹中的乙個結點,x節點包含關鍵字key,節點x的key值記為 ke y x k ey x 如果y是x的左子樹中的乙個結點,則 ke y y ke y x k ey y ke y x 如果y是x的右子樹的乙個結點,則 ...
資料結構查詢演算法之二叉查詢樹
關於二叉查詢樹,介紹肯定是很多的,這裡我列舉的下面的 和思想,是按照二叉查詢樹,但是沒有使用二叉樹的資料結構,而是按照陣列索引建立邏輯上的二叉樹結構,並使用二叉樹的遞迴方式查詢給定的值,並在注釋中對不同的情況提出一點自己的看法,建立樹的過程參考堆排序的思想,並且在結構上大致相同,所以需要注意的地方也...