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 , các nút ở cây con bên trái của đều có giá trị key nhỏ hơn : , còn các nút ở cây con bên phải của đều có key lớn hơn hoặc bằng : .

Định nghĩa Cây Tìm Kiếm Nhị Phân

Cây Tìm Kiếm Nhị Phân 
Cây tìm kiếm nhị phân

Cây tìm kiếm ứng với n khóa Cây Tìm Kiếm Nhị Phân 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:

  • Mọi khóa trên cây con trái đều nhỏ hơn khóa trên nút k
  • Mọi khóa trên cây con phải đều lớn hơn khóa trên 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 Cây Tìm Kiếm Nhị Phân

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.

Cây Tìm Kiếm Nhị Phân 

Giả mã

 

Search_binary_tree(node, key);

    if node is Null then         return None;  /* key not found */             if key < node.key:           return search binary_tree(node.left, key);       else             if key > node.key               return search_binary_tree(node.right, key)           else  /* key is equal to node key */               return node.value;  # found key 

Mã Python:

 def search_binary_tree(node, key):      if node is None:          return None  # key not found      if key < node.key:          return search_binary_tree(node.leftChild, key)      elif key > node.key:          return search_binary_tree(node.rightChild, key)      else:  # key is equal to node key          return node.value  # found key 

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++:

void InsertNode(struct node *&treeNode, struct node *newNode) { //Inserts node pointered by "newNode" to the subtree started by "treeNode"      if (treeNode == NULL)         treeNode = newNode; //Only changes "node" when it is NULL     else if (newNode->value < treeNode->value)         InsertNode(treeNode->left, newNode);     else         InsertNode(treeNode->right, newNode); } 

Mã Python:

 def binary_tree_insert(node, key, value):      if node is None:          return TreeNode(None, key, value, None)        if key == node.key:          return TreeNode(node.left, key, value, node.right)      if key < node.key:          return TreeNode(binary_tree_insert(node.left, key, value), node.key, node.value, node.right)      else:          return TreeNode(node.left, node.key, node.value, binary_tree_insert(node.right, key, value)) 

Xóa (Deletion)

Xét các trường hợp sau

  • Xóa một lá: Vì lá không có con nên chỉ cần giải phóng nó khỏi cây.

Cây Tìm Kiếm Nhị Phân 

  • Xóa nút có một con: Xóa và thay thế nó bằng con duy nhất của nó.

Cây Tìm Kiếm Nhị Phân 

  • Xóa một nút có hai con: Xóa nút đó và thay thế nó bằng nút có khóa lớn nhất trong các khóa nhỏ hơn khóa của nó (được gọi là "nút tiền nhiệm" -nút cực phải của cây con trái) hoặc nút có khóa nhỏ nhất trong các khóa lớn hơn nó (được gọi là "nút kế vị" - nút cực trái của cây con phải)

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.

Cây Tìm Kiếm Nhị Phân 

Sau đây là mã C++

void DeleteNode(struct node*& node) {     if (node->left == NULL) {               struct node* temp = node;         node = node->right;         delete temp;     } else if (node->right == NULL) {               struct node* temp = node;         node = node->left;         delete temp;             } else {         // In-Order predecessor(right most child of left subtree)          // Node has two children - get max of left subtree          struct node** temp = &(node->left); // get left node of the original node          // find the right most child of the subtree of the left node         while ((*temp)->right != NULL) {             temp = &((*temp)->right);         }                   // copy the value from the right most child of left subtree to the original node         node->value = (*temp)->value;          // then delete the right most child of left subtree since it's value is         // now in the original node         DeleteNode(*temp);     } } 

Mã python:

def findMin(self):     '''     Finds the smallest element that is a child of *self*     '''     current_node = self     while current_node.left_child:         current_node = current_node.left_child     return current_node  def replace_node_in_parent(self, new_value=None):     '''     Removes the reference to *self* from *self.parent* and replaces it with *new_value*.     '''     if self == self.parent.left_child:         self.parent.left_child = new_value     else:         self.parent.right_child = new_value     if new_value:         new_value.parent = self.parent  def binary_tree_delete(self, key):     if key < self.key:         self.left_child.binary_tree_delete(key)     elif key > self.key:         self.right_child.binary_tree_delete(key)     else: # delete the key here         if self.left_child and self.right_child: # if both children are present             # get the smallest node that's bigger than *self*             successor = self.right_child.findMin()             self.key = successor.key             # if *successor* has a child, replace it with that             # at this point, it can only have a *right_child*             # if it has no children, *right_child* will be "None"             successor.replace_node_in_parent(successor.right_child)         elif self.left_child or self.right_child:   # if the node has only one child             if self.left_child:                 self.replace_node_in_parent(self.left_child)             else:                 self.replace_node_in_parent(self.right_child)         else: # this node has no children             self.replace_node_in_parent(None) 

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++:

void traverse_binary_tree(struct node* n) {    if(n==null)     //Cay rong        return NULL;    else        {            traverse_binary_tree(n->left);     //Duyet cay con trai theo thu tu giua            printf("%d",n.key);                //Tham nut            traverse_binary_tree(n->right);    //Duyet cay con phai theo thu tu giua        } } 

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ự:

def build_binary_tree(values):     tree = None     for v in values:         tree = binary_tree_insert(tree, v)     return tree  def get_inorder_traversal(root):     '''     Returns a list containing all the values in the tree, starting at *root*.     Traverses the tree in-order(leftChild, root, rightChild).     '''     result = []     traverse_binary_tree(root, lambda element: result.append(element))     return result 

Trường hợp xấu nhất của build_binary_tree có độ phức tạp là Cây Tìm Kiếm Nhị Phân —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ụ, traverse_binary_tree([1, 2, 3, 4, 5]) tạo thành cây (1 (2 (3 (4 (5))))).

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(nlog n).

Mã giả bằng ngôn ngữ C Cây Tìm Kiếm Nhị Phân

#include  #include  #include  //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->left;  else   return NULL; } /*=== Xac dinh con phai ===*/ TTree RightChild(TTree T) {  if(T != NULL) return T->right;  else   return NULL; } /*=== Xac dinh nut la ===*/ int isLeaf(TTree T) {   if((T != NULL) && (T->left == NULL) && (T->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->left)+nb_nodes(T->right)+1; }  // Ham xac dinh gia tri lon nhat trong hai so nguyen  int max(int value1,int value2) { return ((value1 > 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->Data = v; // Nhan cua nut N la v   N->left = left; //Con trai cua N la cay left   N->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->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->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->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 AVLcâ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.

Xem thêm

Chú thích

Tham khảo

  • Donald Knuth. The Art of Compter Programming, Volume 3: Sorting and Searching, Third Edition. Addison-Wesley, 1997. ISBN 0-201-89685-0. Section 6.2.2: Binary Tree Searching, pp. 426–458.
  • Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, and Clifford Stein. Introduction to Algorithms, Second Edition. MIT Press and McGraw-Hill, 2001. ISBN 0-262-03293-7. Chapter 12: Binary search trees, pp. 253–272. Section 15.5: Optimal binary search trees, pp. 356–363.

Liên kết ngoài

Tags:

Định nghĩa Cây Tìm Kiếm Nhị PhânCác phép toán trên BST Cây Tìm Kiếm Nhị PhânMã giả bằng ngôn ngữ C Cây Tìm Kiếm Nhị PhânCác loại Cây Tìm Kiếm Nhị PhânCây Tìm Kiếm Nhị PhânCấu trúc dữ liệuTiếng Anh

🔥 Trending searches on Wiki Tiếng Việt:

Trận SekigaharaRomeo và JulietTriết họcBùi Văn CườngLương CườngBenjamin FranklinNgân hàng thương mại cổ phần Quân độiUzbekistanHòa BìnhNgười TàyTrần Văn RónGốm Bát TràngHội nghị thành lập Đảng Cộng sản Việt NamĐội tuyển bóng đá trong nhà quốc gia Thái LanDanh mục các dân tộc Việt NamNguyễn Thị ĐịnhĐiện BiênCách mạng Tháng TámTrường ChinhKhánh HòaNhà ĐườngNhà ThanhTikTokAFC Champions LeagueLê Minh KhuêHồ Chí MinhBắc thuộcĐài Tiếng nói Việt NamNguyễn Thị BìnhSóng thầnHà NamCao BằngBảo toàn năng lượngLê Trọng TấnTrấn ThànhXHamsterPhenolLý Chiêu HoàngAldehydeCố đô HuếMông CổTrần Cẩm TúNguyễn TrãiKinh tế Trung QuốcCuộc đua xe đạp toàn quốc tranh Cúp truyền hình Thành phố Hồ Chí Minh 2024Tập đoàn VingroupLưới thức ănBảo ĐạiQuảng BìnhMặt trận Dân tộc Giải phóng miền Nam Việt NamĐảng Cộng sản Việt NamNguyễn Thị Kim NgânQDương vật ngườiTác động của con người đến môi trườngXuân DiệuCarles PuigdemontThái BìnhQuy NhơnMai vàngDanh sách trường trung học phổ thông tại Hà NộiTừ Hi Thái hậuMiduZaloCúp bóng đá U-23 châu ÁThuận TrịAn Nam tứ đại khíNăm CamNgười Buôn GióChính phủ Việt NamTư Mã ÝBảy hoàng tử của Địa ngụcKim LânNguyễn Công PhượngGia KhánhDuyên hải Nam Trung BộSư tửChữ Hán🡆 More