• 数据结构——二叉查找树


     一、二叉查找树的定义

      二叉查找树是一种特殊的二叉树,又称为排序二叉树、二叉搜索树、二叉排序树。递归定义如下:

      •  要么二叉查找树是一棵空树
      •  要么二叉查找树由根结点、左子树、右子树组成,其中左子树和右子树都是二叉查找树,且左子树上所有结点的数据域均小于或等于根结点的数据域,右子树上所有结点的数据域均大于根结点的数据域。  

    二、二叉查找树的基本操作

      二叉查找树的基本操作有查找、插入、建树、删除。

      1. 查找操作

      二叉查找树的性质决定了可以只选择其中一棵子树进行遍历,因此查找将会是从树根到查找结点的一条路径,故最坏复杂度为 O(h),其中 h 是二叉查找树的高度。代码如下:

     1 // 查找二叉查找树中数据域为 x 的结点 
     2 void search(node* root, int x) {
     3     if(root == NULL) {    // 空树,查找失败 
     4         printf("search failed
    ");
     5         return; 
     6     }
     7     if(x == root->data) {    // 查找成功,访问之 
     8         printf("search success %d
    ", root->data); 
     9     } else if(x < root->data) {        // x 比根结点的数据域小,往左子树查找 
    10         search(root->lchild, x);
    11     } else {
    12         search(root->rchild, x);    // x 比根结点的数据域大,往右子树查找
    13     } 
    14 } 

      2. 插入操作

      对一棵二叉查找树来说,查找某个数据域的结点一定是沿着确定的路径进行的。因此,当某个需要查找的值在二叉查找树中查找成功,说明结点已经存在;反之,如果这个需要查找的值在二叉查找树中查找失败,那么说明查找失败的地方一定是结点需要插入的地方。代码如下:

     1 // 在二叉树中插入一个数据域为 x 的新结点 
     2 void insert(node** root, int x) {
     3     if((*root) == NULL) {        // 空树,说明查找失败,即为插入位置 
     4         (*root) = newNode(x);    // 生成数据域为 x 的结点
     5         return; 
     6     } 
     7     if(x == (*root)->data) {    // 查找成功,说明数据已经存在 
     8         return;
     9     } else if(x < (*root)->data) {        // x 比根结点的数据域小,往左子树查找 
    10         insert(&(*root)->lchild, x);
    11     } else {
    12         insert(&(*root)->rchild, x);    // x 比根结点的数据域大,往右子树查找
    13     } 
    14 } 

      3. 二叉查找树的建立

      建立一棵二叉查找树,就是先后插入 n 个结点的过程。代码如下:

    1 // 二叉查找树的建立 
    2 node* create(int data[], int n) {
    3     node* root = NULL;        // 新建根结点 root
    4     int i;
    5     for(i=0; i<n; ++i) {
    6         insert(&root, data[i]);        // 将 data 中数据依次插入二叉树 
    7     } 
    8     return root;            // 返回根结点 
    9 }

      4. 二叉查找树的删除

      为了保证操作之后仍然是一棵二叉查找树,可以以树中比待删除结点小的最大结点覆盖该结点,然后删除找到的最大结点;也可以以树中比待删除结点大的最小结点覆盖该结点,然后删除找到的最小结点。下面两个函数用来寻找以 root 为根的树中最大或最小权值的结点,用以辅助寻找结点的前驱和后继,代码如下:

     1 // 寻找以 root 为根结点的树中的最大权值结点 
     2 node* findMax(node* root) {
     3     while(root->rchild != NULL) {
     4         root = root->rchild;        // 不断往右,直到没有右孩子 
     5     }
     6     return root;
     7 } 
     8 
     9 // 寻找以 root 为根结点的树中的最小权值结点 
    10 node* findMin(node* root) {
    11     while(root->lchild != NULL) {
    12         root = root->lchild;        // 不断往左,直到没有左孩子 
    13     }
    14     return root;    
    15 }

      删除操作的代码如下:

     1 void deleteNode(node** root, int x) {
     2     if((*root) == NULL) {
     3         return;            // 不存在数据为 x 的结点 
     4     }
     5     if((*root)->data == x) {        // 找到欲删除结点 
     6         if((*root)->lchild == NULL && (*root)->rchild == NULL) {
     7             // 叶子结点,直接删除 
     8             (*root) = NULL; 
     9         } else if((*root)->lchild != NULL) {    // 左子树不为空 
    10             node* pre = findMax((*root)->lchild);    // 找前驱结点
    11             (*root)->data = pre->data;                // 用前驱结点覆盖原结点 
    12             deleteNode(&(*root)->lchild, pre->data);    // 在左子树中删除前驱结点 
    13         } else {                                // 右子树不为空 
    14             node* next = findMin((*root)->rchild);    // 找后继结点
    15             (*root)->data = next->data;                // 用后继结点覆盖原结点 
    16             deleteNode(&(*root)->rchild, next->data);    // 在左子树中删除后继结点 
    17         } 
    18     } else if((*root)->data > x) {
    19         deleteNode(&(*root)->lchild, x);            // 在左子树删除 x 
    20     } else {
    21         deleteNode(&(*root)->rchild, x);            // 在右子树删除 x 
    22     }
    23 } 

      完整的测试代码如下:

      1 /*
      2     二叉查找树 
      3 */
      4 
      5 #include <stdio.h>
      6 #include <string.h>
      7 #include <math.h>
      8 #include <stdlib.h>
      9 #include <time.h>
     10 #include <stdbool.h>
     11 
     12 #define typename int
     13 #define maxn 20
     14 // 二叉树结构定义 
     15 typedef struct _node {
     16     typename data;        // 数据域
     17     struct _node* lchild;        // 指向左子树的根结点
     18     struct _node* rchild;        // 指向右子树的根结点 
     19 } node; 
     20 
     21 // 生成一个新节点,v为数据 
     22 node* newNode(typename v) {
     23     node* Node = (node*)malloc(sizeof(node));
     24     Node->data = v;
     25     // 初始状态下没有左右子树 
     26     Node->lchild = Node->rchild = NULL;
     27     return Node;
     28 } 
     29 
     30 // 查找二叉查找树中数据域为 x 的结点 
     31 void search(node* root, int x) {
     32     if(root == NULL) {    // 空树,查找失败 
     33         printf("search failed
    ");
     34         return; 
     35     }
     36     if(x == root->data) {    // 查找成功,访问之 
     37         printf("search success %d
    ", root->data); 
     38     } else if(x < root->data) {        // x 比根结点的数据域小,往左子树查找 
     39         search(root->lchild, x);
     40     } else {
     41         search(root->rchild, x);    // x 比根结点的数据域大,往右子树查找
     42     } 
     43 } 
     44 
     45 // 在二叉树中插入一个数据域为 x 的新结点 
     46 void insert(node** root, int x) {
     47     if((*root) == NULL) {        // 空树,说明查找失败,即为插入位置 
     48         (*root) = newNode(x);    // 生成数据域为 x 的结点
     49         return; 
     50     } 
     51     if(x == (*root)->data) {    // 查找成功,说明数据已经存在 
     52         return;
     53     } else if(x < (*root)->data) {        // x 比根结点的数据域小,往左子树查找 
     54         insert(&(*root)->lchild, x);
     55     } else {
     56         insert(&(*root)->rchild, x);    // x 比根结点的数据域大,往右子树查找
     57     } 
     58 } 
     59 
     60 // 二叉查找树的建立 
     61 node* create(int data[], int n) {
     62     node* root = NULL;        // 新建根结点 root
     63     int i;
     64     for(i=0; i<n; ++i) {
     65         insert(&root, data[i]);        // 将 data 中数据依次插入二叉树 
     66     } 
     67     return root;            // 返回根结点 
     68 }
     69 
     70 // 寻找以 root 为根结点的树中的最大权值结点 
     71 node* findMax(node* root) {
     72     while(root->rchild != NULL) {
     73         root = root->rchild;        // 不断往右,直到没有右孩子 
     74     }
     75     return root;
     76 } 
     77 
     78 // 寻找以 root 为根结点的树中的最小权值结点 
     79 node* findMin(node* root) {
     80     while(root->lchild != NULL) {
     81         root = root->lchild;        // 不断往左,直到没有左孩子 
     82     }
     83     return root;    
     84 }
     85 
     86 void deleteNode(node** root, int x) {
     87     if((*root) == NULL) {
     88         return;            // 不存在数据为 x 的结点 
     89     }
     90     if((*root)->data == x) {        // 找到欲删除结点 
     91         if((*root)->lchild == NULL && (*root)->rchild == NULL) {
     92             // 叶子结点,直接删除 
     93             (*root) = NULL; 
     94         } else if((*root)->lchild != NULL) {    // 左子树不为空 
     95             node* pre = findMax((*root)->lchild);    // 找前驱结点
     96             (*root)->data = pre->data;                // 用前驱结点覆盖原结点 
     97             deleteNode(&(*root)->lchild, pre->data);    // 在左子树中删除前驱结点 
     98         } else {                                // 右子树不为空 
     99             node* next = findMin((*root)->rchild);    // 找后继结点
    100             (*root)->data = next->data;                // 用后继结点覆盖原结点 
    101             deleteNode(&(*root)->rchild, next->data);    // 在左子树中删除后继结点 
    102         } 
    103     } else if((*root)->data > x) {
    104         deleteNode(&(*root)->lchild, x);            // 在左子树删除 x 
    105     } else {
    106         deleteNode(&(*root)->rchild, x);            // 在右子树删除 x 
    107     }
    108 } 
    109 
    110 // 先序遍历
    111 void preorder(node* root) {
    112     if(root == NULL) {
    113         return;        // 空树,递归边界 
    114     }
    115     printf("%d
    ", root->data);        // 访问该结点
    116     preorder(root->lchild);            // 访问左子树 
    117     preorder(root->rchild);            // 访问右子树  
    118 } 
    119 
    120 int main() {
    121     int data[10] = {5, 1, 8, 0, 3, 6, 9, 2, 4, 7};
    122     node* root = NULL;
    123     root = create(data, 10);    // 创建二叉查找树 
    124     preorder(root);                // 先序遍历 
    125     search(root, 11);            // 查找数据域为 11 的结点 
    126     deleteNode(&root, 5);        // 删除数据域为 5 的结点 
    127     preorder(root);                // 先序遍历 
    128 
    129     return 0;
    130 }
    二叉查找树
  • 相关阅读:
    PAT (Basic Level) Practice (中文)1022 D进制的A+B
    PAT (Basic Level) Practice (中文)1001 害死人不偿命的(3n+1)猜想
    Pycharm的调试
    Pycharm自带Git实现版本管理
    JMeter分布式压测
    JMeter内存溢出:java.lang.OutOfMemoryError: Java heap space解决方法
    JMeter资源监控插件PerfMon的使用
    JMeter命令行执行+生成HTML报告
    JMeter压测“java.net.SocketException: Socket closed”解决方法
    JMeter压测“java.net.BindException: Address already in use: connect”解决方法
  • 原文地址:https://www.cnblogs.com/coderJiebao/p/Algorithmofnotes22.html
Copyright © 2020-2023  润新知