Рекурсия. Другие предложения?

avenger_msoft

Новичок
Рекурсия. Другие предложения?

Привет всем!

Есть таблица в БД:
PHP:
CREATE TABLE Childrens (
    AdoptionPF INTEGER NOT NULL,
    ChildrenFK INTEGER NOT NULL,
    CONSTRAINT PF_Childrens PRIMARY KEY (AdoptionPF, ChildrenFK)
);
Она принимает, скажем следующие значения:
PHP:
ADOPTIONPF	CHILDRENFK
4	8
8	14
8	17
17	19
Мне необходимо рекурсивно получить всех братьев определенного родителя. Например 4 имет братьев 8, 14, 17, 19 (Выборка по ADOPTIONPF). А 19 (Обратите внимание ЕГО нет в ADOPTIONPF) имеет братьев 4, 8, 14, 17.
Делал так
PHP:
                  function GetChildren($id, &$child_array, $type = 0) {
                           global $base;
                           if ($id > 0) {
                               if ($type == 0 ) {
                                   $child_array[] = $base->GetCol("SELECT CHILDRENFK FROM CHILDRENS WHERE ADOPTIONPF = $id") or Halt($base->ErrorMsg());
                               } else {
                                   $child_array[] = $base->GetCol("SELECT ADOPTIONPF FROM CHILDRENS WHERE CHILDRENFK = $id") or Halt($base->ErrorMsg());
                               }
                               $cnt = count($child_array) - 1;

                               if (count($child_array[$cnt]) > 0) {
                                   reset($child_array[$cnt]);
                                   foreach ($child_array[$cnt] as $name => $value) {
                                            GetChildren($value, $child_array, $type);
//                                            GetChildren($value, $child_array, 1);
                                   }
                               }
                           }
                  }
Функция $base->GetCol() возвращает массив значений, например: array("0"=>"4", "1"=>"2")
Но это не правильный подход. Т.к. рекурсивный обход проходить по одному из столбцов в базе (поле $type). По логике надо делать два вызова рекурсивной процедуры GetChildren для $type = 0 и для $type = 1 и + в селект добавить WHERE ... CHILDRENFK NOT IN (... здесь значения CHILDRENFK которые уже выбирались из базы) для первого селекта, и практически такая же конструкция для второго селекта WHERE ... ADOPTIONPF NOT IN (... здесь значения ADOPTIONPF которые уже выбирались из базы), для того что-бы рекурсия не зацыклилась.

Тогда все как-то грамоздко получится. Есть другие предложения?

-~{}~ 02.05.06 13:44:

Задача решена следующим образом
PHP:
                   function GetChildren($id, &$child_array, $type = 0) {
                           global $base;
                           if ($id > 0) {

                               $temp_arr = array();
                               $add_sql  = "";
                               if ($type == 0 ) {
                                   if (count($child_array) > 0) $add_sql = "AND CHILDRENFK NOT IN (" . implode(", ", $child_array) . ")";
                                   $temp_arr = $base->GetCol("SELECT CHILDRENFK FROM CHILDRENS WHERE ADOPTIONPF = $id $add_sql") or Halt($base->ErrorMsg());
                               } else {
                                   if (count($child_array) > 0) $add_sql = "AND ADOPTIONPF NOT IN (" . implode(", ", $child_array) . ")";
                                   $temp_arr = $base->GetCol("SELECT ADOPTIONPF FROM CHILDRENS WHERE CHILDRENFK = $id $add_sql") or Halt($base->ErrorMsg());
                               }

                               if (count($temp_arr) > 0) {
                                   $child_array = array_merge($child_array, $temp_arr);
                                   reset($temp_arr);
                                   foreach ($temp_arr as $name => $value) {
                                            GetChildren($value, $child_array, 0);
                                            GetChildren($value, $child_array, 1);
                                   }
                               }

                           } // if ($id > 0)
                  }

                  function GetChildrenAll($id) {
                           $childrensID = array();
                           GetChildren($id, $childrensID, 0);
                           GetChildren($id, $childrensID, 1);
                           $childrensID = array_unique( array_diff( $childrensID, array($id) ) );
                           return ($childrensID);
                  }
Может есть другие предложения, более рациональные?
 

avenger_msoft

Новичок
Автор оригинала: Nogrogomed
Ты бы хоть код объяснил...
Ты с деревьями работал?

Выходом функции, в идеале, GetChildren($id, &$child_array, $type = 0) является массив $child_array, который содержит всех братьев и родителей $id, $type = указывает по какому столбцу идет выборка.
PHP:
ADOPTIONPF    CHILDRENFK 
4    8 
8    14 
8    17 
17    19
Например, для 4 ФУНКЦИЯ ДОЛЖНА ВЕРНУТЬ 8,14,17,19. Для 19: 8,14,17,4 И т.д.

Автор оригинала: Nogrogomed
непонятны назначения полей.
А что тут понимать для 4 ребенок 8. Для 8 - дети 14 и 17. Для 17 - ребенок 19.

Или для 17 родитель 8, + у 17 есть сын 19. У родителя 8 есть сын 14. Для родителя 8 - 4 является отцом...
 

ybilevych

Новичок
avenger_msoft
Ты как-нибудь определись, что тебе нужно - или:
Мне необходимо рекурсивно получить всех братьев определенного родителя. Например 4 имет братьев 8, 14, 17, 19 (Выборка по ADOPTIONPF). А 19 (Обратите внимание ЕГО нет в ADOPTIONPF) имеет братьев 4, 8, 14, 17.
или
А что тут понимать для 4 ребенок 8. Для 8 - дети 14 и 17. Для 17 - ребенок 19.

Или для 17 родитель 8, + у 17 есть сын 19. У родителя 8 есть сын 14. Для родителя 8 - 4 является отцом...
Во втором случае ты хочешь получить дерево наследования, т.е от предков к потомкам
В первом случае вообще ничего непонятно

Предваряя вопрос:
Ты с деревьями работал?
Да работал, и с фамильным в том числе. И задачу получения иерархий предков и потомков решил. Причем значительно проще, чем у тебя
 

avenger_msoft

Новичок
ybilevych

Может так понятнее будет: мне надо получить все элементы (ветку) от потомка к детям, которой (ветке) пренадлежит заданный $id. Есть предложения?
 

ybilevych

Новичок
Наверное, имелось в виду от предка к потомкам (детям)

Код, возможно надо доработать напильником (пишу по памяти), но общий алгоритм таков:
PHP:
...
$level = 1;
...
function getTreeUp($parent_id, $level)
{
    $result = mysql_query("SELECT * FROM Childrens WHERE ADOPTIONPF=$parent_id");
    while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
    {
        echo str_repeat('-',$level);
        echo $row['CHILDRENFK'];
        getTreeUp($row['CHILDRENFK'], $level+1);
    }
}
Да,чуть не забыл: этот алгоритм не выводит самого предка, от которого мы пляшем. Надо его просто добыть из базы селектом перед вызовом рекурсивной функции
 

avenger_msoft

Новичок
2ybilevych:

В нашем примере мы имеем дерево:
PHP:
4 - 8 - 17 - 19
     /      
    14
Функция должна возращать все ветки, у которых есть связь с данным ID. А вы даете обычный рекурсивный обход от предка к детю.

В моем случае при ID=4 фукция возвращает (8,17,19,14). При ID=17=>(19,4,8,14). При ID=14=>(4,8,17,19) и т.д.

Здесь не обычный рекурсивный обход. Здесь обход от предка к потомку и от потомка к предку, далее от детей и предков так же в двух направления (вверх и вниз) по дереву.

В таблице БД хранится много друг с другом несвязанных деревьев. Пример одного дерева я вам привел. Мне надо вернуть все элементы дерева, которому принадлежит данный ID.

Извините, за то что изначально упустил фразу В таблице БД хранится много друг с другом несвязанных деревьев

С уважением, Иван.
 

ybilevych

Новичок
Да, из предыдущих постов не было ясно, что речь идет о поиске ВСЕХ узлов дерева.

Мне на ум приходит такая идея
1. Рекурсивно найти верхнего предка (родителя, для которого нет родителя)
2. Опять-таки рекурсивно выбрать все дерево для данного предка
Это, конечно в случае, если это дерево, а не граф с замкнутыми путями

Но я думаю, задачу можно было бы упростить, если хранить дополнительную информацию в БД.
Я тут пару ссылок нашел:
http://phpclub.ru/detail/article/myXTree
http://phpclub.ru/detail/article/db_tree
http://phpclub.ru/detail/article/2002-06-03
Может, будет полезным
 

Nogrogomed

Новичок
Вот это ты намудрил.
Короче говоря у тебя есть лес (множество деревьев).
Ты задаешь ID. Тебе нужно вернуть все элементы дерева, которому принадлежит этот ID. Так?

Если так, тогда:
1. Узнаешь корень дерева
2. Применяешь какой-нибудь из методов обхода дерева и все.

Вот тебе корявая наспех сделанная функция
PHP:
function GetTree(          &$mas_der, // массив элементов дерева (в начале в массиве будет только id корня)
                           $mas_pred) // массив родителей текущего уровня
{
// узнаем детей каких родителей нужно искать
$pod_sql="";
for ($i=0; $i<count($mas_pred); $i++)
   $pod_sql.=" `поле_родителя`='{$mas_pred[$i]}' OR ";

// находим детей
$sql="SELECT * FROM `таблица` WHERE $pod_sql 0=1";
$r = mysql_query($sql) or die(mysql_error());
// делаем новй массив родителей
$kol=mysql_num_rows($r);
if ($kol)
{
   $new_mas_pred=array();
   for ($i=0; $i<$kol; $i++)
   {
        mysql_data_seek($r, $i);
        $mas=mysql_fetch_assoc($r);
        // добавляем ребенка в новый массив родителей
        $new_mas_pred[]=$mas['ид_ребенка'];
        // добавляем ребенка к массиву элементов дерева
        $mas_der[]=$mas['ид_ребенка'];
   }
   GetTree($mas_der, $new_mas_pred);
}
else
   return;
}
 

Nogrogomed

Новичок
StUV, в данном случае напрямую использовать эти алгоритмы не очень удобно. Функция, которую я написал выше - производная от ПВШ, и за каждый шаг узнает все узлы одного яруса.
 

avenger_msoft

Новичок
Всем спасибо! Проблема решена одновременным движением и вверх и вниз по дереву.
 
Сверху