Cây tìm kiếm nhị phân

Cây tìm kiếm nhị phân (viết tắt tiếng Anh: BST - "Binary Search Tree") là một cấu trúc dữ liệu rất thuận lợi cho bài toán tìm kiếm. Mỗi cây tìm kiếm nhị phân đều có tính chất sau: Với mỗi nút formula_1, các nút ở cây con bên trái của formula_1 đều có giá trị "key" nhỏ hơn formula_1: formula_4, còn các nút ở cây con bên phải của formula_1 đều có key lớn hơn hoặc bằng formula_1: formula_7.
Định nghĩa.
Cây tìm kiếm ứng với n khóa formula_8 là cây nhị phân mà mỗi nút đều được gán một khóa sao cho với mỗi mỗi nút k:
Cây tìm kiếm nhị phân là một cấu trúc dữ liệu cơ bản được sử dụng để xây dựng các cấu trúc dữ liệu trừu tượng hơn như các tập hợp, đa tập hợp, các dãy kết hợp.
Nếu một BST có chứa các giá trị giống nhau thì nó biểu diễn một đa tập hợp. Cây loại này sử dụng các bất đẳng thức không nghiêm ngặt. Mọi nút trong cây con trái có khóa nhỏ hơn khóa của nút cha, mọi nút trên cây con phải có nút lớn hơn hoặc bằng khóa của nút cha.
Nếu một BST không chứa các giá trị giống nhau thì nó biểu diễn một tập hợp đơn trị như trong lý thuyết tập hợp. Cây loại này sử dụng các bất đẳng thức nghiêm ngặt. Mọi nút trong cây con trái có khóa nhỏ hơn khóa của nút cha, mọi nút trên cây con phải có khóa lớn hơn khóa của nút cha.
Việc chọn đưa các giá trị bằng nhau vào cây con phải (hay trái) là tùy theo mỗi người. Một số người cũng đưa các giá trị bằng nhau vào cả hai phía, nhưng khi đó việc tìm kiếm trở nên phức tạp hơn.
Các phép toán trên BST.
Tìm kiếm (Searching).
Việc tìm một khóa trên BST có thể thực hiện nhờ đệ quy. Chúng ta bắt đầu từ gốc. Nếu khóa cần tìm bằng khóa của gốc thì khóa đó trên cây, nếu khóa cần tìm nhỏ hơn khóa ở gốc, ta phải tìm nó trên cây con trái, nếu khóa cần tìm lớn hơn khóa ở gốc, ta phải tìm nó trên cây con phải. Nếu cây con (trái hoặc phải) là rỗng thì khóa cần tìm không có trên cây.
Giả mã
 codice_1
Mã Python:
codice_2
Thời gian tìm kiếm trung bình là O(log "2n"), và là O("n") khi cây là không cân bằng chỉ là một danh sách liên kết.
Chèn (Insertion).
Phép chèn bắt đầu giống như phép tìm kiếm; Nếu khóa của gốc khác khóa cần chèn ta tìm nó trong cây con trái hoặc phải. Nếu cây con trái hoặc phải tương ứng là rỗng (không tìm thấy) thì thêm một nút và gán cho nút ấy khóa cần chèn.
Sau đây là mã trong C++:
codice_3
Mã Python:
codice_4
Xóa (Deletion).
Xét các trường hợp sau
Cũng có thể tìm nút tiền nhiệm hoặc nút kế vị đổi chỗ nó với nút cần xóa và sau đó xóa nó. Vì các nút kiểu này có ít hơn hai con nên việc xóa nó được quy về hai trường hợp trước.
Sau đây là mã C++
 codice_5
Mã python:
codice_6
Phép duyệt.
Khi một cây tìm kiếm nhị phân được tạo ra, tất cả các nút có thể được duyệt theo thứ tự giữa nhờ duyệt đệ quy cây con bên trái, in nút đang duyệt, rồi duyệt đệ quy cây con bên phải, tiếp tục làm như vây với mỗi nút của cây trong quá trình đệ quy. Với mọi cây nhị phân, cây có thể được duyệt theo thứ tự trước() hoặc theo thứ tự sau(), cả hai cách đều hữu dụng với cây tìm kiếm nhị phân.
Đoạn mã cho duyệt theo thứ giữa được viết dưới đây với C++:
codice_7
Phép duyệt có độ phức tạp tính toán là Ω("n"), vì nó phải duyệt qua tất cả các nút. Độ phức tạp trên cũng là O("n").
Sắp xếp.
Một cây tìm kiếm nhị phân có thể được sử dụng như một giải thuật sắp xếp đơn giản nhưng hiểu quả. Giống như heapsort, chúng ta chèn tất cả các giá trị chúng ta muốn sắp xếp vào một cây tìm kiếm nhị phân và in ra kết quả theo thứ tự:
codice_8
Trường hợp xấu nhất của codice_9 có độ phức tạp là formula_9—nếu nhập vào một dãy giá trị đã sắp xếp, cây nhị phân tạo thành sẽ không có các nút trái. Ví dụ, codice_10 tạo thành cây codice_11.
Có một vài cách để vượt qua trường hợp này với các cây nhị phân đơn giản; cách đơn giản nhất là cây tìm kiếm nhị phân tự cân bằng. Với thủ tục này được sử dụng với cây nhị phân tự cân bằng, trường hợp xấu nhất sẽ có độ phức tạp là O("n"log "n").
Mã giả bằng ngôn ngữ C.
//Khai bao cay nhi phan
typedef char TData;
typedef struct TNode{
 TData Data;
 TNode* left;
 TNode* right;
typedef TNode* TTree;
/*=== Tao cay rong ===*/
void MakeNull_Tree(TTree *T)
 (*T)=NULL;
/*=== Kiem tra cay rong ===*/
int EmptyTree(TTree T)
 return T==NULL;
/*=== Xac dinh con trai ===*/
TTree LeftChild(TTree T)
 if(T != NULL)
 return T-&gt;left;
 else return NULL;
/*=== Xac dinh con phai ===*/
TTree RightChild(TTree T)
 if(T != NULL)
 return T-&gt;right;
 else return NULL;
/*=== Xac dinh nut la ===*/
int isLeaf(TTree T)
 if((T != NULL) &amp;&amp; (T-&gt;left == NULL) &amp;&amp; (T-&gt;right == NULL))
 return 1;
 else return NULL;
/*=== Xac dinh so nut cua cay ===*/
int nb_nodes(TTree T)
 if(EmptyTree(T))
 return 0;
 else return nb_nodes(T-&gt;left)+nb_nodes(T-&gt;right)+1;
// Ham xac dinh gia tri lon nhat trong hai so nguyen
int max(int value1,int value2)
 return ((value1 &gt; value2) ? value1: value2); //xac dinh gia tri lon nhat trong 2 gia tri so nguyen
// Ham xac dinh chieu cao cua cay
int TreeHeight(TTree T)
 int height=0;
 if (!EmptyTree(T))
 if (isLeaf(T))
 return 0;
 else
 height = max(TreeHeight(LeftChild(T)),TreeHeight(RightChild(T)))+1;
 return height;
// Ham xac dinh so nut la tren cay
int nb_leaf(TTree T)
 int leaf=0;
 if(!EmptyTree(T))
 if (isLeaf(T))
 leaf++;
 else
 leaf = nb_leaf(LeftChild(T))+nb_leaf(RightChild(T));
 return leaf;
/*=== Tao cay moi tu hai cay co san ===*/
TTree Create2(TData v,TTree left,TTree right)
 TTree N; // Khai bao 1 cay moi
 N = (TNode*)malloc(sizeof(TNode)); //Cap phat vung nho cho nut N
 N-&gt;Data = v; // Nhan cua nut N la v
 N-&gt;left = left; //Con trai cua N la cay left
 N-&gt;right = right; //Con phai cua N la cay right
 return N;
/*=== Duyet cay nhi phan ===*/
//Duyet tien tu
void NLR(TTree T)
 if(!EmptyTree(T))
 printf(" %c",T-&gt;Data); //Xu ly nut
 NLR(LeftChild(T)); //Duyet tien tu con trai
 NLR(RightChild(T)); //Duyet tien tu con phai
//Duyet trung tu
void LNR(TTree T)
 if(!EmptyTree(T))
 LNR(LeftChild(T)); //Duyet trung tu con trai
 printf(" %c",T-&gt;Data);//Xu ly nut
 LNR(RightChild(T));//Duyet trung tu con phai
//Duyet hau tu
void LRN(TTree T)
 if(!EmptyTree(T))
 LRN(LeftChild(T)); //Duyet hau tu con trai
 LRN(RightChild(T));//Duyet hau tu con phai
 printf(" %c",T-&gt;Data);//Xu ly nut
Các loại cây tìm kiếm nhị phân.
Có rất nhiều loại cây tìm kiếm nhị phân. cây AVL và cây đỏ đen đều là các dạng của cây tìm kiếm nhị phân tự cân bằng. () là một cây nhị phân có thể tự đẩy các phần mới vào gần nút gốc. Trong một treap ("cây heap"), mỗi nút có một sự ưu tiên (priority) và các nút cha có sự ưu tiên cao hơn các nút con của chúng.