二叉排序樹

2021-08-26 17:38:23 字數 3821 閱讀 9932

二叉排序樹又稱二叉查詢樹。或者為空樹,或者是具有以下性質的二叉樹:

(1)若它的左子樹不為空,則左子樹所有節點的值小於根結點,

(2)若它的右子樹不為空,則根結點的值小於所有右子樹結點的值

(3)它的左右子樹葉分別為二叉排序樹

二叉排序樹有一些基本的應用,可以用於查詢,查詢的長度與樹的深度有關。其平均查詢長度為logn(以2位底的對數,n為結點個數)。

乙個序列的排序二叉樹有多種構造情況。下面考慮兩種極端情況:深度最小和深度最大的二叉排序樹。

關鍵字序列(45,24,53,12,37,93),假設6個記錄的查詢概率相等都為1/6

容易看出這是一棵完全二叉樹,該數的深度(最大層次)為3,它的平均查詢長度為:

asl = 1/6(1+2+2+3+3+3)= 14/6

再來看該關鍵字序列構成深度最大的二叉排序樹

這是一棵單支樹,其深度為6,它的平均查詢長度為:

asl = 1/6(1+2+3+4+5+6)= 21/6

在隨機情況下,該數的平均查詢長度總是介於這兩種情況之間,即14/6  < log6 < 21/6。二叉排序樹的平均查詢長度和logn是等數量級的。

以上述完全叉樹序列為例,構造該二叉排序數的過程如下圖:

容易看出,每次插入新的結點都是二叉排序樹上的葉子結點,則在插入時,不必移動其他結點,只需改動某個結點的指標,由空變為非空即可。

結點的插入操作與二叉排序樹的定義緊密相關,即左《根《右,新插入乙個關鍵字時,從根結點開始比較,直到找到合適的插入位置為止。還有一種情況就是乙個序列中可能有兩個相同的關鍵字,對於這種情況,向樹中插入關鍵字時遇到相同關鍵字時,什麼都不做,不進行重複插入操作。

二叉排序樹的刪除過程

刪除二叉排序樹中任意乙個結點,需要分成以下三種情況討論:

(1)刪除的是葉子結點

這種情況最簡單,由於刪除葉子結點不會破壞整棵樹的結構,只需要修改其雙親結點的指標即可。

(2)刪除的結點只有左子樹或只有右子樹。

這種情況只需要將其左子樹或右子樹往上推,替代要刪除結點的位置,顯然,作此修改也不會破壞二叉排序樹的結構。

(3)刪除的結點左右子樹都不為空

這種情況稍微複雜一點,為了不保持二叉排序樹的結構,有兩種思路,一種就是從要刪除結點的左子樹中選取最大的結點進行替代之,另一種就是從要刪除的結點的右子樹中選取最小的結點替代之。

下面考慮第三種情況

f、p、q、s分別為指向結點f、p、q、s的指標,假如現要刪除結點p,它的左右子樹都不為空。

其一就是尋找p的左子樹中的最大結點,代替之。

易知左子樹中最大結點為s,將p結點用s結點代替,還需處理s結點的左孩子k,由於s只有左子樹k,在刪除結點s之後,只要令k為s的雙親q的右子樹即可。

其二就是尋找p結點的右子樹中最小的結點,代替之。

找到p後,第一步就是往右走一步到r,若r的左子樹不為空,則最小的結點在r的左子樹中,只需一直往左走即p=p->left,指定p->left為空(右子樹最小結點,其左子樹定為空

,則找到最小結點,將其代替要刪除的結點p,並將最小結點的右子樹代替該最小結點即可。如下圖,z為要刪除結點中右子樹中最小結點,將p用z代替後,z的位置再由y代替

若r的左子樹為空,那麼最小結點就是r,只需將r往上推代替p

具體實現:

#include "stdio.h"    

#include "stdlib.h"

#include "io.h"

#include "math.h"

#include "time.h"

#define ok 1

#define error 0

#define true 1

#define false 0

#define maxsize 100 /* 儲存空間初始分配量 */

typedef int status; /* status是函式的型別,其值是函式結果狀態**,如ok等 */

/* 二叉樹的二叉鍊錶結點結構定義 */

typedef struct bitnode /* 結點結構 */

bitnode, *bitree;

/* 遞迴查詢二叉排序樹t中是否存在key, */

/* 指標f指向t的雙親,其初始呼叫值為null */

/* 若查詢成功,則指標p指向該資料元素結點,並返回true */

/* 否則指標p指向查詢路徑上訪問的最後乙個結點並返回false */

status searchbst(bitree t, int key, bitree f, bitree *p)

else if (key==t->data) /* 查詢成功 */

else if (keydata)

return searchbst(t->lchild, key, t, p); /* 在左子樹中繼續查詢 */

else

return searchbst(t->rchild, key, t, p); /* 在右子樹中繼續查詢 */

}/* 當二叉排序樹t中不存在關鍵字等於key的資料元素時, */

/* 插入key並返回true,否則返回false */

status insertbst(bitree *t, int key)

else

return false; /* 樹中已有關鍵字相同的結點,不再插入 */

}/* 從二叉排序樹中刪除結點p,並重接它的左或右子樹。 */

status delete(bitree *p)

else if((*p)->lchild==null) /* 只需重接它的右子樹 */

else /* 左右子樹均不空 */

(*p)->data=s->data; /* s指向被刪結點的直接前驅(將被刪結點前驅的值取代被刪結點的值) */

if(q!=*p)

q->rchild=s->lchild; /* 重接q的右子樹 */

else

q->lchild=s->lchild; /* 重接q的左子樹 */

free(s);

} return true;

}/* 若二叉排序樹t中存在關鍵字等於key的資料元素時,則刪除該資料元素結點, */

/* 並返回true;否則返回false。 */

status deletebst(bitree *t,int key)

}int main(void)

; bitree t=null;

for(i=0;i<10;i++)

deletebst(&t,93);

deletebst(&t,47);

printf("本樣例建議斷點跟蹤檢視二叉排序樹結構");

return 0;

}

二叉排序樹

在複習資料結構,把這個東西總結一下。這種結構是動態查詢表,這種動態是相對靜態查詢 順序查詢,折半查詢,分塊查詢等 來說的。對於各種靜態鍊錶,要達到查詢複雜度為o logn 必須要求有序 而要使插入刪除複雜度為o 1 必須是鍊錶儲存。動態查詢表就可以同時滿足這兩者。動態查詢表的特點是表結構本身在查詢過...

二叉排序樹

name 二叉排序樹相關操作 author unimen date 2011 10 8 13 14 21 刪除結點比較麻煩,總結如下 4大種情況 1 結點p無右孩子 將該點的左孩子變為其在雙親中的同位孩子 1 p為其雙親的左孩子時將其的左孩子變為雙親的左孩子 2 p為其雙親的右孩子時將其的左孩子變為...

二叉排序樹

include include include include struct tree node void insert node struct tree node int void pre order struct tree node void in order struct tree node ...