PHP使用二分搜索树实现映射

212 阅读2分钟
namespace map;
/**
 * 基于搜索二叉树实现的映射类
 * @package map
 */
class BSTMap implements Map
{

    private $root;
    private $size;

    public function __construct()
    {
        $this->root = null;
        $this->size = 0;
    }

    public function isEmpty()
    {
        return $this->size == 0;
    }

    public function getSize()
    {
        return $this->size;
    }

    public function contains($key)
    {
        return $this->getNode($this->root, $key) != null;
    }

    /**
     * 添加元素到而二分搜索树
     * @param mixed $key
     * @param mixed $value
     */
    public function add($key, $value)
    {
        $this->root = $this->addNode($this->root, $key, $value);
    }

    /**
     * 往以node为根的二分搜索树添加元素(key,value),递归算法
     * 返回插入新节点后二分搜索树的根
     * @param BSTNode|null $node
     * @param null|mixed $key 映射的键
     * @param null|mixed $value 映射的值
     * @return BSTNode
     */
    private function addNode(BSTNode $node = null, $key = null, $value = null)
    {
        if ($node == null) {
            $this->size++;
            return new BSTNode($key, $value);
        }

        if ($key < $node->key)
            $node->left = $this->addNode($node->left, $key, $value);
        else if ($key > $node->key)
            $node->right = $this->addNode($node->right, $key, $value);
        else // $key == $node->key
            $node->value = $value;

        return $node;
    }


    /**
     * 返回以node为根的二分搜索树中key所在的节点
     * @param BSTNode|null $node
     * @param null|mixed $key
     * @return BSTNode|null
     */
    private function getNode(BSTNode $node = null, $key = null)
    {
        if ($node == null)
            return null;

        if ($key == $node->key)
            return $node;
        else if ($key < $node->key)
            return $this->getNode($node->left, $key);
        else // $key > $node->key
            return $this->getNode($node->right, $key);
    }

    public function set($key, $newValue)
    {
        $node = $this->getNode($this->root, $key);
        if ($node == null)
            throw new \InvalidArgumentException($key . " does't exist");

        $node->value = $newValue;
    }


    /**
     * 获取键为key的值
     * @param $key
     * @return mixed|null
     */
    public function get($key)
    {
        $node = $this->getNode($this->root, $key);
        return $node == null ? null : $node->value;
    }

    /**
     * 从二分搜索树中删除键为key的节点
     * @param $key
     * @return mixed|null
     */
    public function remove($key)
    {
        $node = $this->getNode($this->root, $key);
        if ($node == null)
            return null;

        $this->root = $this->removeNode($this->root, $key);
        return $node->value;
    }


    /**
     * 从以node为根节点的二分搜索树中删除键为key的节点,递归算法
     * 返回删除节点后新的二分搜索树的根节点
     * @param BSTNode|null $node
     * @param null $key
     * @return BSTNode|null
     */
    private function removeNode(BSTNode $node = null, $key = null)
    {
        if ($node == null)
            return null;

        if ($key < $node->key) {
            $node->left = $this->removeNode($node->left, $key);
            return $node;
        } else if ($key > $node->key) {
            $node->right = $this->removeNode($node->right, $key);
            return $node;
        } else {
            if ($node->left == null) {
                $rightNode   = $node->right;
                $node->right = null;
                $this->size--;
                return $rightNode;
            }

            if ($node->right == null) {
                $leftNode   = $node->left;
                $node->left = null;
                $this->size--;
                return $leftNode;
            }

            $successor        = $this->minNum($node->right);
            $successor->right = $this->removeMin($node->right);
            $successor->left  = $node->left;

            $node->right = $node->left = null;
            return $successor;
        }
    }

    /**
     * 查找最小元素
     * @param BSTNode|null $node
     * @return BSTNode
     */
    private function minNum(BSTNode $node = null)
    {
        if ($node->left == null)
            return $node;

        return $this->minNum($node->left);
    }

    /**
     * 删除最小元素
     * @param BSTNode|null $node
     * @return BSTNode|null
     */
    private function removeMin(BSTNode $node = null)
    {
        if ($node->left == null) {
            $rightNode   = $node->right;
            $node->right = null;
            $this->size--;
            return $rightNode;
        }

        $node->left = $this->removeMin($node->left);
        return $node;
    }
}

namespace map;
interface Map
{
    function add($key, $value);

    function remove($key);

    function contains($key);

    function get($key);

    function set($key, $newValue);

    function getSize();

    function isEmpty();
}

namespace map;
/**
 * 二分搜索树的节点类
 * @package map
 */
class BSTNode
{
    /**
     * 左孩子
     * @var BSTNode
     */
    public $left;

    /**
     * 右孩子
     * @var BSTNode
     */
    public $right;

    /**
     * 映射键
     * @var mixed
     */
    public $key;


    /**
     * 映射值
     * @var mixed
     */
    public $value;

    /**
     * BSTNode constructor.
     * @param $key
     * @param $value
     */
    public function __construct($key, $value)
    {
        $this->key   = $key;
        $this->value = $value;
        $this->left  = null;
        $this->right = null;
    }
}

测试用例1

$words=[
    'jack','jackson','jack','huan','wang','li','jack','li','king','tom'
];

$map = new \map\BSTMap();
foreach ($words as $word){
    if($map->contains($word))
        $map->set($word,$map->get($word)+1);
    else
        $map->add($word,1);
}

echo 'total words:'.count($words),'<br/>';
echo 'total different words:'.$map->getSize(),'<br/>';
echo 'Frenquency of jack: '. $map->get('jack'),'<br/>';
echo 'Frenquency of li: '. $map->get('li'),'<br/>';
echo 'Frequency of huan: '. $map->get('huan'),'<br/>';

测试用例2

给定两个数组,编写一个函数来计算它们的交集。

输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2,2]

$nums1 = [1,2,2,1];
$nums2 = [2,2];
$map = new map\BSTMap();

foreach ($nums1 as $n) {
    if ($map->contains($n))
        $map->set($n, $map->get($n) + 1);
    else
        $map->add($n, 1);
}

$arr = [];
foreach ($nums2 as $n) {
    if ($map->contains($n)) {
        $arr[] = $n;
        $map->set($n, $map->get($n) - 1);
        if ($map->get($n) == 0)
            $map->remove($n);
    }
}