/*----------------------------------------------------------------------------*- YAdmin system - Binary Tree Core Provides functions to generate balanced binary search trees for efficient searching of large arrays by value. Left branch is less than, right branch is greater than or equal to for multiple matching values. Changelog: 24/03/07: First version. -*----------------------------------------------------------------------------*/ #define BINTREE_NO_BRANCH -1 #define BINTREE_NOT_FOUND -1 enum E_BINTREE_TREE { E_BINTREE_TREE_VALUE, E_BINTREE_TREE_LEFT, E_BINTREE_TREE_RIGHT, E_BINTREE_TREE_POINTER } enum E_BINTREE_INPUT { E_BINTREE_INPUT_VALUE, E_BINTREE_INPUT_POINTER } /*----------------------------------------------------------------------------*- Function: Bintree_Sort Params: input[][E_BINTREE_INPUT] - Data to sort. size - Size of data to sort. Return: - Notes: Entry point for Bintree_QSort. -*----------------------------------------------------------------------------*/ #define Bintree_Sort(%1,%2) \ Bintree_QSort((%1), 0, (%2) - 1) /*----------------------------------------------------------------------------*- Function: Bintree_Fill Params: Bintree:output[][E_BINTREE_TREE] - Destination for balanced tree. data[][E_BINTREE_INPUT] - Source data. size - Size of data. Return: Bintree_SortHalf. Notes: Entry point for Bintree_SortHalf. -*----------------------------------------------------------------------------*/ #define Bintree_Fill(%1,%2,%3) \ Bintree_SortHalf((%1), (%2), 0, (%3), 0) /*----------------------------------------------------------------------------*- Function: Bintree_Generate Params: Bintree:output[][E_BINTREE_TREE] - Binary tree to store the data in. input[][E_BINTREE_INPUT] - Input data to get the data from. size - Number of items to sort. Return: - Notes: Just calls the sort and fill routines. -*----------------------------------------------------------------------------*/ Bintree_Generate(Bintree:output[][E_BINTREE_TREE], input[][E_BINTREE_INPUT], size) { Bintree_Sort(input, size); Bintree_Fill(output, input, size); return 1; } /*----------------------------------------------------------------------------*- Function: Bintree_Reset Params: Bintree:tree[][E_BINTREE_TREE] - Array to reset. size - Size of array. Return: - Notes: Initialises the array for use. -*----------------------------------------------------------------------------*/ Bintree_Reset(Bintree:tree[][E_BINTREE_TREE], size) { for (new i; i < size; i++) { tree[i][E_BINTREE_TREE_VALUE] = 0; tree[i][E_BINTREE_TREE_LEFT] = BINTREE_NO_BRANCH; tree[i][E_BINTREE_TREE_RIGHT] = BINTREE_NO_BRANCH; tree[i][E_BINTREE_TREE_POINTER] = BINTREE_NOT_FOUND; } } /*----------------------------------------------------------------------------*- Function: Bintree_FindValue Params: Bintree:tree[][E_BINTREE_TREE] - Tree to find the data in. value - Value to search for. &cont - Start point. Return: - Notes: Itterates through the array following the various paths till it locates the value provided or reaches a dead end. If the current value is greater than the search value, the search goes left, otherwise right. If cont is not -1 the search will start from the data pointed to by the data pointed to by conts' right path, this is to allow collisions to be passed over if you want a subsequent one. -*----------------------------------------------------------------------------*/ Bintree_FindValue(Bintree:tree[][E_BINTREE_TREE], value, &cont = -1) { new treeValue; if (cont != -1) cont = tree[cont][E_BINTREE_TREE_RIGHT]; else cont = 0; while (cont != BINTREE_NO_BRANCH) { treeValue = tree[cont][E_BINTREE_TREE_VALUE]; if (value < treeValue) cont = tree[cont][E_BINTREE_TREE_LEFT]; else { new tmp = cont; cont = tree[cont][E_BINTREE_TREE_RIGHT]; if (value == treeValue) { return tree[tmp][E_BINTREE_TREE_POINTER]; } } } return BINTREE_NOT_FOUND; } /*----------------------------------------------------------------------------*- Function: Bintree_QSort Params: numbers[][E_BINTREE_INPUT] - Data to sort. left - Start index. right - End index. Return: - Notes: Custom version of QSort (see YSI_misc) allows for E_BINTREE_INPUT data types, preserving the relative pointers for the sorted data. -*----------------------------------------------------------------------------*/ Bintree_QSort(numbers[][E_BINTREE_INPUT], left, right) { new pivot = numbers[left][E_BINTREE_INPUT_VALUE], pointer = numbers[left][E_BINTREE_INPUT_POINTER], l_hold = left, r_hold = right; while (left < right) { while ((numbers[right][E_BINTREE_INPUT_VALUE] >= pivot) && (left < right)) right--; if (left != right) { numbers[left][E_BINTREE_INPUT_VALUE] = numbers[right][E_BINTREE_INPUT_VALUE]; numbers[left][E_BINTREE_INPUT_POINTER] = numbers[right][E_BINTREE_INPUT_POINTER]; left++; } while ((numbers[left][E_BINTREE_INPUT_VALUE] <= pivot) && (left < right)) left++; if (left != right) { numbers[right][E_BINTREE_INPUT_VALUE] = numbers[left][E_BINTREE_INPUT_VALUE]; numbers[right][E_BINTREE_INPUT_POINTER] = numbers[left][E_BINTREE_INPUT_POINTER]; right--; } } numbers[left][E_BINTREE_INPUT_VALUE] = pivot; numbers[left][E_BINTREE_INPUT_POINTER] = pointer; pivot = left; left = l_hold; right = r_hold; if (left < pivot) Bintree_QSort(numbers, left, pivot - 1); if (right > pivot) Bintree_QSort(numbers, pivot + 1, right); } /*----------------------------------------------------------------------------*- Function: Bintree_SortHalf Params: Bintree:output[][E_BINTREE_TREE] - Destination array. data[][E_BINTREE_INPUT] - Source array. index - Start index of the source for processing. upper - End index of the source for processing. offset - Current offset in the destination array for writing. Return: Size of balanced tree. Notes: Recursively calls itself. Bisects the passed array and passed each half back to itself, with the middle value of each half being the left and right branches of the middle value of the passed array (which isn't included in either bisected half). This is itterative so those are again split and again split. If the passed array is only one or two elements big the behaviour is set and hardcoded. Equal values SHOULD branch right, the code is designed for this however the generation is not fully tested (it mostly branches right but adjacent after bisecting values haven't been tested). Based on code written for PHP by me. -*----------------------------------------------------------------------------*/ Bintree_SortHalf(Bintree:output[][E_BINTREE_TREE], data[][E_BINTREE_INPUT], index, upper, offset) { new num = upper - index; if (num == 1) { output[offset][E_BINTREE_TREE_VALUE] = data[index][E_BINTREE_INPUT_VALUE]; output[offset][E_BINTREE_TREE_POINTER] = data[index][E_BINTREE_INPUT_POINTER]; output[offset][E_BINTREE_TREE_LEFT] = BINTREE_NO_BRANCH; output[offset][E_BINTREE_TREE_RIGHT] = BINTREE_NO_BRANCH; } else if (num == 2) { output[offset][E_BINTREE_TREE_VALUE] = data[index][E_BINTREE_INPUT_VALUE]; output[offset][E_BINTREE_TREE_POINTER] = data[index][E_BINTREE_INPUT_POINTER]; output[offset][E_BINTREE_TREE_LEFT] = BINTREE_NO_BRANCH; output[offset][E_BINTREE_TREE_RIGHT] = ++offset; output[offset][E_BINTREE_TREE_VALUE] = data[++index][E_BINTREE_INPUT_VALUE]; output[offset][E_BINTREE_TREE_POINTER] = data[index][E_BINTREE_INPUT_POINTER]; output[offset][E_BINTREE_TREE_LEFT] = BINTREE_NO_BRANCH; output[offset][E_BINTREE_TREE_RIGHT] = BINTREE_NO_BRANCH; } else { new half = num / 2, off = half + index, right = Bintree_SortHalf(output, data, index, off, offset + 1); output[offset][E_BINTREE_TREE_VALUE] = data[off][E_BINTREE_INPUT_VALUE]; output[offset][E_BINTREE_TREE_POINTER] = data[off][E_BINTREE_INPUT_POINTER]; output[offset][E_BINTREE_TREE_LEFT] = offset + 1; output[offset][E_BINTREE_TREE_RIGHT] = right; return Bintree_SortHalf(output, data, off + 1, upper, right); } return offset + 1; }