二叉查找树:简单点说就是颗做孩子小,右孩子大的树

说几个关键点

 

最小值:总是树的最左节点的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 

posted on 2012-09-19 00:08  ZimZz  阅读(1109)  评论(0编辑  收藏  举报