二叉查找树:简单点说就是颗做孩子小,右孩子大的树
说几个关键点
最小值:总是树的最左节点的key
最大值:总是树的最右节点的key
前趋:按照中序遍历的顺序,遍历输出时当前节点的前一个节点
如果当前节点有左子节点,前驱就是当前节点的左边的最右节点,也可以认为是以当前节点的左孩子为根的树的最大值
如果当前节点没有左子节点,前驱就是找到一个父节点,使得当前节点位于该父节点的右边,不明白的看代码
后继:按照中序遍历的顺序,遍历输出时当前节点的后一个节点
如果当前节点有右子节点,后继就是当前节点的右边的最左节点,也可以认为是以当前节点的右孩子为根的树的最小值
如果当前节点没有柚子节点,后继就是找到一个父节点,使得当前节点位于该父节点的左边
插入节点:插入节点的思想是从root开始用当前节点的key值比较待插入节点的key值,待插入key比当前节点key小,则当前节点变为当前节点左孩子,否则变为当前节点右孩子,继续比较key,直到当前节点为null(到达树底部),记下比较完的最后一个叶子节点为P,如果P不为空,比较这个叶子节点和待插入节点的key,待插入节点小则作为这个叶子节点的左孩子,否则作为他的右孩子,如果P为空,说明这是一个空树,直接将待插入节点作为根返回即可
删除节点:删除节点分三种情况
1)如果待删除节点没有孩子,直接将节点删除即可。
2)如果待删除节点只有一个孩子,将待删除节点删除,并连接待删除节点的孩子和父亲,原来的左孩子还是作为其父节点的左孩子,原来的右孩子还是作为其父亲的右孩子
3)如果待删除节点有两个孩子,则找到待删除节点的直接后继,将后继删除(直接后继肯定只有一个子节点,要不然不可能是一个直接后继),然后按照2)连接后继的子节点和父节点,最后交换待删除节点和后继节点的key值
下面是代码和输出结果
1 <?php 2 #二叉查找树实现 3 #节点 4 class Node { 5 public $key = null; 6 public $parent = null; 7 public $left = null; 8 public $right = null; 9 } 10 11 #查找操作 12 function search($root, $k) { 13 $cnode = $root; 14 while ($cnode != null) { 15 if ($cnode->key == $K) { 16 return $cnode; 17 } else if ($cnode->key > $k) { 18 $cnode = $cnode->left; 19 } else { 20 $cnode = $cnode->right; 21 } 22 } 23 24 return $cnode; 25 } 26 27 #查找最小关键字 28 function search_minimum($root) { 29 $cnode = $root; 30 while ($cnode->left != null) { 31 $cnode = $cnode->left; 32 } 33 return $cnode; 34 } 35 36 #查找最大关键字 37 function search_maximum($root) { 38 $cnode = $root; 39 while ($cnode->right != null) { 40 $cnode = $cnode->right; 41 } 42 return $cnode; 43 } 44 45 #查找中序遍历前驱节点 46 function predecessor($x) { 47 if ($x->left !== null) { #左子结点存在,直接返回左子结点的最右子节点 48 return search_maximum($x->left); 49 } 50 #否则查找其父节点,直到当前节点位于父节点的右边 51 $p = $x->parent; 52 while ($p !== null && $x == $p->left) { #如果x是p的左孩子,说明p是x的后继,我们需要找的是p是x的前驱 53 $x = $p; 54 $p = $p->parent; 55 } 56 return $p; 57 } 58 59 #查找中序遍历的后继结点 60 function successor($x) { 61 if ($x->right !== null) { 62 return search_minimum($x->right); 63 } 64 $p = $x->parent; 65 while ($p !== null && $x = $p->right) { 66 $x = $p; 67 $p = $p->parent; 68 } 69 70 return $p; 71 } 72 73 #插入结点 74 function insert($root, $inode) { 75 $cnode = $root; 76 $pnode = null; 77 while ($cnode !== null) { #为inode找到合适的插入位置 78 $pnode = $cnode; 79 if ($cnode->key > $inode->key) { 80 $cnode = $cnode->left; 81 } else { 82 $cnode = $cnode->right; 83 } 84 } 85 86 $inode->parent = $pnode; 87 if ($pnode === null) { #pnode == null,说明是空树 88 $root = $inode; 89 } else { 90 if ($pnode->key > $inode->key) { 91 $pnode->left = $inode; 92 } else { 93 $pnode->right = $inode; 94 } 95 } 96 // print_r($root); 97 // echo "<br>"; 98 } 99 100 #删除结点 101 function delete($root, $dnode) { 102 if ($dnode-> left === null || $dnode->right === null) { #如果待删除结点无子节点或只有一个子节点,则c = dnode 103 $c = $dnode; 104 } else { #如果待删除结点有两个子节点,c置为dnode的直接后继,以待最后将待删除结点的值换为其后继的值 105 $c = successor($dnode); 106 } 107 108 if ($c->left !== null) { 109 $s = $c->left; 110 } else { 111 $s = $c->right; 112 } 113 114 if ($s !== null) { #将c的子节点的父母结点置为c的父母结点,此处c只可能有1个子节点,因为如果c有两个子节点,则c不可能是dnode的直接后继 115 $s->parent = $c->parent; 116 } 117 118 if ($c->parent === null) { #如果c的父母为空,说明c=dnode是根节点,删除根节点后直接将根节点置为根节点的子节点,此处dnode是根节点,且拥有两个子节点,则c是dnode的后继结点,c的父母就不会为空,就不会进入这个if 119 $root = $s; 120 } else if ($c == $c->parent->left) { #如果c是其父节点的左右子节点,则将c父母的左右子节点置为c的左右子节点 121 $c->parent->left = $s; 122 } else { 123 $c->parent->right = $s; 124 } 125 126 #如果c!=dnode,说明c是dnode的后继结点,交换c和dnode的key值 127 if ($c != $dnode) { 128 $dnode->key = $c->key; 129 } 130 131 #返回根节点 132 return $root; 133 } 134 135 #使用数组构造二叉查找树 136 function build_iterative_tree($arr) { 137 $root = new Node(); 138 $root->key = $arr[0]; 139 for ($i = 1; $i < count($arr); $i++) { 140 $new_node = new Node(); 141 $new_node->key = $arr[$i]; 142 insert($root, $new_node); 143 } 144 return $root; 145 } 146 147 #二叉查找树中序遍历 148 function inorder_traverse($root) { 149 if ($root->left !== null) { 150 inorder_traverse($root->left); 151 } 152 153 echo $root->key . " "; 154 155 if ($root->right !== null) { 156 inorder_traverse($root->right); 157 } 158 } 159 160 $arr = array(55, 1, 5, 9, 3, 4, 2, 2, 7, 8, 8, 0, 1); 161 $root = build_iterative_tree($arr); 162 inorder_traverse($root); 163 echo "<br>"; 164 echo search_maximum($root)->key; 165 echo "<br>"; 166 echo search_minimum($root)->key; 167 echo "<br>"; 168 $inode = new Node(); 169 $inode->key = 99; 170 insert($root, $inode); 171 inorder_traverse($root); 172 echo "<br>"; 173 delete($root, $root); 174 inorder_traverse($root); 175 echo "<br>"; 176 ?>
0 1 1 2 2 3 4 5 7 8 8 9 55
55
0
0 1 1 2 2 3 4 5 7 8 8 9 55 99
0 1 1 2 2 3 4 5 7 8 8 9 99