二叉排序树C#
寒假回家的时候,看了一下C#语言描述版的数据结构。我只是单纯的想,更加熟悉计算机语言,我希望能像是用母语一样使用计算机语言和计算机交流。很 奇怪之前学C语言版本的数据结构,那些算法我都不是非常理解,但在读这本书的时候,却有种豁然开朗的感觉。是书写的好还是我能力有所提高亦或是两者皆有?
进入正题。
二叉排序树说起来其实并不是很难。二叉查找树是一种把值比较小的节点存储在左子节点内而值比较大的节点存储在右子节点里的树。其基本操作有以下几种:
插入
我们对这个二叉树插入节点。如果二叉树本身没有任何节点,那么插入的这个节点就成为根节点;否则,根据定义,你应该遍历树,找出某一个节点,如果带 插入节点的值大于这个节点,则成为带插入节点的右节点,否则就是左节点。从这里看来,根节点本身就是一个树的节点。因此,我首先实现了一个 TreeNode类型,如下:
1: public class TreeNode<T>:IComparable,IComparable<TreeNode<T>> { 2: 3: #region .ctor 串联构造函数 4: 5: public TreeNode():this(default(T),null,null,0){ 6: } 7: 8: public TreeNode(T value):this(value,null,null,1) { 9: } 10: 11: public TreeNode(T value,TreeNode<T> leftNode,TreeNode<T> rightNode):this(value,leftNode,rightNode,1) { 12: } 13: 14: public TreeNode(T value, TreeNode<T> leftNode, TreeNode<T> rightNode, int deepness) { 15: Value = value; 16: LeftNode = leftNode; 17: RightNode = rightNode; 18: Deepness = deepness; 19: IsLeaf = true; 20: } 21: 22: public override string ToString() { 23: return Value.ToString(); 24: } 25: 26: #endregion 27: 28: #region Interface Members 29: 30: int IComparable.CompareTo(Object item) { 31: TreeNode<T> node = item as TreeNode<T>; 32: if (node == null) 33: throw new ArgumentException("Argument for comparing is not a object of TreeNode Type !!"); 34: else { 35: if (this == item) 36: return 0; 37: else { 38: Comparer comparer = new Comparer(CultureInfo.CurrentCulture); 39: return comparer.Compare(this.Value, node.Value); 40: } 41: } 42: } 43: 44: int IComparable<TreeNode<T>>.CompareTo(TreeNode<T> item) { 45: if (item == null) { 46: throw new ArgumentException("Argument for comparing is not a object of TreeNode Type !!"); 47: } else { 48: if (this == item) 49: return 0; 50: else { 51: Comparer comparer = new Comparer(CultureInfo.CurrentCulture); 52: return comparer.Compare(this.Value, item.Value); 53: } 54: } 55: } 56: #endregion 57: 58: #region Properties 59: 60: public TreeNode<T> LeftNode { 61: get; 62: set; 63: } 64: 65: public TreeNode<T> RightNode { 66: get; 67: set; 68: } 69: 70: public TreeNode<T> FatherNode { 71: get; 72: set; 73: } 74: 75: //这个属性是指数的层数,也就是深度,根的深度为1 76: public int Deepness { 77: get; 78: set; 79: } 80: 81: //这个属性指示这个节点是不是叶子节点 82: public bool IsLeaf { 83: get; 84: set; 85: } 86: 87: //这个属性返回或者设置该节点的值 88: public T Value { 89: get; 90: set; 91: } 92: 93: #endregion 94: }
接下来我们就能实现插入的功能了,通常我觉得好的代码是自描述的。也就是一段好的代码应该能自己描述自己的用途的。
1: public void Add(T itemValue) { 2: if (Root == null) { 3: TreeNode<T> root = new TreeNode<T>(itemValue); 4: this.Root = root; 5: this.Count++; 6: } else { 7: TreeNode<T> _iterator = this.Root; 8: bool okFlag = true;; 9: int deepness = 2; 10: int result = _comparer.Compare(itemValue, _iterator.Value); ; 11: 12: while (okFlag) { 13: Debug.WriteLine("Comaper Result is :" + result.ToString()); 14: Debug.WriteLine("---------------------------------------"); 15: if (result > 0) { 16: if (_iterator.RightNode != null) { 17: _iterator = _iterator.RightNode; 18: result = _comparer.Compare(itemValue, _iterator.Value); 19: deepness++; 20: } else { 21: //在添加节点的时候就设置该节点的深度,而在计算整棵树的深度的时候,其实只要对所有叶节点的深度的值进行排序就可以得到了 22: _iterator.RightNode = new TreeNode<T>(itemValue,null,null,deepness); 23: _iterator.IsLeaf = false; 24: _iterator.RightNode.FatherNode = _iterator; 25: Count++; 26: okFlag = false; 27: } 28: } else if (result < 0) { 29: if (_iterator.LeftNode != null) { 30: _iterator = _iterator.LeftNode; 31: result = _comparer.Compare(itemValue, _iterator.Value); 32: deepness++; 33: } else { 34: _iterator.LeftNode = new TreeNode<T>(itemValue, null, null, deepness); 35: _iterator.IsLeaf = false; 36: _iterator.LeftNode.FatherNode = _iterator; 37: Count++; 38: okFlag = false; 39: } 40: } else 41: okFlag = false; 42: } 43: } 44: }
这里在比较的时候,我是在全局设置了一个与本地文化相关的Comparer类型的私有成员_comparer。这个类型用于对象判等。关键是你要判断的对象必须实现IComparable 接口。我编写的TreeNode类型就实现了这个接口了。
查找
根据二叉搜索树的特点,如果你要搜索的节点的值比当前节点值小,那么就再搜索该节点的左子树;否则,搜索右子树。这个过程是递归过程。
如果找到匹配的节点,返回ture;否则,当前节点为Null,然后又不等于你要搜索的节点的值,那么就直接返回false。我的实现如下:
1: public bool IsExit(T key,out TreeNode<T> node) { 2: node = null; 3: TreeNode<T> _iterator = Root; 4: int result = _comparer.Compare(key, _iterator.Value); 5: bool okFlag = true; 6: while (okFlag) { 7: if (result == 0) { 8: okFlag = false; 9: node = _iterator;//如果找到这个叶子结点,那么得到叶子节点的指针 10: return true; 11: } else if (result > 0) { 12: _iterator = _iterator.RightNode; 13: if (_iterator != null) 14: result = _comparer.Compare(key, _iterator.Value); 15: else { 16: okFlag = false; 17: return false; 18: } 19: } else { 20: _iterator = _iterator.LeftNode; 21: if (_iterator != null) 22: result = _comparer.Compare(key, _iterator.Value); 23: else { 24: okFlag = false; 25: return false; 26: } 27: } 28: } 29: return false; 30: }
遍历
这个分三种情况的遍历,分别是前根遍历,中根遍历,后根遍历。其实很简单,就是按照访问节点的顺序来划分的。如果先访问左子节点,然后访问父节点,最后在访问右节点,这个情况就是前序遍历。其他的情况以此类推。这里我实现的时候,是用递归的方式。
1: /// <summary> 2: /// 中根遍历树 3: /// </summary> 4: /// <param name="root"></param> 5: public void InOrder(TreeNode<T> root) { 6: if (root != null) { 7: InOrder(root.LeftNode); 8: Console.WriteLine(string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString())); 9: InOrder(root.RightNode); 10: } 11: } 12: 13: /// <summary> 14: /// 先根遍历树 15: /// </summary> 16: /// <param name="root"></param> 17: public void PreOrder(TreeNode<T> root) { 18: if (root != null) { 19: Console.WriteLine(string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString())); 20: PreOrder(root.LeftNode); 21: PreOrder(root.RightNode); 22: } 23: } 24: 25: /// <summary> 26: /// 后根遍历树 27: /// </summary> 28: /// <param name="root"></param> 29: public void PostOrder(TreeNode<T> root) { 30: if (root != null) { 31: PostOrder(root.LeftNode); 32: PostOrder(root.RightNode); 33: string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString()); 34: } 35: } 删除节点 作为这个树的实现里面最有难度的地方,要考虑三种情况。 1、要删除的节点时叶子结点;这个情况很好解决,直接删掉就好了; 2、要删除的节点只有一个子节点;这个情况也很好解决,子节点替换一下父节点,问题解决; 2、要删除的节点有两个节点。这个就比较复杂了一些,但我们仔细分析,就会发现,要删除这个节点,只要利用中根遍历得到直接前驱或者直接后继结点代替该节点就可以了。同时,你还要删除前驱或者后继这个节点,而这两个节点,一定符合前面的两种情况之一。 1: public void Remove(T key) { 2: TreeNode<T> node; 3: bool isExit = IsExit(key, out node); 4: if (!isExit) { 5: return; 6: } else { 7: if (IsLeafNode(node)) { 8: if (node.FatherNode.LeftNode == node) 9: node.FatherNode.LeftNode = null; 10: else 11: node.FatherNode.RightNode = null; 12: if (node != null) node = null; 13: } else { 14: if (!HasTwoLeafNodes(node)) { 15: TreeNode<T> child = GetUniqueChild(node); 16: if (node.FatherNode.LeftNode == node) { 17: node.FatherNode.LeftNode = child; 18: } else { 19: if (node.LeftNode != null) 20: node.FatherNode.RightNode = child; 21: } 22: if (node != null) node = null; 23: } else { 24: //首先找到后继结点 25: TreeNode<T> successor = GetSuccessor(node); 26: //调整节点值,这个时候有一个值得注意的地方,比如你的树是这样的情况: 27: /* 45 或者: 50 28: * \ / 29: * 52 25 30: * / \ / \ 31: * / \ 15 28 32: * 49 57 / \ / 33: * / \ / \ 7 8 26 34: * 46 50 55 58 35: * \ 36: * 56 37: */ 38: //树A中节点52相应的后继结点应该是55,那么57的左子节点应调整成56,52就要调整成55 39: node.Value = successor.Value; 40: Remove(successor); 41: } 42: } 43: this.Count--; 44: } 45: }
以下是完整的代码,尽管是泛型,但其实我仅仅使用int类型编写 过测试。如果其他的类型,我想只要是实现了IComparable接口,应该就能正常工作。
1: using System; 2: using System.Collections; 3: using System.Collections.Generic; 4: using System.Globalization; 5: using System.Diagnostics; 6: using System.Text; 7: 8: namespace Ultis { 9: public class BinaryTree<T> { 10: #region .ctor 11: 12: static BinaryTree() { 13: _comparer = new Comparer(CultureInfo.CurrentCulture); 14: } 15: 16: public BinaryTree() { 17: this.Root = null; 18: this.Count = 0; 19: this._deepness = 0; 20: } 21: 22: public BinaryTree(TreeNode<T> root) { 23: if (root == null) { 24: throw new ArgumentException("Root can not be null !!"); 25: } 26: this.Root = root; 27: this.Count++; 28: this._deepness = 1; 29: } 30: #endregion 31: 32: #region Public Members 33: 34: /// <summary> 35: /// 36: /// </summary> 37: /// <param name="itemValue"></param> 38: public void Add(T itemValue) { 39: if (Root == null) { 40: TreeNode<T> root = new TreeNode<T>(itemValue); 41: this.Root = root; 42: this.Count++; 43: } else { 44: TreeNode<T> _iterator = this.Root; 45: bool okFlag = true;; 46: int deepness = 2; 47: int result = _comparer.Compare(itemValue, _iterator.Value); ; 48: 49: while (okFlag) { 50: Debug.WriteLine("Comaper Result is :" + result.ToString()); 51: Debug.WriteLine("---------------------------------------"); 52: if (result > 0) { 53: if (_iterator.RightNode != null) { 54: _iterator = _iterator.RightNode; 55: result = _comparer.Compare(itemValue, _iterator.Value); 56: deepness++; 57: } else { 58: //在添加节点的时候就设置该节点的深度,而在计算整棵树的深度的时候,其实只要对所有叶节点的深度的值进行排序就可以得到了 59: _iterator.RightNode = new TreeNode<T>(itemValue,null,null,deepness); 60: _iterator.IsLeaf = false; 61: _iterator.RightNode.FatherNode = _iterator; 62: Count++; 63: okFlag = false; 64: } 65: } else if (result < 0) { 66: if (_iterator.LeftNode != null) { 67: _iterator = _iterator.LeftNode; 68: result = _comparer.Compare(itemValue, _iterator.Value); 69: deepness++; 70: } else { 71: _iterator.LeftNode = new TreeNode<T>(itemValue, null, null, deepness); 72: _iterator.IsLeaf = false; 73: _iterator.LeftNode.FatherNode = _iterator; 74: Count++; 75: okFlag = false; 76: } 77: } else 78: okFlag = false; 79: } 80: } 81: } 82: 83: /// <summary> 84: /// 从树中移出一个节点,要考虑很多情况,比如要删除的节点没有子节点,有一个子节点,有两个子节点 85: /// PS:这个方法应进行测试 86: /// </summary> 87: /** 88: * 删除指定的节点实现 89: * 90: * 算法思想: 91: * 92: * 1、若待删除的节点p是叶子节点,则直接删除该节点; 93: * 94: * 2、若待删除的节点p只有一个子节点,则将p的子节点与p的父节点直接连接,然后删除节点p; 95: * 为什么只有一个子节点时可以直接接到删除节点的父节点下面呢?因为只有一个子节点,直接接上 96: * 去不会影响排序子节点本身的排序,当然更不会影响另外一个子树(因为另一子树跟本不存在!); 97: * 98: * 3、若待删除节点p有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点s 99: * 的值来代替点s的值,然后删除节点s,(注:节点s肯定属于上述1、2情况之一)而不是直接删除 100: * p,这样可以将该删除问题转换成上面1、2问题; 101: */ 102: public void Remove(T key) { 103: TreeNode<T> node; 104: bool isExit = IsExit(key, out node); 105: if (!isExit) { 106: return; 107: } else { 108: if (IsLeafNode(node)) { 109: if (node.FatherNode.LeftNode == node) 110: node.FatherNode.LeftNode = null; 111: else 112: node.FatherNode.RightNode = null; 113: if (node != null) node = null; 114: } else { 115: if (!HasTwoLeafNodes(node)) { 116: TreeNode<T> child = GetUniqueChild(node); 117: if (node.FatherNode.LeftNode == node) { 118: node.FatherNode.LeftNode = child; 119: } else { 120: if (node.LeftNode != null) 121: node.FatherNode.RightNode = child; 122: } 123: if (node != null) node = null; 124: } else { 125: //首先找到后继结点 126: TreeNode<T> successor = GetSuccessor(node); 127: //调整节点值,这个时候有一个值得注意的地方,比如你的树是这样的情况: 128: /* 45 或者: 50 129: * \ / 130: * 52 25 131: * / \ / \ 132: * / \ 15 28 133: * 49 57 / \ / 134: * / \ / \ 7 8 26 135: * 46 50 55 58 136: * \ 137: * 56 138: */ 139: //树A中节点52相应的后继结点应该是55,那么57的左子节点应调整成56,52就要调整成55 140: node.Value = successor.Value; 141: Remove(successor); 142: } 143: } 144: this.Count--; 145: } 146: } 147: 148: 149: /** 150: * 删除指定的节点实现 151: * 152: * 算法思想: 153: * 154: * 1、若待删除的节点p是叶子节点,则直接删除该节点; 155: * 156: * 2、若待删除的节点p只有一个子节点,则将p的子节点与p的父节点直接连接,然后删除节点p; 157: * 为什么只有一个子节点时可以直接接到删除节点的父节点下面呢?因为只有一个子节点,直接接上 158: * 去不会影响排序子节点本身的排序,当然更不会影响另外一个子树(因为另一子树跟本不存在!); 159: * 160: * 3、若待删除节点p有两个子节点时,应该使用中序遍历方式得到的直接前置节点S或直接后继节点s 161: * 的值来代替点s的值,然后删除节点s,(注:节点s肯定属于上述1、2情况之一)而不是直接删除 162: * p,这样可以将该删除问题转换成上面1、2问题; 163: */ 164: public void Remove(TreeNode<T> node) { 165: if (IsLeafNode(node)) { 166: if (node.FatherNode.LeftNode == node) 167: node.FatherNode.LeftNode = null; 168: else 169: node.FatherNode.RightNode = null; 170: if (node != null) node = null; 171: } else { 172: if (!HasTwoLeafNodes(node)) { 173: TreeNode<T> child = GetUniqueChild(node); 174: if (node.FatherNode.LeftNode == node) { 175: node.FatherNode.LeftNode = child; 176: } else { 177: node.FatherNode.RightNode = child; 178: } 179: if(node != null) node = null; 180: } else { 181: //要删除的节点有两个子节点 182: TreeNode<T> successor = GetSuccessor(node); 183: node.Value = successor.Value; 184: Remove(successor); //删除相应的后继结点 185: if (successor != null) successor = null; 186: } 187: } 188: this.Count--; 189: } 190: 191: /// <summary> 192: /// 返回某一节点唯一的一个节点 193: /// </summary> 194: /// <param name="node"></param> 195: /// <returns></returns> 196: private TreeNode<T> GetUniqueChild(TreeNode<T> node) { 197: TreeNode<T> child; 198: if (node.LeftNode != null) 199: child = node.LeftNode; 200: else 201: child = node.RightNode; 202: return child; 203: } 204: 205: /// <summary> 206: /// 根据中根遍历来得到相应的后继结点,仍然还有BUG存在! 207: /// </summary> 208: /// <param name="value"></param> 209: /// <returns></returns> 210: public TreeNode<T> GetSuccessor(T value) { 211: throw new NotImplementedException("This function is not complete yet !"); 212: IComparable icomparable = Root as IComparable; 213: TreeNode<T> wanted = new TreeNode<T>(value); 214: if (icomparable.CompareTo(wanted) == 0) { 215: return Root; 216: } else { 217: TreeNode<T> node; 218: if (IsExit(value, out node)) { 219: if (IsLeafNode(node)) { //如果是叶子节点,那么应该做么做才能返回正确的后继节点? 220: return null; 221: }else 222: return GetSuccessor(node); //如果是非叶子节点,则调用相应的方法返回非叶子节点的后继结点 223: } else 224: return null; 225: } 226: } 227: 228: /// <summary> 229: /// 中根遍历树 230: /// </summary> 231: /// <param name="root"></param> 232: public void InOrder(TreeNode<T> root) { 233: if (root != null) { 234: InOrder(root.LeftNode); 235: Console.WriteLine(string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString())); 236: InOrder(root.RightNode); 237: } 238: } 239: 240: /// <summary> 241: /// 先根遍历树 242: /// </summary> 243: /// <param name="root"></param> 244: public void PreOrder(TreeNode<T> root) { 245: if (root != null) { 246: Console.WriteLine(string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString())); 247: PreOrder(root.LeftNode); 248: PreOrder(root.RightNode); 249: } 250: } 251: 252: /// <summary> 253: /// 后根遍历树 254: /// </summary> 255: /// <param name="root"></param> 256: public void PostOrder(TreeNode<T> root) { 257: if (root != null) { 258: PostOrder(root.LeftNode); 259: PostOrder(root.RightNode); 260: string.Format("This node's value is {0} and it's deepness is {1},leaf:{2}", root.ToString(), root.Deepness.ToString(), root.IsLeaf.ToString()); 261: } 262: } 263: 264: /// <summary> 265: /// 返回具有最大节点值的树节点 266: /// </summary> 267: /// <returns></returns> 268: public TreeNode<T> GetMaxNode() { 269: TreeNode<T> _iterator = Root; 270: while (_iterator.RightNode != null) { 271: _iterator = _iterator.RightNode; 272: } 273: return _iterator; 274: } 275: 276: /// <summary> 277: /// 返回最大的值 278: /// </summary> 279: /// <returns></returns> 280: public T GetMaxValue() { 281: return GetMaxNode().Value; 282: } 283: 284: /// <summary> 285: /// 返回具有最小节点值得节点 286: /// </summary> 287: /// <returns></returns> 288: public TreeNode<T> GetMinNode() { 289: TreeNode<T> _iterator = Root; 290: while (_iterator.LeftNode != null) { 291: _iterator = _iterator.LeftNode; 292: } 293: return _iterator; 294: } 295: 296: /// <summary> 297: /// 返回最小值 298: /// </summary> 299: /// <returns></returns> 300: public T GetMinValue() { 301: return GetMinNode().Value; 302: } 303: 304: public bool IsExit(T key,out TreeNode<T> node) { 305: node = null; 306: TreeNode<T> _iterator = Root; 307: int result = _comparer.Compare(key, _iterator.Value); 308: bool okFlag = true; 309: while (okFlag) { 310: if (result == 0) { 311: okFlag = false; 312: node = _iterator;//如果找到这个叶子结点,那么得到叶子节点的指针 313: return true; 314: } else if (result > 0) { 315: _iterator = _iterator.RightNode; 316: if (_iterator != null) 317: result = _comparer.Compare(key, _iterator.Value); 318: else { 319: okFlag = false; 320: return false; 321: } 322: } else { 323: _iterator = _iterator.LeftNode; 324: if (_iterator != null) 325: result = _comparer.Compare(key, _iterator.Value); 326: else { 327: okFlag = false; 328: return false; 329: } 330: } 331: } 332: return false; 333: } 334: 335: public override string ToString() { 336: string rtnString = string.Format("This is a kind of binary tree that impleted by Master, and it has {0} nodes.", Count.ToString()); 337: return rtnString; 338: } 339: 340: public TreeNode<T> Root { 341: get; 342: set; 343: } 344: 345: public int Count { 346: get; 347: private set; 348: } 349: 350: //返回树的深度 351: public int Deepness { 352: get { 353: if (Count == 1) 354: return 1; 355: else { 356: int[] _deepnessSortArray = new int[Count]; 357: int pointer = Count - 1; 358: for (int i = 0; i < Count; i++) _deepnessSortArray[i] = 0; 359: InOrder(Root, ref pointer, ref _deepnessSortArray); 360: Array.Sort<int>(_deepnessSortArray); //对_deepnessSortArray进行排序,得出其中最大的数值就是该树的深度 361: return _deepnessSortArray[Count - 1]; 362: } 363: } 364: } 365: 366: #endregion 367: 368: #region Private Members 369: 370: private static Comparer _comparer; 371: 372: private int _deepness; 373: 374: /// <summary> 375: /// 遍历树节点,然后给深度数组_deepnessSortArray[i]赋值,之后对这个数组进行排序,得到的最大值就是该树的深度 376: /// </summary> 377: /// <param name="root"></param> 378: /// <param name="pointer"></param> 379: /// <param name="deepnessSortArray"></param> 380: private void InOrder(TreeNode<T> root,ref int pointer,ref int[] deepnessSortArray) { 381: if (root != null) { 382: InOrder(root.LeftNode,ref pointer,ref deepnessSortArray); 383: deepnessSortArray[pointer] = root.Deepness; 384: pointer--; 385: InOrder(root.RightNode,ref pointer, ref deepnessSortArray); 386: } 387: } 388: 389: /// <summary> 390: /// 基于中根遍历的算法来得到某一个非叶子节点的后继结点 391: /// </summary> 392: /// <param name="wannaDelNode"></param> 393: private TreeNode<T> GetSuccessor(TreeNode<T> wannaDelNode) { 394: TreeNode<T> _iterator = wannaDelNode.RightNode; 395: TreeNode<T> successorFather = null; 396: TreeNode<T> successor = null; 397: 398: //Debug.WriteLine(string.Format("_iterator's value is {0}", _iterator.Value.ToString())); 399: //Debug.WriteLine(string.Format("successor's value is {0}", successor.Value.ToString())); 400: //首先应该要判断是不是叶子节点,如果是叶子节点,情况就完全不一样了 401: while (_iterator != null) { 402: successorFather = _iterator.FatherNode; 403: successor = _iterator; 404: _iterator = _iterator.LeftNode; 405: } 406: return successor; 407: } 408: 409: private bool IsLeafNode(TreeNode<T> node) { 410: return (node.LeftNode == null && node.RightNode == null) ? true : false; 411: } 412: 413: private bool HasTwoLeafNodes(TreeNode<T> node) { 414: return (node.LeftNode != null && node.RightNode != null) ? true : false; 415: } 416: 417: #endregion 418: } 419: }