zoukankan      html  css  js  c++  java
  • PHP 二叉查找树

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

    说几个关键点

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

  • 相关阅读:
    【转】完全用Linux工作(王垠)
    PPPoE拨号设置
    sping mvc 结合 hibernate 实现用户登录功能(一)!
    MyEclipse + Maven开发Web工程的详细配置过程
    sping mvc 结合 hibernate 实现用户登录功能(三)!
    编程式事务
    struts2 结合extjs实现的一个登录实例
    tomcat使用总结
    使用Spring 2.5 和 Hibernate 3.2 开发MVC Web程序(基于annotation特性)
    如果你在买东西时
  • 原文地址:https://www.cnblogs.com/zemliu/p/2692405.html
Copyright © 2011-2022 走看看