二叉排序樹
若左子樹不空,則左子樹上所有結點的值均<根結點的值
若右子樹不空,則右子樹上所有結點
的值均>根結點的值它的左、右子樹也都是二叉排序樹。
二叉排序樹的查找過程
若樹爲空,則結束
若樹非空,則進行如下操作
- 若給定值k=根關鍵字,則查找成功
- 若給定值<根關鍵字,則繼續在左子樹進行
- 若給定值>根關鍵字,則繼續在右子樹進行
二叉排序樹的刪除過程
- 若刪除的是葉子結點,則直接刪除該結點即可。若同時也是根結點,則刪除後二叉排序樹爲空樹
- 若被刪除結點只有左子樹而無右子樹 ,可直接將其左子樹的根結點替代被刪除結點的位置
- 若刪除結點只有右子樹而無左子樹,可直接將其右子樹的根結點替代被刪除結點的位置
- 若刪除結點有 左、右子樹,可將被刪除結點在中序遍歷下的前驅結點(或者後繼結點)去代替被刪除的結點
BSTree
package Search;
/**
* @description 二叉排序樹結構
* @date 2016年1月7日
*/
public class BSTree {
private BiTreeNode root;
public BSTree(){
root = null;
}
public BiTreeNode getRoot() {
return root;
}
/**
* @description 中序遍歷二叉樹
* @param p
* @author liuquan
* @date 2016年1月7日
*/
public void inOrderTraverse(BiTreeNode p){
if(p != null){
inOrderTraverse(p.getLchild());
System.out.print(p.getData() + " ");
inOrderTraverse(p.getRchild());
}
}
/**
* @description 在二叉排序樹中查找關鍵字爲key的結點,若查找成功,則返回該結點
* @param p
* @param key
* @return
* @author liuquan
* @date 2016年1月7日
*/
public Object searchBST(BiTreeNode p, int key){
if(p != null){
if(key == (int)p.getData()){
return p;
}
if(key < (int)p.getData()){
return searchBST(p.getLchild(), key);
}
else{
return searchBST(p.getRchild(), key);
}
}
return null;
}
/**
* @description 插入操作,若插入成功,則返回true。。
* 先比較key值,若已存在,則不用插入;否則,將新結點插入到表中。新插入的結點一定是作爲葉子結點添加到表中
* @return
* @author liuquan
* @date 2016年1月7日
*/
public boolean insertBST(BiTreeNode p, int key){
if(p == null){
root = new BiTreeNode(key);
return true;
}
//不插入關鍵字重複的結點
if(key == (int)p.getData()){
return false;
}
if(key < (int)p.getData()){ //往左子樹插入
if(p.getLchild() == null){
p.setLchild(new BiTreeNode(key));
return true;
}
else{
return insertBST(p.getLchild(), key);
}
}
else if(p.getRchild() == null){
p.setRchild(new BiTreeNode(key));
return true;
}
else{
return insertBST(p.getRchild(), key);
}
}
/**
* @description 刪除操作。若刪除成功,則返回被刪除的結點值
* 1.若刪除的是葉子結點,則直接刪除該結點即可。若同時也是根結點,則刪除後二叉排序樹爲空樹
* 2.若被刪除結點只有左子樹而無右子樹 ,可直接將其左子樹的根結點替代被刪除結點的位置
* 3.若刪除結點只有右子樹而無左子樹,可直接將其右子樹的根結點替代被刪除結點的位置
* 4.若刪除結點有左、右子樹,可將被刪除結點在中序遍歷下的前驅結點(或者後繼結點)去代替被刪除的結點
* @param p 以p爲根的二叉排序樹
* @param key
* @param parent 是p的父結點,根結點的父結點是null
* @return
* @author liuquan
* @date 2016年1月7日
*/
public int removeBST(BiTreeNode p, int key,BiTreeNode parent){
if(p != null){
if(key < (int)p.getData()){ //在左子樹中刪除
return removeBST(p.getLchild(), key, p);
}
else if(key > (int)p.getData()){ //在右子樹中刪除
return removeBST(p.getRchild(), key, p);
}
else if(p.getLchild()!=null && p.getRchild() != null){ //相等且有左右子樹
// 尋找p在中序遍歷下的後繼結點next
BiTreeNode next = p.getRchild();
//尋找右子樹中的最左孩子就是p的中序後繼結點
while(next.getLchild() != null){
next = next.getLchild();
}
p.setData(next.getData());
return removeBST(p.getRchild(), (int)p.getData(), p);
}
else{ //p是1度或者葉子結點
if(parent == null){ //p是根
if(p.getLchild() != null){
root = p.getLchild();
}
else{
root = p.getRchild();
}
return (int) p.getData();
}
if(p == parent.getLchild()){ // p是左孩子
if(p.getLchild() != null){
parent.setLchild(p.getLchild());
}
else{
parent.setLchild(p.getRchild());
}
}
else if(p == parent.getRchild()){ //p是右孩子
if(p.getLchild() != null){
parent.setRchild(p.getLchild());
}
else{
parent.setRchild(p.getRchild());
}
}
return (int) p.getData();
}
}
return -1;
}
}
/**
* @description 二叉排序樹結點結構
* @date 2016年1月7日
*/
class BiTreeNode {
private Object data;
private BiTreeNode lchild, rchild;
public BiTreeNode() {
this(null);
}
public BiTreeNode(Object data) {
this(data, null, null);
}
public BiTreeNode(Object data, BiTreeNode lchild, BiTreeNode rchild) {
this.data = data;
this.lchild = lchild;
this.rchild = rchild;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public BiTreeNode getLchild() {
return lchild;
}
public void setLchild(BiTreeNode lchild) {
this.lchild = lchild;
}
public BiTreeNode getRchild() {
return rchild;
}
public void setRchild(BiTreeNode rchild) {
this.rchild = rchild;
}
}
Test
package Search;
import Sort.Sort;
public class Test {
public static void main(String[] args) {
int[] a = new int[]{52, 39, 67, 95, 70, 8, 25, 52};
Sort.display(a);
System.out.println("順序查找" );
System.out.println("95的位置是:" + Search.seqSearch(a, 95));
System.out.println("30的位置是:" + Search.seqSearch(a, 30));
System.out.println();
int[] b = Sort.qSort(a);
Sort.display(b);
System.out.println("二分查找");
System.out.println("95的位置是:" + Search.binarySearch(b, 95));
System.out.println("30的位置是:" + Search.seqSearchWithGuard(b, 30));
System.out.println();
System.out.println("二叉排序樹 ");
// 構建二叉排序樹
BSTree bs = new BSTree();
for(int i = 0; i < a.length; i++){
bs.insertBST(bs.getRoot(), a[i]);
}
System.out.println("二叉樹中根遍歷:");
bs.inOrderTraverse(bs.getRoot());
System.out.println();
System.out.println("95的位置是:" + bs.searchBST(bs.getRoot(), 95));
System.out.println("30的位置是:" + bs.searchBST(bs.getRoot(), 30));
System.out.println("刪除結點50後的二叉樹中根遍歷:");
bs.removeBST(bs.getRoot(), 52, null);
bs.inOrderTraverse(bs.getRoot());
}
}
平衡二叉樹
平衡二叉樹又稱爲AVL樹,它是一顆空樹,或者其左子樹和右子樹都是平衡二叉樹且左子樹和右子樹的深度之差(平衡因子,或平衡度)不會超過1。
失去平衡的原因可歸納爲4類:
LL型平衡旋轉(單向右旋):
RR型平衡旋轉(單向左旋):
LR型平衡旋轉(先左旋後右旋):
RL型平衡旋轉(先右旋後左旋):
平衡二叉樹插入一個記錄
若AVL樹爲空,則插入x作爲根結點,樹的深度增1
若x與AVL樹根結點相等,則不插入
若x<根,則x插入到左子樹中,且插入後左子樹深度增1就要分列不同情況:
若AVL根平衡度爲-1,則將根結點的平衡因子調整爲0,且樹深度不變
若AVL根平衡度爲0,則將根平衡度調爲1,樹深度增1
若AVL根平衡度爲1,則若左子樹根結點平衡度爲1時需要LL型旋轉,爲-1時需要LR型旋轉
若x>根,參考步驟3的算法
平衡二叉樹的優缺點
優點是使樹結構更好,提高了查找操作的速度;缺點是插入和刪除操作複雜化,降低了速度。