• 算法系列15天速成——第六天 五大经典查找【下】


    大家是否感觉到,树在数据结构中大行其道,什么领域都要沾一沾,碰一碰。

    就拿我们前几天学过的排序就用到了堆和今天讲的”二叉排序树“,所以偏激的说,掌握的树你就是牛人了。

    今天就聊聊这个”五大经典查找“中的最后一个”二叉排序树“。

    1. 概念:

         <1> 其实很简单,若根节点有左子树,则左子树的所有节点都比根节点小。

                                 若根节点有右子树,则右子树的所有节点都比根节点大。

         <2> 如图就是一个”二叉排序树“,然后对照概念一比较比较。

                  

             

    2.实际操作:

        我们都知道,对一个东西进行操作,无非就是增删查改,接下来我们就聊聊其中的基本操作。

        <1> 插入:相信大家对“排序树”的概念都清楚了吧,那么插入的原理就很简单了。

                        比如说我们插入一个20到这棵树中。

                                     首先:20跟50比,发现20是老小,不得已,得要归结到50的左子树中去比较。

                                     然后:20跟30比,发现20还是老小。

                                  再然后:20跟10比,发现自己是老大,随即插入到10的右子树中。

                                     最后: 效果呈现图如下:

                   

                   

        <2>查找:相信懂得了插入,查找就跟容易理解了。

                        就拿上面一幅图来说,比如我想找到节点10.

                                         首先:10跟50比,发现10是老小,则在50的左子树中找。

                                         然后:10跟30比,发现还是老小,则在30的左子树中找。

                                      再然后:  10跟10比,发现一样,然后就返回找到的信号。

                    

         <3>删除:删除节点在树中还是比较麻烦的,主要有三种情况。

                       《1》 删除的是“叶节点20“,这种情况还是比较简单的,删除20不会破坏树的结构。如图:

                        

                          

                       《2》删除”单孩子节点90“,这个情况相比第一种要麻烦一点点,需要把他的孩子顶上去。

                        

                           

                       《3》删除“左右孩子都有的节点50”,这个让我在代码编写上纠结了好长时间,问题很直白,

                               我把50删掉了,谁顶上去了问题,是左孩子呢?还是右孩子呢?还是另有蹊跷?这里我就

                               坦白吧,不知道大家可否知道“二叉树”的中序遍历,不过这个我会在后面讲的,现在可以当

                              公式记住吧,就是找到右节点的左子树最左孩子。

                              比如:首先 找到50的右孩子70。

                                      然后  找到70的最左孩子,发现没有,则返回自己。

                                      最后  原始图和最终图如下。 

      

    3.说了这么多,上代码说话。

      1 using System;
    2 using System.Collections.Generic;
    3 using System.Linq;
    4 using System.Text;
    5 using System.Diagnostics;
    6
    7 namespace TreeSearch
    8 {
    9 class Program
    10 {
    11 static void Main(string[] args)
    12 {
    13 List<int> list = new List<int>() { 50, 30, 70, 10, 40, 90, 80 };
    14
    15 //创建二叉遍历树
    16 BSTree bsTree = CreateBST(list);
    17
    18 Console.Write("中序遍历的原始数据:");
    19
    20 //中序遍历
    21 LDR_BST(bsTree);
    22
    23 Console.WriteLine("\n---------------------------------------------------------------------------n");
    24
    25 //查找一个节点
    26 Console.WriteLine("\n10在二叉树中是否包含:" + SearchBST(bsTree, 10));
    27
    28 Console.WriteLine("\n---------------------------------------------------------------------------n");
    29
    30 bool isExcute = false;
    31
    32 //插入一个节点
    33 InsertBST(bsTree, 20, ref isExcute);
    34
    35 Console.WriteLine("\n20插入到二叉树,中序遍历后:");
    36
    37 //中序遍历
    38 LDR_BST(bsTree);
    39
    40 Console.WriteLine("\n---------------------------------------------------------------------------n");
    41
    42 Console.Write("删除叶子节点 20, \n中序遍历后:");
    43
    44 //删除一个节点(叶子节点)
    45 DeleteBST(ref bsTree, 20);
    46
    47 //再次中序遍历
    48 LDR_BST(bsTree);
    49
    50 Console.WriteLine("\n****************************************************************************\n");
    51
    52 Console.WriteLine("删除单孩子节点 90, \n中序遍历后:");
    53
    54 //删除单孩子节点
    55 DeleteBST(ref bsTree, 90);
    56
    57 //再次中序遍历
    58 LDR_BST(bsTree);
    59
    60 Console.WriteLine("\n****************************************************************************\n");
    61
    62 Console.WriteLine("删除根节点 50, \n中序遍历后:");
    63 //删除根节点
    64 DeleteBST(ref bsTree, 50);
    65
    66 LDR_BST(bsTree);
    67
    68 }
    69
    70 ///<summary>
    71 /// 定义一个二叉排序树结构
    72 ///</summary>
    73 public class BSTree
    74 {
    75 public int data;
    76 public BSTree left;
    77 public BSTree right;
    78 }
    79
    80 ///<summary>
    81 /// 二叉排序树的插入操作
    82 ///</summary>
    83 ///<param name="bsTree">排序树</param>
    84 ///<param name="key">插入数</param>
    85 ///<param name="isExcute">是否执行了if语句</param>
    86 static void InsertBST(BSTree bsTree, int key, ref bool isExcute)
    87 {
    88 if (bsTree == null)
    89 return;
    90
    91 //如果父节点大于key,则遍历左子树
    92 if (bsTree.data > key)
    93 InsertBST(bsTree.left, key, ref isExcute);
    94 else
    95 InsertBST(bsTree.right, key, ref isExcute);
    96
    97 if (!isExcute)
    98 {
    99 //构建当前节点
    100 BSTree current = new BSTree()
    101 {
    102 data = key,
    103 left = null,
    104 right = null
    105 };
    106
    107 //插入到父节点的当前元素
    108 if (bsTree.data > key)
    109 bsTree.left = current;
    110 else
    111 bsTree.right = current;
    112
    113 isExcute = true;
    114 }
    115
    116 }
    117
    118 ///<summary>
    119 /// 创建二叉排序树
    120 ///</summary>
    121 ///<param name="list"></param>
    122 static BSTree CreateBST(List<int> list)
    123 {
    124 //构建BST中的根节点
    125 BSTree bsTree = new BSTree()
    126 {
    127 data = list[0],
    128 left = null,
    129 right = null
    130 };
    131
    132 for (int i = 1; i < list.Count; i++)
    133 {
    134 bool isExcute = false;
    135 InsertBST(bsTree, list[i], ref isExcute);
    136 }
    137 return bsTree;
    138 }
    139
    140 ///<summary>
    141 /// 在排序二叉树中搜索指定节点
    142 ///</summary>
    143 ///<param name="bsTree"></param>
    144 ///<param name="key"></param>
    145 ///<returns></returns>
    146 static bool SearchBST(BSTree bsTree, int key)
    147 {
    148 //如果bsTree为空,说明已经遍历到头了
    149 if (bsTree == null)
    150 return false;
    151
    152 if (bsTree.data == key)
    153 return true;
    154
    155 if (bsTree.data > key)
    156 return SearchBST(bsTree.left, key);
    157 else
    158 return SearchBST(bsTree.right, key);
    159 }
    160
    161 ///<summary>
    162 /// 中序遍历二叉排序树
    163 ///</summary>
    164 ///<param name="bsTree"></param>
    165 ///<returns></returns>
    166 static void LDR_BST(BSTree bsTree)
    167 {
    168 if (bsTree != null)
    169 {
    170 //遍历左子树
    171 LDR_BST(bsTree.left);
    172
    173 //输入节点数据
    174 Console.Write(bsTree.data + "");
    175
    176 //遍历右子树
    177 LDR_BST(bsTree.right);
    178 }
    179 }
    180
    181 ///<summary>
    182 /// 删除二叉排序树中指定key节点
    183 ///</summary>
    184 ///<param name="bsTree"></param>
    185 ///<param name="key"></param>
    186 static void DeleteBST(ref BSTree bsTree, int key)
    187 {
    188 if (bsTree == null)
    189 return;
    190
    191 if (bsTree.data == key)
    192 {
    193 //第一种情况:叶子节点
    194 if (bsTree.left == null && bsTree.right == null)
    195 {
    196 bsTree = null;
    197 return;
    198 }
    199 //第二种情况:左子树不为空
    200 if (bsTree.left != null && bsTree.right == null)
    201 {
    202 bsTree = bsTree.left;
    203 return;
    204 }
    205 //第三种情况,右子树不为空
    206 if (bsTree.left == null && bsTree.right != null)
    207 {
    208 bsTree = bsTree.right;
    209 return;
    210 }
    211 //第四种情况,左右子树都不为空
    212 if (bsTree.left != null && bsTree.right != null)
    213 {
    214 var node = bsTree.right;
    215
    216 //找到右子树中的最左节点
    217 while (node.left != null)
    218 {
    219 //遍历它的左子树
    220 node = node.left;
    221 }
    222
    223 //交换左右孩子
    224 node.left = bsTree.left;
    225
    226 //判断是真正的叶子节点还是空左孩子的父节点
    227 if (node.right == null)
    228 {
    229 //删除掉右子树最左节点
    230 DeleteBST(ref bsTree, node.data);
    231
    232 node.right = bsTree.right;
    233 }
    234 //重新赋值一下
    235 bsTree = node;
    236
    237 }
    238 }
    239
    240 if (bsTree.data > key)
    241 {
    242 DeleteBST(ref bsTree.left, key);
    243 }
    244 else
    245 {
    246 DeleteBST(ref bsTree.right, key);
    247 }
    248 }
    249 }
    250 }

    运行结果:

    值的注意的是:二叉排序树同样采用“空间换时间”的做法。

    突然发现,二叉排序树的中序遍历同样可以排序数组,呵呵,不错!

    PS:  插入操作:O(LogN)。

           删除操作:O(LogN)。

           查找操作:O(LogN)。

  • 相关阅读:
    [Unity] 如何通过 C# 代码控制角色动画的播放
    [Unity] Unity粒子系统报错: Ensure Read/Write is enabled on the Particle System's Texture.
    [Unity] Shader Graph Error 当前渲染管道与此主节点不兼容(The current render pipeline is not compatible with this master node)
    [运维] 请求 nginx 出现 502 Bad Gateway 的解决方案!
    [排错] SpringBoot 警告 Could not find acceptable representation
    [理解] C++ 中的 源文件 和 头文件
    [经验] 如何将 Java 项目发布到云服务器上并可以访问
    均衡与竞争
    指数退避算法
    判断两个多项式是否为同一个方程
  • 原文地址:https://www.cnblogs.com/huangxincheng/p/2265427.html
Copyright © 2020-2023  润新知