За последние 24 часа нас посетили 18355 программистов и 1634 робота. Сейчас ищут 1604 программиста ...

Двоичное (или бинарное) дерево поиска на PHP

Тема в разделе "Решения, алгоритмы", создана пользователем Professor, 26 авг 2010.

  1. Professor

    Professor Активный пользователь

    С нами с:
    2 янв 2008
    Сообщения:
    121
    Симпатии:
    0
    Адрес:
    г. Одесса, Украина
    Всем привет !

    Недавно (если быть точным - вчера) я задался вопросом, что же такое бинарное дерево поиска, как оно реализуется, какова логика и где это применимо, в частности, применимо ли это в PHP программировании.

    Порыскал по интернету, нашёл теоретическое описание (очень хорошее даёт википедия), и пример формирования его на различных языках. Вот чего я не смог найти - так это реализации его на PHP. Возможно это связано с тем, что дереву просто нет применения в PHP программировании - я не знаю. Но понять что это такое без примера понятной реализации (а в полной мере для меня понятен только PHP) я не мог.
    Интерес загрыз, и сегодня с самого утра я размышлял, как можно его реализовать, и под вечер сел и вот за пару часов написал вот такое вот чудо, прошу любить и жаловать.

    Реализовано только добавление потомка и поиск по ключу, т.к. этого хватило для понимания структуры дерева и основных его особенностей.

    Открыто для обсуждения: другие примеры реализации на PHP, и самое главное - применение на PHP (возможно в связке с БД)

    PHP:
    1. <?php
    2.  
    3. function shuffle_assoc(&$array) {
    4.         $keys = array_keys($array);
    5.  
    6.         shuffle($keys);
    7.  
    8.         foreach($keys as $key) {
    9.             $new[$key] = $array[$key];
    10.         }
    11.  
    12.         $array = $new;
    13.  
    14.         return true;
    15.     }
    16.  
    17. /**
    18.  * A tree-node class, contains all attributes for creating nodes
    19.  */
    20. class TreeNode
    21. {
    22.     public $leftChild = null;
    23.     public $rightChild = null;
    24.     public $key = null;
    25.     public $value = null;  
    26. }
    27.  
    28. /**
    29.  * A binary search tree handler class, contains methods to manipulate with trees
    30.  */
    31. class BinarySearchTreeHandler
    32. {
    33.     /**
    34.      * The raw binary search tree variable
    35.      *
    36.      * @access  protected
    37.      * @var     object
    38.      */
    39.     protected $_tree = null;
    40.    
    41.     /**
    42.      * The raw variable to store lines of tree-building log
    43.      *
    44.      * @access  protected
    45.      * @var     array
    46.      */
    47.     protected $_buildLog = array ();
    48.    
    49.     /**
    50.      * The raw variable to store lines of tree-searching log
    51.      *
    52.      * @access  protected
    53.      * @var     array
    54.      */
    55.     protected $_searchLog = array ();
    56.    
    57.     /**
    58.      * Class constructor, builds a binary search tree if array is given
    59.      *
    60.      * @access  public
    61.      * @param   array   given array, indexes must be integers
    62.      */
    63.     public function __construct ($array = null)
    64.     {
    65.         if ($array !== null)
    66.         {
    67.             $this->buildTree ($array);
    68.         }
    69.     }
    70.    
    71.     /**
    72.      * Method which build new binary search tree from given array
    73.      *
    74.      * @access  public
    75.      * @param   array   given array, indexes must be integers
    76.      * @return  obj     binary search tree
    77.      */
    78.     public function buildTree ($array)
    79.     {
    80.         $this->_tree = new TreeNode;
    81.        
    82.         shuffle_assoc ($array);
    83.        
    84.         foreach ($array as $key => $value)
    85.         {
    86.             $this->appendChild ($this->_tree, $key, $value);
    87.         }
    88.         return $this->_tree;
    89.     }
    90.    
    91.     /**
    92.      * Method to append new node to the tree
    93.      *
    94.      * @access  public
    95.      * @param   viod    object - binary search tree to append to
    96.      *               OR string - keyword SELF to append to already formed tree
    97.      *               OR boolean (null) - if we have ran into the bottom of the
    98.      *                                   tree and have to create a leaf
    99.      * @param   int     key of new node
    100.      * @param   void    value of new node
    101.      */
    102.     public function appendChild ($tree, $key, $value)
    103.     {
    104.         if (is_string ($tree))
    105.         {
    106.             if (strtoupper ($tree) === "SELF")
    107.             {
    108.                 $tree = $this->_tree;
    109.             }
    110.         }
    111.        
    112.         if ($tree->key === null)
    113.         {
    114.             $tree->key = $key;
    115.             $tree->value = $value;
    116.             return $tree;
    117.         }
    118.         if ($tree->key > $key)
    119.         {
    120.             if ($tree->leftChild !== null)
    121.             {
    122.                 $this->_buildLog[] = "key given (<strong>key</strong> = <u>" . $key . "</u>, <strong>value</strong> = <u>" . $value . "</u>) is smaller than key of current node (<em><strong>key</strong>: <u>" . $tree->key . "</u>, <strong>value</strong>: <u>" . $tree->value . "</u></em>), but this node already has LEFT CHILD. So <strong>passing</strong> key-value pair to the LEFT CHILD (<em><strong>key</strong>: <u>" . $tree->leftChild->key . "</u>, <strong>value</strong>: <u>" . $tree->leftChild->value . "</u></em>).";
    123.                 return $this->appendChild ($tree->leftChild, $key, $value);
    124.             }
    125.             else
    126.             {
    127.                 $this->_buildLog[] = "<strong>appending</strong> LEFT CHILD to node (<em><strong>key</strong>: <u>" . $tree->key . "</u>, <strong>value</strong>: <u>" . $tree->value . "</u></em>): <strong>key</strong> = <u>" . $key . "</u>, <strong>value</strong> = <u>" . $value . "</u>";
    128.                 return $tree->leftChild = $this->appendChild (new TreeNode, $key, $value);
    129.             }
    130.         }
    131.         if ($tree->key <= $key)
    132.         {
    133.             if ($tree->rightChild !== null)
    134.             {
    135.                 $this->_buildLog[] = "key given (<strong>key</strong> = <u>" . $key . "</u>, <strong>value</strong> = <u>" . $value . "</u>) is bigger than key of current node (<em><strong>key</strong>: <u>" . $tree->key . "</u>, <strong>value</strong>: <u>" . $tree->value . "</u></em>), but this node already has RIGHT CHILD. So <strong>passing</strong> key-value pair to the RIGHT CHILD (<em><strong>key</strong>: <u>" . $tree->rightChild->key . "</u>, <strong>value</strong>: <u>" . $tree->rightChild->value . "</u></em>).";
    136.                 return $this->appendChild ($tree->rightChild, $key, $value);
    137.             } else
    138.             {
    139.                 $this->_buildLog[] = "<strong>appending</strong> RIGHT CHILD to node (<em><strong>key</strong>: <u>" . $tree->key . "</u>, <strong>value</strong>: <u>" . $tree->value . "</u></em>): <strong>key</strong> = <u>" . $key . "</u>, <strong>value</strong> = <u>" . $value . "</u>";
    140.                 return $tree->rightChild = $this->appendChild (new TreeNode, $key, $value);
    141.             }
    142.         }
    143.     }
    144.    
    145.     /**
    146.      * Method to search a node in binary search tree
    147.      *
    148.      * Tries to find a node in a binary search tree with given key
    149.      * If finds - returns it's value
    150.      * If fails - returns false
    151.      *
    152.      * @access  public
    153.      * @param   int     key of node to find
    154.      * @param   viod    object - binary search tree to search in
    155.      *               OR string - keyword SELF to search in already formed tree
    156.      *               OR boolean (null) - if we fail to find what we look for
    157.      * @return  void    false on failure, node value on success
    158.      */
    159.     public function find ($key, $tree)
    160.     {
    161.         if (is_string ($tree))
    162.         {
    163.             if (strtoupper ($tree) === "SELF") {
    164.                 $tree = $this->_tree;
    165.             }
    166.         }
    167.        
    168.         if (!is_object ($tree) AND $tree !== null)
    169.         {
    170.             $this->_searchLog[] = "given tree isn't a tree at all";
    171.             return false;
    172.         }
    173.        
    174.         if ($tree === null) {
    175.             $this->_searchLog[] = "requested key <strong>wasn't found</strong>";
    176.             return false;
    177.         }
    178.        
    179.         if ($tree->key === $key)
    180.         {
    181.             $this->_searchLog[] = "given key <strong>found</strong>, returned value is " . $tree->value;
    182.             return $tree->value;
    183.         }
    184.         if ($tree->key > $key)
    185.         {
    186.             $this->_searchLog[] = "given key (" . $key . ") is <strong>smaller</strong> than the key in the node (" . $tree->key . "), trying to find in LEFT child...";
    187.             return $this->find ($key, $tree->leftChild);
    188.         }
    189.         if ($tree->key < $key)
    190.         {
    191.             $this->_searchLog[] = "given key (" . $key . ") is <strong>bigger</strong> than the key in the node (" . $tree->key . "), trying to find in RIGHT child...";
    192.             return $this->find ($key, $tree->rightChild);
    193.         }
    194.     }
    195.    
    196.     /**
    197.      * Returns requested log, buildLog by default
    198.      *
    199.      * @access  public
    200.      * @param   string  keyword for log, can be either "build" or "search"
    201.      * @return  string  html log of operations
    202.      */
    203.     public function getLog ($type = "build")
    204.     {
    205.         $type = strtolower ($type);
    206.        
    207.         if (!in_array ($type, array ("build", "search"))) {
    208.             $type = "build";
    209.         }
    210.        
    211.         $name = "_" . $type . "Log";
    212.        
    213.         return implode ("<br />", $this->$name);
    214.     }
    215. }?>
    216.  
    217.  
    P.S. функция shuffle_assoc подсмотрена на php.net
     
  2. Professor

    Professor Активный пользователь

    С нами с:
    2 янв 2008
    Сообщения:
    121
    Симпатии:
    0
    Адрес:
    г. Одесса, Украина
    Пример использования:
    PHP:
    1. <?php
    2. $a = array (8  => 1,
    3.             3  => 2,
    4.             5  => 3,
    5.             9  => 4,
    6.             18 => 5,
    7.             13 => 6,
    8.             16 => 7,
    9.             19 => 8,
    10.             1  => 9,
    11.             7  => 10,
    12.             12 => 11,
    13.             4  => 12,
    14.             10 => 13,
    15.             20 => 14,
    16.             11 => 15,
    17.             14 => 16,
    18.             6  => 17,
    19.             2  => 18,
    20.             15 => 19,
    21.             17 => 20);
    22.  
    23. $b = new BinarySearchTreeHandler ($a);
    24.  
    25. echo "<pre>";
    26. var_dump ($b);
    27. echo "</pre>";
    28.  
    29. echo $b->getLog ();
    30.  
    31. echo $s = $b->find (13, "SELF");
    32.  
    33. echo $b->getLog ("search");
    34.  
    35. $b->appendChild ("SELF", 34, "straight");
    36. $b->appendChild ("SELF", 24, "dexterity");
    37. $b->appendChild ("SELF", 35, "intellegence");
    38. $b->appendChild ("SELF", 31, "someworlsd");
    39.  
    40. echo "<pre>";
    41. var_dump ($b);
    42. echo "</pre>";
    43.  
    44. echo $b->getLog ();
    45.  
    46. ?>
    47.