红黑树实现 --php版本

<?php //大部分代码按照自己对算法导论的伪代码的理解编写 namespace zxszl; /* 红黑树的特性: (1)每个节点或者是黑色,或者是红色。 (2)根节点是黑色。 (3)每个叶节点(NIL)是黑色。 [注意:这里叶节点,是指为空(NIL或NULL)的子节点!] (4)如果一个节点是红色的,则它的子节点必须是黑色的。 (5)从一个节点到该节点的叶节点的所有路径上包含相同数目的黑节点。 */ //参考 https://blog.csdn.net/v_JULY_v/article/details/6105630 class RedBlackNode { public $parent;//父节点 public $left; //左子节点 public $right; //右子节点 public $isRed; //红色节点或者黑色节点 /** * 这里引入两个变量,是为了方便字典数据结构 * 其中,key作为红黑树的排序依据 */ public $key; //节点值 public $value; //节点值 public function __construct () { $this->parent = null; $this->left = null; $this->right = null; $this->isRed = true; $this->key = null; $this->value = null; } } /** * 红黑树 ,左边小节点,右边大节点 */ class RedBlackTree { /** * 深度(树高度) * @param RedBlackNode $node * @return int */ static public function depth($node) { if ($node === NULL) { return 0; } $left_d = RedBlackTree::depth($node->left); $right_d = RedBlackTree::depth($node->right); return ($left_d > $right_d ? $left_d : $right_d) + 1; } /** * 最小节点 * @return RedBlackNode 最小节点 */ static public function minNode($node) { $current = $node; if ($current !== null) { while ($current->left !== null) { $current = $current->left; } } return $current; } /** * 最大节点 * @return RedBlackNode 最大节点 */ static public function maxNode($node) { $current = $node; if ($current !== null) { while ($current->right !== null) { $current = $current->right; } } return $current; } /** * 查找指定节点 * @return RedBlackNode 节点 */ public function searchNode($key) { $current = $this->rootNode; while ($current !== NULL) { if ($current->key === $key) { return $current; } elseif ($current->key > $key) { $current = $current->left; } else { $current = $current->right; } } return null; } /** * 插入节点,若$key已经存在,则替换对应节点的$value * @return RedBlackNode 插入的节点 */ public function insertNode($key,$value) { //查找节点插入时的父亲节点 $nodeParent = null; do { $tmp = $this->rootNode; while ($tmp !== null) { $nodeParent = $tmp; if ($key === $tmp->key ) {//已经存在key,不必再插入,只要替换value就可以 $tmp->value = $value; return; } else if ( $key < $tmp->key ) { $tmp = $tmp->left; } else { $tmp = $tmp->right; } } }while(0); //创建新节点,节点为红色 $node = new RedBlackNode(); // $node->isRed = true; $node->key = $key; $node->value = $value; //插入节点 $node->parent = $nodeParent; if ($nodeParent === null) { $this->rootNode = $node; } else if ($node->key < $nodeParent->key) { $nodeParent->left = $node; } else { $nodeParent->right = $node; } //修正红黑树 $this->p_insert_fixup($node); } /** * 删除节点 */ public function deleteNode ($key) { $z = $this->searchNode($key); if ( $z !== null ) //找到$key对应的节点 { //$z //待删除节点 $y = null; $x = null; ///>>>START $z不为null,那么经过操作,$y一定不为null if ($z->left === null || $z->right === null) { $y = $z; } else { $y = RedBlackTree::minNode($z->right); //$z的最小上限 } ///<<<END if ( $y->left !== null ) { $x = $y->left; } else { $x = $y->right; } //删除节点$y if ($x !== null){ $x->parent = $y->parent; } if ($y->parent === null ) { $this->rootNode = $x; } else if ( $y === $y->parent->left ) { $y->parent->left = $x; } else { $y->parent->right = $x; } $xParent = $y->parent; //如果$y不是$z,那么用$y的信息覆盖$z,这样达到删除$z的目的 if ($y !== $z) { $z->key = $y->key; $z->value = $y->value; } //如果删除的节点是黑色,则需要修正 if ($y->isRed === false) {//$y可能是$z ,也可能是$z的最小上限 $this->p_delete_fixup($x,$xParent); } } } /** * 修正红黑树 * @param RedBlackNode node 插入的节点 */ private function p_insert_fixup($node) { while ($node->parent !== null && $node->parent->isRed === true ) //如果node的父节点是红色 (node是红色) { $grandParent = $node->parent->parent; //祖父节点,一定存在 (根据红黑树性质,红色节点一定有父节点)) if ($node->parent === $grandParent->left ) { //node的父节点是左孩子 $uncle = $grandParent->right; //case1:node的父节点是红色,且叔节点是红色 if ($uncle && $uncle->isRed === true ) { $uncle->isRed = false; $node->parent->isRed = false; $grandParent->isRed = true; $node = $grandParent; continue; //经过这一步之后,组父节点作为新节点存在(跳到case2) } //case2:node的父节点是红色,叔节点是黑色,node是其父节点的右孩子 else if ($node === $node->parent->right ) { $node = $node->parent; $this->p_left_rotate($node); } //case3:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子 $node->parent->isRed = false; $grandParent->isRed = true; $this->p_right_rotate($grandParent); } else { //node的父节点是右孩子 $uncle = $grandParent->left; //case1:node的父节点是红色,且叔节点是红色 if ($uncle && $uncle->isRed === true ) { $uncle->isRed = false; $node->parent->isRed = false; $grandParent->isRed = true; $node = $grandParent; continue; //经过这一步之后,组父节点作为新节点存在(跳到case2) } //case2:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子 else if ($node === $node->parent->left ) { $node = $node->parent; $this->p_right_rotate($node); } //case3:node的父节点是红色,叔节点是黑色,node是其父节点的左孩子 $node->parent->isRed = false; $grandParent->isRed = true; $this->p_left_rotate($grandParent); } } $this->rootNode->isRed = false; } /** * 修正红黑树 * @param RedBlackNode $x * @param RedBlackNode $xParent */ private function p_delete_fixup($x,$xParent) { while ( $x !== $this->rootNode && ($x ===null || $x->isRed === false ) ) { if ($x === $xParent->left ) //$x不是根节点,那么$xParent一定存在 { $w = $xParent->right; if ($w !== null ) //若为null,则$w黑色 { //case1:$x黑, 兄弟节点$w红,父节点黑 if ($w->isRed === true ) { $w->isRed = false; $xParent->isRed = true; $this->p_left_rotate($xParent); $w = $xParent->right; //在左旋处理后,$xParent->right指向的是原来兄弟结点$w的右孩子,黑色 } //case2:$x黑色, $w是黑色,且$w两个子节点都是黑色 if ( ($w->left===null || $w->left->isRed === false) && ($w->right===null || $w->right->isRed === false) ) { $w->isRed = true; $x = $xParent; $xParent = $x->parent; } else { //case3:$x黑色,$w黑色,$w左子是红色,$w右子是黑色 if ( $w->right===null || $w->right->isRed === false ) { $w->left->isRed = false; $w->isRed = true; $this->p_right_rotate($w); $w = $xParent->right; } //case4:$x黑色,$w黑色,$w右子是红色,$w左子的颜色任意 $w->isRed = $xParent->isRed; $xParent->isRed = false; if ($w->right !== null ) { $w->right->isRed = false; } $this->p_left_rotate($xParent); $x = $this->rootNode; break;//加不加break都可以,因为下次循环条件不满足 } } } else //$x是右孩子 $x不是根节点,那么$xParent一定存在 { $w = $xParent->left; if ($w !== null ) //若为null,则$w黑色 { //case1:$x黑, 兄弟节点$w红,父节点黑 if ($w->isRed === true ) { $w->isRed = false; $xParent->isRed = true; $this->p_right_rotate($xParent); $w = $xParent->left; //在右旋处理后,$xParent->left指向的是原来兄弟结点$w的左孩子,黑色 } //case2:$x黑色, $w是黑色,且$w两个子节点都是黑色 if ( ($w->left===null || $w->left->isRed === false) && ($w->right===null || $w->right->isRed === false) ) { $w->isRed = true; $x = $xParent; $xParent = $x->parent; } else { //case3:$x黑色,$w黑色,$w左子是黑色,$w右子是红色 if ( $w->left===null || $w->left->isRed === false ) { $w->right->isRed = false; $w->isRed = true; $this->p_left_rotate($w); $w = $xParent->left; } //case4:$x黑色,$w黑色,$w左子是红色,$w右子的颜色任意 $w->isRed = $xParent->isRed; $xParent->isRed = false; if ($w->left !== null ) { $w->left->isRed = false; } $this->p_right_rotate($xParent); $x = $this->rootNode; break;//加不加break都可以,因为下次循环条件不满足 } } } } if ($x !== null ) { $x->isRed = false; } } /** * 对指定节点进行左旋操作 * 左旋中的“左”,意味着“被旋转的节点将变成(自己右孩子的)一个左节点 * node right * / \ / \ * a right ======> node c * / \ / \ * b c a b * * @param RedBlackNode $node */ private function p_left_rotate($node) { $rightChild = $node->right; //右孩子 if ($rightChild === null){ //右孩子为null,左旋结束 return; } $rightChild->parent = $node->parent; //将 “node的父亲” 设为 “rightChild的父亲” if ($rightChild->parent === null) { //情况1:如果 “rightChild的父亲” 是空节点,则将rightChild设为根节点 $this->rootNode = $rightChild; } else if ($node === $node->parent->left ) { //情况2:如果 node是它父节点的左孩子,则将rightChild设为“node的父节点的左孩子” $node->parent->left = $rightChild; } else { // 情况3:(node是它父节点的右孩子) 将rightChild设为“node的父节点的右孩子” $node->parent->right = $rightChild; } $node->parent = $rightChild; //将 “node的父节点” 设为 “rightChild” $node->right = $rightChild->left; //将 “rightChild的左孩子” 设为 “node的右孩子” if ($rightChild->left !== null) { $rightChild->left->parent = $node; //将 “node” 设为 “rightChild的左孩子的父亲” } $rightChild->left = $node; //将 “node” 设为 “rightChild的左孩子” } /** * 对指定节点进行右旋操作 * 右旋中的“右”,意味着“被旋转的节点将变成(自己左孩子的)一个右节点” * left node * / \ / \ * a node <====== left c * / \ / \ * b c a b * * @param RedBlackNode $node */ private function p_right_rotate($node) { $leftChild = $node->left; //左孩子 if ($leftChild === null) { //左孩子为null,右旋结束 return; } $leftChild->parent = $node->parent; //将 “node的父节点” 设为 “leftChild的父节点” if ($leftChild->parent === null) { // 情况1:如果 “leftChild的父亲” 是空节点,则将leftChild设为根节点 $this->rootNode = $leftChild; } else if ($node === $node->parent->left ) { // 情况2:如果 node 是它父节点的左孩子,则将leftChild设为“node的父节点的左孩子” $node->parent->left = $leftChild; } else { // 情况3:如果 node 是它父节点的右孩子,则将leftChild设为“node的父节点的右孩子” $node->parent->right = $leftChild; } $node->parent = $leftChild; //将leftChild设为“node的父节点” $node->left = $leftChild->right; //将 “leftChild的右孩子” 设为"node的左孩子" if ( $leftChild->right !== null ){ $leftChild->right->parent = $node; //将node 设为 “leftChild的右孩子的父节点” } $leftChild->right = $node; //将 "node" 设为 “leftChild的右孩子” } private $rootNode; } $rbTree = new RedBlackTree(); $testN = 10000; function testInsert( $rbTree ) { global $testN; $tmpArray = array(); for ($i = 0 ; $i < $testN ; $i++) { array_push($tmpArray,$i); } shuffle($tmpArray); for ($i = 0 ; $i < $testN ; $i++) { $k = $tmpArray[$i]; $rbTree->insertNode($k,$k); } for ($i = 0 ; $i < $testN ; $i++) { $node = $rbTree->searchNode($i); if($node === null) { echo "not found $i"; } else if ($node->value !== $i ) { $value = $node->value; echo "found $i,but value is $value"; } } } function testDelete( $rbTree ) { global $testN; $tmpArray = array(); for ($i = 0 ; $i < $testN ; $i++) { array_push($tmpArray,$i); } shuffle($tmpArray); for ($i = 0 ; $i < $testN ; $i++) { $k = $tmpArray[$i]; $rbTree->deleteNode($k); } for ($i = 0 ; $i < $testN ; $i++) { $node = $rbTree->searchNode($i); if($node !== null) { echo "found $i"; } } } testInsert($rbTree); testDelete($rbTree);

September 17, 2019 · 7 min · holdsky

ubuntu18.04 php pdo : could not find driver

用PHP PDO打开数据库,出现错误 could not find driver 一个原因是没安装对应的PDO 解决方法 #查看php版本 $ php -v #安装PDO sudo apt-get install php7.3-mysql sudo apt-get install php7.3-pgsql sudo apt-get install php7.3-sqlite

September 13, 2019 · 1 min · holdsky

学习笔记:Phalcon 微应用示例

创建工程 需要先安装 Phalcon DevTools 创建Test工程 phalcon project Test 然后,只保留四个文件 Test\.htaccess (访问权限控制) Test\.htrouter.php(访问路由) Test\public\.htaccess(访问权限控制) Test\public\index.php(应用入口) 一个简单微应用的示例 将 Test\public\index.php 的内容修改成: <?php //导入Micro use Phalcon\Mvc\Micro; //创建对象 $app = new Micro(); //注册路由 //GET方法,路径为 /xxxx , 响应输出 hello xxxx $app->get("/{name}", //响应方法 function ($name){ echo "hello " . $name; }); $app->handle(); 进入 Test 目录,开启服务 phalcon serve 默认情况下开启的是8000端口,用浏览器访问 http://127.0.0.1:8000/world 应能看到输出 hello world 进一步,自定义微应用的路由 微应用使用起来很简单,我们可以在这基础上抽象出自定义的路由(这里,仅仅是演示示意,不代表一定这么做),即把 $app->getxxxx 这段拆开,定义一个路由表 <?php use Phalcon\Mvc\Micro; function func_test($name){ echo "hello " . $name; } //定义路由 $route_list = [ //GET方发 路径/xxxx 响应函数func_test ["GET", "/{name}", "func_test"], ]; $app = new Micro(); //注册路由 foreach ($route_list as $item) { if ($item[0] == "GET") { $app->get($item[1],$item[2]); } else { //错误处理; } } $app->handle(); 再次用浏览器访问 http://127.0.0.1:8000/world,看看效果

September 12, 2019 · 1 min · holdsky

VS Code 对 Phalcon的语法自动补全

官方没有找到很好的支持(Phalcon只有一个对PhpStorm的支持),想到一个折中方案 下载https://github.com/phalcon/ide-stubs.git 把src目录下面的Phalcon目录整体复制到Phalcon的工程目录中 然后在VS Code中安装扩展插件 PHP Intelephense

September 11, 2019 · 1 min · holdsky

mac OS 安装php7.3

安装homebrew方法(若已经安装,忽略) /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 通过homebrew安装 brew install php@7.3 刷新php连接 brew link php@7.3 重启shell,查看php版本 php -v

September 10, 2019 · 1 min · holdsky

git+ssh:为仓库或域名指定ssh验证信息

默认情况下,ssh会默认读取 ~/.ssh/id_rsa 验证信息(私钥文件),如何指定特定的验证信息呢? ssh shell 指定验证信息文件 此方法适用于支持ssh shell访问的服务 ssh -i 私钥文件路径 git@xxxxx.com 启用config文件 参考 https://linux.die.net/man/5/ssh_config 此方法可以为不同的域名指定不同的验证信息;在git服务支持的情况下,还可以为不同的git仓库指定不同的验证信息 1、创建config文件 vim ~/.ssh/config 内容如下 #仓库A的验证信息(也可以是指定域名的验证信息) Host 名字A HostName 域名或者ip地址 User 用户名 IdentityFile 私钥文件路径 # Host 名字B HostName 域名或者ip地址 User 用户名 Port 22 IdentityFile 私钥文件路径 2、ssh 访问git仓库 以clone为例,访问名字A对应的仓库 git clone git@名字A:xxx/xxx/repo.git ssh会根据 名字A 找到config文件中对应的验证信息进行验证

September 10, 2019 · 1 min · holdsky

部署ubuntu + nginx + php-fpm + phalcon

参考连接 1、 https://docs.phalcon.io/3.4/en/installation 2、 http://phalcondoc.p2hp.com/zh/3.4 3、 https://github.com/phalcon/phalcon-devtools 最终环境 ubuntu 18.04.1 nginx 1.14.0 (Ubuntu) php7.3 和 php7.3-fpm phalcon3.4.4-1 安装nginx 如果已经安装,请忽略 sudo apt-get install nginx 安装php和php-fpm 如果已经安装,请忽略 添加php源 sudo apt-add-repository ppa:ondrej/php sudo apt-get update 安装 #安装php7.3 sudo apt-get install php7.3 #安装php-fpm sudo apt-get install php-fpm 安装phalcon sudo apt-get install php7.3-phalcon 重启php-fpm 这个步骤是要把phalcon模块注册到php-fpm sudo systemctl reload php7.3-fpm.service 安装phalcon-devtools(脚手架) 从git安装 #下载 git clone --depth=1 https://github.com/phalcon/phalcon-devtools.git #进入目录 cd phalcon-devtools/ #创建软连接 sudo ln -s $(pwd)/phalcon /usr/bin/phalcon #修改权限 sudo chmod ugo+x /usr/bin/phalcon #执行命令,查看是否安装成功 phalcon commands help 如果提示 "phalcon: command not found" 尝试命令 ...

September 9, 2019 · 2 min · holdsky

学习笔记: Ubuntu 18 部署 Apache2 + Python 3.6 + Django

记录下过程 安装Python3.6 正常情况下,Python3.6是自带的 $ sudo apt-get install python3.6 创建Python3虚拟环境 ubuntu自带Python2和Python3,默认情况下Python2的优先级高 需要虚拟一个Python3的环境 # 安装virtualenv $ sudo apt-get install virtualenv # 创建python3.6虚拟环境 -p指定python3.6解释器路径 $ virtualenv -p /usr/bin/python3.6 envPython3.6 # 激活Python3.6环境 $ cd envPython3.6 $ source bin/activate #查看当前版本 $ python -V 注: 如果不使用虚拟环境,也可提升Python3.6的优先级 如果有多个Python3.x,调整Python3的优先级,使得3.6优先级较高 $ sudo update-alternatives –install /usr/bin/python3 python3 /usr/bin/python3.5 1 $ sudo update-alternatives –install /usr/bin/python3 python3 /usr/bin/python3.6 2 更改默认值,python默认为Python2,现在修改为Python3 $ sudo update-alternatives –install /usr/bin/python python /usr/bin/python2 100 $ sudo update-alternatives –install /usr/bin/python python /usr/bin/python3 150 ...

September 9, 2019 · 2 min · holdsky

学习笔记:Ubuntu18 安装 phalcon7

参考连接 https://docs.phalcon.io/4.0/en/installation sudo apt-get update sudo apt-get install php7.2-phalcon

September 9, 2019 · 1 min · holdsky

学习笔记:ubuntu18 安装nginx + mysql + wordpress

参考连接 1、 https://www.linuxidc.com/Linux/2015-01/112587.htm 2、 https://www.centos.bz/2018/07/%E4%B8%80%E6%AC%A1ubuntunginx%E6%90%AD%E5%BB%BAwordpress%E7%9A%84%E7%BB%8F%E5%8E%86/ 3、 https://www.cnblogs.com/ldj3/p/9298734.html 4、 https://wordpress.org/support/article/how-to-install-wordpress/ 最终环境 ubuntu 18.04.1 nginx 1.14.0 (Ubuntu) mysql server 5.7.27-0ubuntu0.18.04.1 (Ubuntu) php7.3 和 php7.3-fpm 和php7.3-mysql wordpress 5.2.3(安装时为最新版本) 安装nginx sudo apt-get install nginx 可以用浏览器打开 http://127.0.0.1 来测试nginx是否安装成功 安装mysql 安装server sudo apt-get install mysql-server 安全配置 sudo mysql_secure_installation 按照提示操作,配置密码、测试库、匿名用户、远程访问等 修改mysql数据库路径(数据迁移,可选) 1、查看当前数据库路径 #root用户登陆 sudo mysql -u root -p #查看路径 > show variables like '%dir%'; #退出 > exit; 输出类似结果 +-----------------------------------------+----------------------------+ | Variable_name | Value | +-----------------------------------------+----------------------------+ | basedir | /usr/ | | binlog_direct_non_transactional_updates | OFF | | character_sets_dir | /usr/share/mysql/charsets/ | | datadir | /var/lib/mysql/ | ...... +-----------------------------------------+----------------------------+ 其中 datadir是需要修改的部分 ...

September 5, 2019 · 2 min · holdsky