二叉排序树(Binary Sort Tree),又称二叉查找树(Binary Search Tree),亦称二叉搜索树。
一、定义
二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
- 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
- 左、右子树也分别为二叉排序树;
如果按照中序遍历一个二叉排序树得到的是一个从小到大排好序的数据集。构造一棵二叉排序树的目的,并不是为了排序,而是为了提高查找和插入删除关键字的速度。不管怎么说,在一个有序数据集上的查找,速度总是要快于无序的数据集的,而二叉排序树这种非线性的结构,也有利于插入和删除的实现。
二、二叉排序树的查找
首先构建节点类,如下
1 <?php 2 /** 3 * Node.php 4 * Created on 2019/4/27 9:09 5 * Created by Wilin 6 */ 7 8 class Node { 9 public $data; 10 public $left = null; 11 public $right = null; 12 13 public function __construct($data) { 14 $this->data = $data; 15 } 16 }
中序遍历方法如下
1 <?php 2 /** 3 * Traverse.php 4 * Created on 2019/4/27 11:10 5 * Created by Wilin 6 */ 7 function midOrderTraverse($tree) { 8 if($tree == null) { 9 return; 10 } 11 12 midOrderTraverse($tree->left); 13 printf("%s ", $tree->data); 14 midOrderTraverse($tree->right); 15 }
二叉排序树类基本结构如下:
1 <?php 2 /** 3 * BinarySortedTree.php 4 * Created on 2019/4/27 11:03 5 * Created by Wilin 6 */ 7 8 include "Node.php"; 9 include "../Traverse.php"; 10 11 class BinarySortedTree 12 { 13 private $tree; 14 15 public function getTree() { 16 return $this->tree; 17 } 18 }
下面开始往二叉排序树中添加查找方法。
查找步骤:
- 若根结点的关键字值等于查找的关键字,成功。
- 否则,若小于根结点的关键字值,递归查左子树。
- 若大于根结点的关键字值,递归查右子树。
- 若子树为空,查找不成功。
根据该步骤,编写出如下查找代码
1 public function find(int $data) { 2 $p = $this->tree; 3 while ($p) { 4 if ($data < $p->data) { 5 $p = $p->left; 6 } elseif ($data > $p->data) { 7 $p = $p->right; 8 } else { 9 return $p; 10 } 11 } 12 return null; 13 }
三、二叉排序树的插入
插入步骤
- 首先执行查找算法,找出被插结点的父亲结点。
- 判断被插结点是其父亲结点的左、右儿子。将被插结点作为叶子结点插入。
- 若二叉树为空。则首先单独生成根结点。
注意:新插入的结点总是叶子结点。
根据插入步骤,编写出如下插入代码
1 public function insert(int $data) { 2 if (!$this->tree) { 3 $this->tree = new Node($data); 4 return true; 5 } 6 $p = $this->tree; 7 while ($p) { 8 if ($data < $p->data) { 9 if(!$p->left){ 10 $p->left = new Node($data); 11 return true; 12 } 13 $p = $p->left; 14 } elseif ($data > $p->data) { 15 if(!$p->right){ 16 $p->right = new Node($data); 17 return true; 18 } 19 $p = $p->right; 20 } else { 21 return false; 22 } 23 } 24 }
四、二叉排序树的删除
二叉排序树的删除相对而言要复杂一些,需要分三种情况来处理:
- 第一种情况是,如果要删除的节点没有子节点,直接将该结点删除就可以。表现在PHP中就是将父节点中指向要删除节点的指针置为 null。
- 第二种情况是,如果要删除的节点只有一个子节点,只需要将父节点中对子结点指针,指向要删除节点的子节点就可以了。
- 第三种情况是,如果要删除的节点有两个子节点,需要找到这个节点的右子树中的最小节点(或者左子树中的最大节点),把它替换到要删除的节点上。然后再删除掉这个最小(最大)节点,因为最小(最大)节点肯定没有左(右)子节点。
二叉排序树的删除代码如下:
1 public function delete(int $data) { 2 if( !$this->tree ) { 3 return; 4 } 5 6 $p = $this->tree; 7 $pp = null; 8 9 while ($p && $data != $p->data) { 10 $pp = $p; 11 if($data < $p->data) { 12 $p = $p->left; 13 } elseif ($data > $p->data) { 14 $p = $p->right; 15 } 16 } 17 18 if($p == null) { 19 return; 20 } 21 22 if($p->left && $p->right) { 23 $minP = $p->right; 24 $minPP = null; 25 while ($minP->left) { 26 $minPP = $minP; 27 $minP = $minP->left; 28 } 29 $p->data = $minP->data; 30 $p = $minP; 31 $pp = $minPP; 32 } 33 34 $child = null; 35 if ($p->left) { 36 $child = $p->left; 37 } elseif ($p->right) { 38 $child = $p->right; 39 } 40 41 if (!$pp) { 42 $this->tree = $child; 43 } elseif ($pp->left == $p){ 44 $pp->left = $child; 45 } else { 46 $pp->right = $child; 47 } 48 }
五、测试及结果
测试完整代码如下:
1 <?php 2 /** 3 * BinarySortedTree.php 4 * Created on 2019/4/27 11:03 5 * Created by Wilin 6 */ 7 8 include "Node.php"; 9 include "../Traverse.php"; 10 11 class BinarySortedTree 12 { 13 private $tree; 14 15 public function getTree() { 16 return $this->tree; 17 } 18 19 public function find(int $data) { 20 $p = $this->tree; 21 while ($p) { 22 if ($data < $p->data) { 23 $p = $p->left; 24 } elseif ($data > $p->data) { 25 $p = $p->right; 26 } else { 27 return $p; 28 } 29 } 30 return null; 31 } 32 33 public function insert(int $data) { 34 if (!$this->tree) { 35 $this->tree = new Node($data); 36 return true; 37 } 38 $p = $this->tree; 39 while ($p) { 40 if ($data < $p->data) { 41 if(!$p->left){ 42 $p->left = new Node($data); 43 return true; 44 } 45 $p = $p->left; 46 } elseif ($data > $p->data) { 47 if(!$p->right){ 48 $p->right = new Node($data); 49 return true; 50 } 51 $p = $p->right; 52 } else { 53 return false; 54 } 55 } 56 } 57 58 public function delete(int $data) { 59 if( !$this->tree ) { 60 return; 61 } 62 63 $p = $this->tree; 64 $pp = null; 65 66 while ($p && $data != $p->data) { 67 $pp = $p; 68 if($data < $p->data) { 69 $p = $p->left; 70 } elseif ($data > $p->data) { 71 $p = $p->right; 72 } 73 } 74 75 if($p == null) { 76 return; 77 } 78 79 if($p->left && $p->right) { 80 $minP = $p->right; 81 $minPP = null; 82 while ($minP->left) { 83 $minPP = $minP; 84 $minP = $minP->left; 85 } 86 $p->data = $minP->data; 87 $p = $minP; 88 $pp = $minPP; 89 } 90 91 $child = null; 92 if ($p->left) { 93 $child = $p->left; 94 } elseif ($p->right) { 95 $child = $p->right; 96 } 97 98 if (!$pp) { 99 $this->tree = $child; 100 } elseif ($pp->left == $p){ 101 $pp->left = $child; 102 } else { 103 $pp->right = $child; 104 } 105 } 106 } 107 108 $tree = new BinarySortedTree(); 109 $tree->insert(1); 110 $tree->insert(3); 111 $tree->insert(4); 112 $tree->insert(6); 113 $tree->insert(6); 114 print "查找4============= "; 115 print_r($tree->find(4)); 116 print "遍历============== "; 117 midOrderTraverse($tree->getTree()); 118 print "删除4============= "; 119 $tree->delete(4); 120 print "查找4============= "; 121 print_r($tree->find(4)); 122 print "遍历============== "; 123 midOrderTraverse($tree->getTree());
结果如下:
E:www ree1>php BinarySortedTree.php 查找4============= Node Object ( [data] => 4 [left] => [right] => Node Object ( [data] => 6 [left] => [right] => ) ) 遍历============== 1 3 4 6 删除4============= 查找4============= 遍历============== 1 3 6