PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】

本文实例讲述了PHP实现绘制二叉树图形显示功能。分享给大家供大家参考,具体如下:

前言:

最近老师布置了一个作业:理解并实现平衡二叉树和红黑树,本来老师是说用C#写的,但是我学的C#基本都还给老师了,怎么办?那就用现在最熟悉的语言PHP来写吧!

有一个问题来了,书上在讲解树的时候基本上会给出形象的树形图。但是当我们自己试着实现某种树,在调试、输出的时候确只能以字符的形式顺序地输出。这给调试等方面带来了很大的不便。然后在各种百度之后,我发现利用PHP实现二叉树的图形显示的资源几乎是零!好吧,那我就自己个儿实现一个!

效果显示:

如果我是直接在这一步摆代码的话,估计大家会比较烦闷,那我就直接上结果吧,后面在补代码,先激发激发大家的阅读兴趣:

1、搜索二叉树:

2、平衡二叉树:

3、红黑树:

上代码:

我们给图片创建一个类吧,显得稍微的小高级:

image.php 文件:

<?php
/**
 * author:LSGOZJ
 * description: 绘制二叉树图像
 */
class image
{
  //树相关设置
  //每层之间的间隔高度
  private $level_high = 100;
  //最底层叶子结点之间的宽度
  private $leaf_width = 50;
  //结点圆的半径
  private $rad = 20;
  //根节点离边框顶端距离
  private $leave = 20;
  //树(保存树对象的引用)
  private $tree;
  //树的层数
  private $level;
  //完全二叉树中最底层叶子结点数量(计算图像宽度时用到,论如何实现图片大小自适应)
  private $maxCount;
  //图像相关设置
  //画布宽度
  private $width;
  //画布高度
  private $height;
  //画布背景颜色(RGB)
  private $bg = array(
    220, 220, 220
  );
  //节点颜色(搜索二叉树和平衡二叉树时用)
  private $nodeColor = array(
    255, 192, 203
  );
  //图像句柄
  private $image;
  /**
   * 构造函数,类属性初始化
   * @param $tree 传递一个树的对象
   * @return null
   */
  public function __construct($tree)
  {
    $this->tree = $tree;
    $this->level = $this->getLevel();
    $this->maxCount = $this->GetMaxCount($this->level);
    $this->width = ($this->rad * 2 * $this->maxCount) + $this->maxCount * $this->leaf_width;
    $this->height = $this->level * ($this->rad * 2) + $this->level_high * ($this->level - 1) + $this->leave;
    //1.创建画布
    $this->image = imagecreatetruecolor($this->width, $this->height); //新建一个真彩色图像,默认背景是黑色
    //填充背景色
    $bgcolor = imagecolorallocate($this->image, $this->bg[0], $this->bg[1], $this->bg[2]);
    imagefill($this->image, 0, 0, $bgcolor);
  }
  /**
   * 返回传进来的树对象对应的完全二叉树中最底层叶子结点数量
   * @param $level 树的层数
   * @return 结点数量
   */
  function GetMaxCount($level)
  {
    return pow(2, $level - 1);
  }
  /**
   * 获取树对象的层数
   * @param null
   * @return 树的层数
   */
  function getLevel()
  {
    return $this->tree->Depth();
  }
  /**
   * 显示二叉树图像
   * @param null
   * @return null
   */
  public function show()
  {
    $this->draw($this->tree->root, 1, 0, 0);
    header("Content-type:image/png");
    imagepng($this->image);
    imagedestroy($this->image);
  }
  /**
   * (递归)画出二叉树的树状结构
   * @param $root,根节点(树或子树) $i,该根节点所处的层 $p_x,父节点的x坐标 $p_y,父节点的y坐标
   * @return null
   */
  private function draw($root, $i, $p_x, $p_y)
  {
    if ($i <= $this->level) {
      //当前节点的y坐标
      $root_y = $i * $this->rad + ($i - 1) * $this->level_high;
      //当前节点的x坐标
      if (!is_null($parent = $root->parent)) {
        if ($root == $parent->left) {
          $root_x = $p_x - $this->width / (pow(2, $i));
        } else {
          $root_x = $p_x + $this->width / (pow(2, $i));
        }
      } else {
        //根节点
        $root_x = (1 / 2) * $this->width;
        $root_y += $this->leave;
      }
      //画结点(确定所画节点的类型(平衡、红黑、排序)和方法)
      $method = 'draw' . get_class($this->tree) . 'Node';
      $this->$method($root_x, $root_y, $root);
      //将当前节点和父节点连线(黑色线)
      $black = imagecolorallocate($this->image, 0, 0, 0);
      if (!is_null($parent = $root->parent)) {
        imageline($this->image, $p_x, $p_y, $root_x, $root_y, $black);
      }
      //画左子节点
      if (!is_null($root->left)) {
        $this->draw($root->left, $i + 1, $root_x, $root_y);
      }
      //画右子节点
      if (!is_null($root->right)) {
        $this->draw($root->right, $i + 1, $root_x, $root_y);
      }
    }
  }
  /**
   * 画搜索二叉树结点
   * @param $x,当前节点的x坐标 $y,当前节点的y坐标 $node,当前节点的引用
   * @return null
   */
  private function drawBstNode($x, $y, $node)
  {
    //节点圆的线颜色
    $black = imagecolorallocate($this->image, 0, 0, 0);
    $nodeColor = imagecolorallocate($this->image, $this->nodeColor[0], $this->nodeColor[1], $this->nodeColor[2]);
    //画节点圆
    imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
    //节点圆颜色填充
    imagefill($this->image, $x, $y, $nodeColor);
    //节点对应的数字
    imagestring($this->image, 4, $x, $y, $node->key, $black);
  }
  /**
   * 画平衡二叉树结点
   * @param $x,当前节点的x坐标 $y,当前节点的y坐标 $node,当前节点的引用
   * @return null
   */
  private function drawAvlNode($x, $y, $node)
  {
    $black = imagecolorallocate($this->image, 0, 0, 0);
    $nodeColor = imagecolorallocate($this->image, $this->nodeColor[0], $this->nodeColor[1], $this->nodeColor[2]);
    imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
    imagefill($this->image, $x, $y, $nodeColor);
    imagestring($this->image, 4, $x, $y, $node->key . '(' . $node->bf . ')', $black);
  }
  /**
   * 画红黑树结点
   * @param $x,当前节点的x坐标 $y,当前节点的y坐标 $node,当前节点的引用
   * @return null
   */
  private function drawRbtNode($x, $y, $node)
  {
    $black = imagecolorallocate($this->image, 0, 0, 0);
    $gray = imagecolorallocate($this->image, 180, 180, 180);
    $pink = imagecolorallocate($this->image, 255, 192, 203);
    imageellipse($this->image, $x, $y, $this->rad * 2, $this->rad * 2, $black);
    if ($node->IsRed == TRUE) {
      imagefill($this->image, $x, $y, $pink);
    } else {
      imagefill($this->image, $x, $y, $gray);
    }
    imagestring($this->image, 4, $x, $y, $node->key, $black);
  }
}

好,现在我们来看看在客户端如何调用:

client.php

class Client
{
  public static function Main()
  {
    try {
      //实现文件的自动加载
      function autoload($class)
      {
        include strtolower($class) . '.php';
      }
      spl_autoload_register('autoload');
      $arr = array(62, 88, 58, 47, 35, 73, 51, 99, 37, 93);
//      $tree = new Bst();  //搜索二叉树
      $tree = new Avl();  //平衡二叉树
//      $tree = new Rbt();  //红黑树
      $tree->init($arr);   //树的初始化
//      $tree->Delete(62);
//      $tree->Insert(100);
//      $tree->MidOrder();  //树的中序遍历(这也是调试的一个手段,看看数字是否从小到大排序)
      $image = new image($tree);
      $image->show();  //显示图像
    } catch (Exception $e) {
      echo $e->getMessage();
    }
  }
}
Client::Main();

这里用到的那三个树的类如下:

二叉搜索树bst.php:

<?php
 /**
 * author:zhongjin
 * description: 二叉查找树
 */
//结点
class Node
{
  public $key;
  public $parent;
  public $left;
  public $right;
  public function __construct($key)
  {
    $this->key = $key;
    $this->parent = NULL;
    $this->left = NULL;
    $this->right = NULL;
  }
}
//二叉搜索树
class Bst
{
  public $root;
  /**
   * 初始化树结构
   * @param $arr 初始化树结构的数组
   * @return null
   */
  public function init($arr)
  {
    $this->root = new Node($arr[0]);
    for ($i = 1; $i < count($arr); $i++) {
      $this->Insert($arr[$i]);
    }
  }
  /**
   * (对内)中序遍历
   * @param $root (树或子树的)根节点
   * @return null
   */
  private function mid_order($root)
  {
    if ($root != NULL) {
      $this->mid_order($root->left);
      echo $root->key . " ";
      $this->mid_order($root->right);
    }
  }
  /**
   * (对外)中序遍历
   * @param null
   * @return null
   */
  public function MidOrder()
  {
    $this->mid_order($this->root);
  }
  /**
   * 查找树中是否存在$key对应的节点
   * @param $key 待搜索数字
   * @return $key对应的节点
   */
  function search($key)
  {
    $current = $this->root;
    while ($current != NULL) {
      if ($current->key == $key) {
        return $current;
      } elseif ($current->key > $key) {
        $current = $current->left;
      } else {
        $current = $current->right;
      }
    }
    return $current;
  }
  /**
   * 查找树中的最小关键字
   * @param $root 根节点
   * @return 最小关键字对应的节点
   */
  function search_min($root)
  {
    $current = $root;
    while ($current->left != NULL) {
      $current = $current->left;
    }
    return $current;
  }
  /**
   * 查找树中的最大关键字
   * @param $root 根节点
   * @return 最大关键字对应的节点
   */
  function search_max($root)
  {
    $current = $root;
    while ($current->right != NULL) {
      $current = $current->right;
    }
    return $current;
  }
  /**
   * 查找某个$key在中序遍历时的直接前驱节点
   * @param $x 待查找前驱节点的节点引用
   * @return 前驱节点引用
   */
  function predecessor($x)
  {
    //左子节点存在,直接返回左子节点的最右子节点
    if ($x->left != NULL) {
      return $this->search_max($x->left);
    }
    //否则查找其父节点,直到当前结点位于父节点的右边
    $p = $x->parent;
    //如果x是p的左孩子,说明p是x的后继,我们需要找的是p是x的前驱
    while ($p != NULL && $x == $p->left) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * 查找某个$key在中序遍历时的直接后继节点
   * @param $x 待查找后继节点的节点引用
   * @return 后继节点引用
   */
  function successor($x)
  {
    if ($x->right != NULL) {
      return $this->search_min($x->right);
    }
    $p = $x->parent;
    while ($p != NULL && $x == $p->right) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * 将$key插入树中
   * @param $key 待插入树的数字
   * @return null
   */
  function Insert($key)
  {
    if (!is_null($this->search($key))) {
      throw new Exception('结点' . $key . '已存在,不可插入!');
    }
    $root = $this->root;
    $inode = new Node($key);
    $current = $root;
    $prenode = NULL;
    //为$inode找到合适的插入位置
    while ($current != NULL) {
      $prenode = $current;
      if ($current->key > $inode->key) {
        $current = $current->left;
      } else {
        $current = $current->right;
      }
    }
    $inode->parent = $prenode;
    //如果$prenode == NULL, 则证明树是空树
    if ($prenode == NULL) {
      $this->root = $inode;
    } else {
      if ($inode->key < $prenode->key) {
        $prenode->left = $inode;
      } else {
        $prenode->right = $inode;
      }
    }
    //return $root;
  }
  /**
   * 在树中删除$key对应的节点
   * @param $key 待删除节点的数字
   * @return null
   */
  function Delete($key)
  {
    if (is_null($this->search($key))) {
      throw new Exception('结点' . $key . "不存在,删除失败!");
    }
    $root = $this->root;
    $dnode = $this->search($key);
    if ($dnode->left == NULL || $dnode->right == NULL) { #如果待删除结点无子节点或只有一个子节点,则c = dnode
      $c = $dnode;
    } else { #如果待删除结点有两个子节点,c置为dnode的直接后继,以待最后将待删除结点的值换为其后继的值
      $c = $this->successor($dnode);
    }
    //无论前面情况如何,到最后c只剩下一边子结点
    if ($c->left != NULL) {
      $s = $c->left;
    } else {
      $s = $c->right;
    }
    if ($s != NULL) { #将c的子节点的父母结点置为c的父母结点,此处c只可能有1个子节点,因为如果c有两个子节点,则c不可能是dnode的直接后继
      $s->parent = $c->parent;
    }
    if ($c->parent == NULL) { #如果c的父母为空,说明c=dnode是根节点,删除根节点后直接将根节点置为根节点的子节点,此处dnode是根节点,且拥有两个子节点,则c是dnode的后继结点,c的父母就不会为空,就不会进入这个if
      $this->root = $s;
    } else if ($c == $c->parent->left) { #如果c是其父节点的左右子节点,则将c父母的左右子节点置为c的左右子节点
      $c->parent->left = $s;
    } else {
      $c->parent->right = $s;
    }
    #如果c!=dnode,说明c是dnode的后继结点,交换c和dnode的key值
    if ($c != $dnode) {
      $dnode->key = $c->key;
    }
    #返回根节点
//    return $root;
  }
  /**
   * (对内)获取树的深度
   * @param $root 根节点
   * @return 树的深度
   */
  private function getdepth($root)
  {
    if ($root == NULL) {
      return 0;
    }
    $dl = $this->getdepth($root->left);
    $dr = $this->getdepth($root->right);
    return ($dl > $dr ? $dl : $dr) + 1;
  }
  /**
   * (对外)获取树的深度
   * @param null
   * @return null
   */
  public function Depth()
  {
    return $this->getdepth($this->root);
  }
}
?>

平衡二叉树avl.php:

<?php
 /**
 * author:zhongjin
 * description: 平衡二叉树
 */
//结点
class Node
{
  public $key;
  public $parent;
  public $left;
  public $right;
  public $bf; //平衡因子
  public function __construct($key)
  {
    $this->key = $key;
    $this->parent = NULL;
    $this->left = NULL;
    $this->right = NULL;
    $this->bf = 0;
  }
}
//平衡二叉树
class Avl
{
  public $root;
  const LH = +1; //左高
  const EH = 0;  //等高
  const RH = -1; //右高
  /**
   * 初始化树结构
   * @param $arr 初始化树结构的数组
   * @return null
   */
  public function init($arr)
  {
    $this->root = new Node($arr[0]);
    for ($i = 1; $i < count($arr); $i++) {
      $this->Insert($arr[$i]);
    }
  }
  /**
   * (对内)中序遍历
   * @param $root (树或子树的)根节点
   * @return null
   */
  private function mid_order($root)
  {
    if ($root != NULL) {
      $this->mid_order($root->left);
      echo $root->key . "-" . $root->bf . " ";
      $this->mid_order($root->right);
    }
  }
  /**
   * (对外)中序遍历
   * @param null
   * @return null
   */
  public function MidOrder()
  {
    $this->mid_order($this->root);
  }
  /**
   * 将以$root为根节点的最小不平衡二叉树做右旋处理
   * @param $root(树或子树)根节点
   * @return null
   */
  private function R_Rotate($root)
  {
    $L = $root->left;
    if (!is_NULL($root->parent)) {
      $P = $root->parent;
      if ($root == $P->left) {
        $P->left = $L;
      } else {
        $P->right = $L;
      }
      $L->parent = $P;
    } else {
      $L->parent = NULL;
    }
    $root->parent = $L;
    $root->left = $L->right;
    $L->right = $root;
    //这句必须啊!
    if ($L->parent == NULL) {
      $this->root = $L;
    }
  }
  /**
   * 将以$root为根节点的最小不平衡二叉树做左旋处理
   * @param $root(树或子树)根节点
   * @return null
   */
  private function L_Rotate($root)
  {
    $R = $root->right;
    if (!is_NULL($root->parent)) {
      $P = $root->parent;
      if ($root == $P->left) {
        $P->left = $R;
      } else {
        $P->right = $R;
      }
      $R->parent = $P;
    } else {
      $R->parent = NULL;
    }
    $root->parent = $R;
    $root->right = $R->left;
    $R->left = $root;
    //这句必须啊!
    if ($R->parent == NULL) {
      $this->root = $R;
    }
  }
  /**
   * 对以$root所指结点为根节点的二叉树作左平衡处理
   * @param $root(树或子树)根节点
   * @return null
   */
  public function LeftBalance($root)
  {
    $L = $root->left;
    $L_bf = $L->bf;
    switch ($L_bf) {
      //检查root的左子树的平衡度,并作相应的平衡处理
      case self::LH:  //新结点插入在root的左孩子的左子树上,要做单右旋处理
        $root->bf = $L->bf = self::EH;
        $this->R_Rotate($root);
        break;
      case self::RH:  //新节点插入在root的左孩子的右子树上,要做双旋处理
        $L_r = $L->right;  //root左孩子的右子树根
        $L_r_bf = $L_r->bf;
        //修改root及其左孩子的平衡因子
        switch ($L_r_bf) {
          case self::LH:
            $root->bf = self::RH;
            $L->bf = self::EH;
            break;
          case self::EH:
            $root->bf = $L->bf = self::EH;
            break;
          case self::RH:
            $root->bf = self::EH;
            $L->bf = self::LH;
            break;
        }
        $L_r->bf = self::EH;
        //对root的左子树作左平衡处理
        $this->L_Rotate($L);
        //对root作右平衡处理
        $this->R_Rotate($root);
    }
  }
  /**
   * 对以$root所指结点为根节点的二叉树作右平衡处理
   * @param $root(树或子树)根节点
   * @return null
   */
  public function RightBalance($root)
  {
    $R = $root->right;
    $R_bf = $R->bf;
    switch ($R_bf) {
      //检查root的右子树的平衡度,并作相应的平衡处理
      case self::RH:  //新结点插入在root的右孩子的右子树上,要做单左旋处理
        $root->bf = $R->bf = self::EH;
        $this->L_Rotate($root);
        break;
      case self::LH:  //新节点插入在root的右孩子的左子树上,要做双旋处理
        $R_l = $R->left;  //root右孩子的左子树根
        $R_l_bf = $R_l->bf;
        //修改root及其右孩子的平衡因子
        switch ($R_l_bf) {
          case self::RH:
            $root->bf = self::LH;
            $R->bf = self::EH;
            break;
          case self::EH:
            $root->bf = $R->bf = self::EH;
            break;
          case self::LH:
            $root->bf = self::EH;
            $R->bf = self::RH;
            break;
        }
        $R_l->bf = self::EH;
        //对root的右子树作右平衡处理
        $this->R_Rotate($R);
        //对root作左平衡处理
        $this->L_Rotate($root);
    }
  }
  /**
   * 查找树中是否存在$key对应的节点
   * @param $key 待搜索数字
   * @return $key对应的节点
   */
  public function search($key)
  {
    $current = $this->root;
    while ($current != NULL) {
      if ($current->key == $key) {
        return $current;
      } elseif ($current->key > $key) {
        $current = $current->left;
      } else {
        $current = $current->right;
      }
    }
    return $current;
  }
  /**
   * 查找树中的最小关键字
   * @param $root 根节点
   * @return 最小关键字对应的节点
   */
  function search_min($root)
  {
    $current = $root;
    while ($current->left != NULL) {
      $current = $current->left;
    }
    return $current;
  }
  /**
   * 查找树中的最大关键字
   * @param $root 根节点
   * @return 最大关键字对应的节点
   */
  function search_max($root)
  {
    $current = $root;
    while ($current->right != NULL) {
      $current = $current->right;
    }
    return $current;
  }
  /**
   * 查找某个$key在中序遍历时的直接前驱节点
   * @param $x 待查找前驱节点的节点引用
   * @return 前驱节点引用
   */
  private function predecessor($x)
  {
    //左子节点存在,直接返回左子节点的最右子节点
    if ($x->left != NULL) {
      return $this->search_max($x->left);
    }
    //否则查找其父节点,直到当前结点位于父节点的右边
    $p = $x->parent;
    //如果x是p的左孩子,说明p是x的后继,我们需要找的是p是x的前驱
    while ($p != NULL && $x == $p->left) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * 查找某个$key在中序遍历时的直接后继节点
   * @param $x 待查找后继节点的节点引用
   * @return 后继节点引用
   */
  private function successor($x)
  {
    if ($x->left != NULL) {
      return $this->search_min($x->right);
    }
    $p = $x->parent;
    while ($p != NULL && $x == $p->right) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * (对内)插入结点,如果结点不存在则插入,失去平衡要做平衡处理
   * @param $root 根节点 $key 待插入树的数字
   * @return null
   */
  private function insert_node(&$root, $key)
  {
    //找到了插入的位置,插入新节点
    if (is_null($root)) {
      $root = new Node($key);
      //插入结点成功
      return TRUE;
    } else {
      //在树中已经存在和$key相等的结点
      if ($key == $root->key) {
        //插入节点失败
        return FALSE;
      } //在root的左子树中继续搜索
      elseif ($key < $root->key) {
        //插入左子树失败
        if (!($this->insert_node($root->left, $key))) {
          //树未长高
          return FALSE;
        }
        //成功插入,修改平衡因子
        if (is_null($root->left->parent)) {
          $root->left->parent = $root;
        }
        switch ($root->bf) {
          //原来左右子树等高,现在左子树增高而树增高
          case self::EH:
            $root->bf = self::LH;
            //树长高
            return TRUE;
            break;
          //原来左子树比右子树高,需要做左平衡处理
          case self::LH:
            $this->LeftBalance($root);
            //平衡后,树并未长高
            return FALSE;
            break;
          //原来右子树比左子树高,现在左右子树等高
          case self::RH:
            $root->bf = self::EH;
            //树并未长高
            return FALSE;
            break;
        }
      } //在root的右子树中继续搜索
      else {
        //插入右子树失败
        if (!$this->insert_node($root->right, $key)) {
          //树未长高
          return FALSE;
        }
        //成功插入,修改平衡因子
        if (is_null($root->right->parent)) {
          $root->right->parent = $root;
        }
        switch ($root->bf) {
          //原来左右子树等高,现在右子树增高而树增高
          case self::EH:
            $root->bf = self::RH;
            //树长高
            return TRUE;
            break;
          //原来左子树比右子树高,现在左右子树等高
          case self::LH:
            $root->bf = self::EH;
            return FALSE;
            break;
          //原来右子树比左子树高,要做右平衡处理
          case self::RH:
            $this->RightBalance($root);
            //树并未长高
            return FALSE;
            break;
        }
      }
    }
  }
  /**
   * (对外)将$key插入树中
   * @param $key 待插入树的数字
   * @return null
   */
  public function Insert($key)
  {
    $this->insert_node($this->root, $key);
  }
  /**
   * 获取待删除的节点(删除的最终节点)
   * @param $key 待删除的数字
   * @return 最终被删除的节点
   */
  private function get_del_node($key)
  {
    $dnode = $this->search($key);
    if ($dnode == NULL) {
      throw new Exception("结点不存在!");
      return;
    }
    if ($dnode->left == NULL || $dnode->right == NULL) { #如果待删除结点无子节点或只有一个子节点,则c = dnode
      $c = $dnode;
    } else { #如果待删除结点有两个子节点,c置为dnode的直接后继,以待最后将待删除结点的值换为其后继的值
      $c = $this->successor($dnode);
    }
    $dnode->key = $c->key;
    return $c;
  }
  /**
   * (对内)删除指定节点,处理该结点往上结点的平衡因子
   * @param $node 最终该被删除的节点
   * @return null
   */
  private function del_node($node)
  {
    if ($node == $this->root) {
      $this->root = NULL;
      return;
    }
    $current = $node;
    //现在的node只有两种情况,要么只有一个子节点,要么没有子节点
    $P = $current->parent;
    //删除一个结点,第一个父节点的平衡都肯定会发生变化
    $lower = TRUE;
    while ($lower == TRUE && !is_null($P)) {
      //待删除结点是左节点
      if ($current == $P->left) {
        if($current == $node){
          if (!is_null($current->left)) {
            $P->left = $current->left;
          } else {
            $P->left = $current->left;
          }
        }
        $P_bf = $P->bf;
        switch ($P_bf) {
          case self::LH:
            $P->bf = self::EH;
            $lower = TRUE;
            $current = $P;
            $P = $current->parent;
            break;
          case self::EH:
            $P->bf = self::RH;
            $lower = FALSE;
            break;
          case self::RH:
            $this->RightBalance($P);
            $lower = TRUE;
            $current = $P->parent;
            $P = $current->parent;
            break;
        }
      } //右结点
      else {
        if($current == $node){
          if (!is_null($current->left)) {
            $P->right = $current->left;
          } else {
            $P->right = $current->left;
          }
        }
        $P_bf = $P->bf;
        switch ($P_bf) {
          case self::LH:
            $this->LeftBalance($P);
            $lower = TRUE;
            $current = $P->parent;
            $P = $current->parent;
            break;
          case self::EH:
            $P->bf = self::LH;
            $lower = FALSE;
            break;
          case self::RH:
            $P->bf = self::LH;
            $lower = TRUE;
            $current = $P;
            $P = $current->parent;
            break;
        }
      }
    }
  }
  /**
   * (对外)删除指定节点
   * @param $key 删除节点的key值
   * @return null
   */
  public function Delete($key)
  {
    $del_node = $this->get_del_node($key);
    $this->del_node($del_node);
  }
  /**
   * (对内)获取树的深度
   * @param $root 根节点
   * @return 树的深度
   */
  private function getdepth($root)
  {
    if ($root == NULL) {
      return 0;
    }
    $dl = $this->getdepth($root->left);
    $dr = $this->getdepth($root->right);
    return ($dl > $dr ? $dl : $dr) + 1;
  }
  /**
   * (对外)获取树的深度
   * @param null
   * @return null
   */
  public function Depth()
  {
    return $this->getdepth($this->root);
  }
}
?>

红黑树rbt.php:

<?php
 /**
 * author:zhongjin
 * description: 红黑树
 */
//结点
class Node
{
  public $key;
  public $parent;
  public $left;
  public $right;
  public $IsRed; //分辨红节点或黑节点
  public function __construct($key, $IsRed = TRUE)
  {
    $this->key = $key;
    $this->parent = NULL;
    $this->left = NULL;
    $this->right = NULL;
    //插入结点默认是红色
    $this->IsRed = $IsRed;
  }
}
//红黑树
class Rbt
{
  public $root;
  /**
   * 初始化树结构
   * @param $arr 初始化树结构的数组
   * @return null
   */
  public function init($arr)
  {
    //根节点必须是黑色
    $this->root = new Node($arr[0], FALSE);
    for ($i = 1; $i < count($arr); $i++) {
      $this->Insert($arr[$i]);
    }
  }
  /**
   * (对内)中序遍历
   * @param $root (树或子树的)根节点
   * @return null
   */
  private function mid_order($root)
  {
    if ($root != NULL) {
      $this->mid_order($root->left);
      echo $root->key . "-" . ($root->IsRed ? 'r' : 'b') . ' ';
      $this->mid_order($root->right);
    }
  }
  /**
   * (对外)中序遍历
   * @param null
   * @return null
   */
  public function MidOrder()
  {
    $this->mid_order($this->root);
  }
  /**
   * 查找树中是否存在$key对应的节点
   * @param $key 待搜索数字
   * @return $key对应的节点
   */
  function search($key)
  {
    $current = $this->root;
    while ($current != NULL) {
      if ($current->key == $key) {
        return $current;
      } elseif ($current->key > $key) {
        $current = $current->left;
      } else {
        $current = $current->right;
      }
    }
    //结点不存在
    return $current;
  }
  /**
   * 将以$root为根节点的最小不平衡二叉树做右旋处理
   * @param $root(树或子树)根节点
   * @return null
   */
  private function R_Rotate($root)
  {
    $L = $root->left;
    if (!is_null($root->parent)) {
      $P = $root->parent;
      if($root == $P->left){
        $P->left = $L;
      }else{
        $P->right = $L;
      }
      $L->parent = $P;
    } else {
      $L->parent = NULL;
    }
    $root->parent = $L;
    $root->left = $L->right;
    $L->right = $root;
    //这句必须啊!
    if ($L->parent == NULL) {
      $this->root = $L;
    }
  }
  /**
   * 将以$root为根节点的最小不平衡二叉树做左旋处理
   * @param $root(树或子树)根节点
   * @return null
   */
  private function L_Rotate($root)
  {
    $R = $root->right;
    if (!is_null($root->parent)) {
      $P = $root->parent;
      if($root == $P->right){
        $P->right = $R;
      }else{
        $P->left = $R;
      }
      $R->parent = $P;
    } else {
      $R->parent = NULL;
    }
    $root->parent = $R;
    $root->right = $R->left;
    $R->left = $root;
    //这句必须啊!
    if ($R->parent == NULL) {
      $this->root = $R;
    }
  }
  /**
   * 查找树中的最小关键字
   * @param $root 根节点
   * @return 最小关键字对应的节点
   */
  function search_min($root)
  {
    $current = $root;
    while ($current->left != NULL) {
      $current = $current->left;
    }
    return $current;
  }
  /**
   * 查找树中的最大关键字
   * @param $root 根节点
   * @return 最大关键字对应的节点
   */
  function search_max($root)
  {
    $current = $root;
    while ($current->right != NULL) {
      $current = $current->right;
    }
    return $current;
  }
  /**
   * 查找某个$key在中序遍历时的直接前驱节点
   * @param $x 待查找前驱节点的节点引用
   * @return 前驱节点引用
   */
  function predecessor($x)
  {
    //左子节点存在,直接返回左子节点的最右子节点
    if ($x->left != NULL) {
      return $this->search_max($x->left);
    }
    //否则查找其父节点,直到当前结点位于父节点的右边
    $p = $x->parent;
    //如果x是p的左孩子,说明p是x的后继,我们需要找的是p是x的前驱
    while ($p != NULL && $x == $p->left) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * 查找某个$key在中序遍历时的直接后继节点
   * @param $x 待查找后继节点的节点引用
   * @return 后继节点引用
   */
  function successor($x)
  {
    if ($x->left != NULL) {
      return $this->search_min($x->right);
    }
    $p = $x->parent;
    while ($p != NULL && $x == $p->right) {
      $x = $p;
      $p = $p->parent;
    }
    return $p;
  }
  /**
   * 将$key插入树中
   * @param $key 待插入树的数字
   * @return null
   */
  public function Insert($key)
  {
    if (!is_null($this->search($key))) {
      throw new Exception('结点' . $key . '已存在,不可插入!');
    }
    $root = $this->root;
    $inode = new Node($key);
    $current = $root;
    $prenode = NULL;
    //为$inode找到合适的插入位置
    while ($current != NULL) {
      $prenode = $current;
      if ($current->key > $inode->key) {
        $current = $current->left;
      } else {
        $current = $current->right;
      }
    }
    $inode->parent = $prenode;
    //如果$prenode == NULL, 则证明树是空树
    if ($prenode == NULL) {
      $this->root = $inode;
    } else {
      if ($inode->key < $prenode->key) {
        $prenode->left = $inode;
      } else {
        $prenode->right = $inode;
      }
    }
    //将它重新修正为一颗红黑树
    $this->InsertFixUp($inode);
  }
  /**
   * 对插入节点的位置及往上的位置进行颜色调整
   * @param $inode 插入的节点
   * @return null
   */
  private function InsertFixUp($inode)
  {
    //情况一:需要调整条件,父节点存在且父节点的颜色是红色
    while (($parent = $inode->parent) != NULL && $parent->IsRed == TRUE) {
      //祖父结点:
      $gparent = $parent->parent;
      //如果父节点是祖父结点的左子结点,下面的else与此相反
      if ($parent == $gparent->left) {
        //叔叔结点
        $uncle = $gparent->right;
        //case1:叔叔结点也是红色
        if ($uncle != NULL && $uncle->IsRed == TRUE) {
          //将父节点和叔叔结点都涂黑,将祖父结点涂红
          $parent->IsRed = FALSE;
          $uncle->IsRed = FALSE;
          $gparent->IsRed = TRUE;
          //将新节点指向祖父节点(现在祖父结点变红,可以看作新节点存在)
          $inode = $gparent;
          //继续while循环,重新判断
          continue;  //经过这一步之后,组父节点作为新节点存在(跳到case2)
        }
        //case2:叔叔结点是黑色,且当前结点是右子节点
        if ($inode == $parent->right) {
          //以父节点作为旋转结点做左旋转处理
          $this->L_Rotate($parent);
          //在树中实际上已经转换,但是这里的变量的指向还没交换,
          //将父节点和字节调换一下,为下面右旋做准备
          $temp = $parent;
          $parent = $inode;
          $inode = $temp;
        }
        //case3:叔叔结点是黑色,而且当前结点是父节点的左子节点
        $parent->IsRed = FALSE;
        $gparent->IsRed = TRUE;
        $this->R_Rotate($gparent);
      } //如果父节点是祖父结点的右子结点,与上面完全相反
      else {
        //叔叔结点
        $uncle = $gparent->left;
        //case1:叔叔结点也是红色
        if ($uncle != NULL && $uncle->IsRed == TRUE) {
          //将父节点和叔叔结点都涂黑,将祖父结点涂红
          $parent->IsRed = FALSE;
          $uncle->IsRed = FALSE;
          $gparent->IsRed = TRUE;
          //将新节点指向祖父节点(现在祖父结点变红,可以看作新节点存在)
          $inode = $gparent;
          //继续while循环,重新判断
          continue;  //经过这一步之后,组父节点作为新节点存在(跳到case2)
        }
        //case2:叔叔结点是黑色,且当前结点是左子节点
        if ($inode == $parent->left) {
          //以父节点作为旋转结点做右旋转处理
          $this->R_Rotate($parent);
          //在树中实际上已经转换,但是这里的变量的指向还没交换,
          //将父节点和字节调换一下,为下面右旋做准备
          $temp = $parent;
          $parent = $inode;
          $inode = $temp;
        }
        //case3:叔叔结点是黑色,而且当前结点是父节点的右子节点
        $parent->IsRed = FALSE;
        $gparent->IsRed = TRUE;
        $this->L_Rotate($gparent);
      }
    }
    //情况二:原树是根节点(父节点为空),则只需将根节点涂黑
    if ($inode == $this->root) {
      $this->root->IsRed = FALSE;
      return;
    }
    //情况三:插入节点的父节点是黑色,则什么也不用做
    if ($inode->parent != NULL && $inode->parent->IsRed == FALSE) {
      return;
    }
  }
  /**
   * (对外)删除指定节点
   * @param $key 删除节点的key值
   * @return null
   */
  function Delete($key)
  {
    if (is_null($this->search($key))) {
      throw new Exception('结点' . $key . "不存在,删除失败!");
    }
    $dnode = $this->search($key);
    if ($dnode->left == NULL || $dnode->right == NULL) { #如果待删除结点无子节点或只有一个子节点,则c = dnode
      $c = $dnode;
    } else { #如果待删除结点有两个子节点,c置为dnode的直接后继,以待最后将待删除结点的值换为其后继的值
      $c = $this->successor($dnode);
    }
    //为了后面颜色处理做准备
    $parent = $c->parent;
    //无论前面情况如何,到最后c只剩下一边子结点
    if ($c->left != NULL) {  //这里不会出现,除非选择的是删除结点的前驱
      $s = $c->left;
    } else {
      $s = $c->right;
    }
    if ($s != NULL) { #将c的子节点的父母结点置为c的父母结点,此处c只可能有1个子节点,因为如果c有两个子节点,则c不可能是dnode的直接后继
      $s->parent = $c->parent;
    }
    if ($c->parent == NULL) { #如果c的父母为空,说明c=dnode是根节点,删除根节点后直接将根节点置为根节点的子节点,此处dnode是根节点,且拥有两个子节点,则c是dnode的后继结点,c的父母就不会为空,就不会进入这个if
      $this->root = $s;
    } else if ($c == $c->parent->left) { #如果c是其父节点的左右子节点,则将c父母的左右子节点置为c的左右子节点
      $c->parent->left = $s;
    } else {
      $c->parent->right = $s;
    }
    $dnode->key = $c->key;
    $node = $s;
    //c的结点颜色是黑色,那么会影响路径上的黑色结点的数量,必须进行调整
    if ($c->IsRed == FALSE) {
      $this->DeleteFixUp($node,$parent);
    }
  }
  /**
   * 删除节点后对接点周围的其他节点进行调整
   * @param $key 删除节点的子节点和父节点
   * @return null
   */
  private function DeleteFixUp($node,$parent)
  {
    //如果待删结点的子节点为红色,直接将子节点涂黑
    if ($node != NULL && $node->IsRed == TRUE) {
      $node->IsRed = FALSE;
      return;
    }
    //如果是根节点,那就直接将根节点置为黑色即可
    while (($node == NULL || $node->IsRed == FALSE) && ($node != $this->root)) {
      //node是父节点的左子节点,下面else与这里相反
      if ($node == $parent->left) {
        $brother = $parent->right;
        //case1:兄弟结点颜色是红色(父节点和兄弟孩子结点都是黑色)
        //将父节点涂红,将兄弟结点涂黑,然后对父节点进行左旋处理(经过这一步,情况转换为兄弟结点颜色为黑色的情况)
        if ($brother->IsRed == TRUE) {
          $brother->IsRed = FALSE;
          $parent->IsRed = TRUE;
          $this->L_Rotate($parent);
          //将情况转化为其他的情况
          $brother = $parent->right; //在左旋处理后,$parent->right指向的是原来兄弟结点的左子节点
        }
        //以下是兄弟结点为黑色的情况
        //case2:兄弟结点是黑色,且兄弟结点的两个子节点都是黑色
        //将兄弟结点涂红,将当前结点指向其父节点,将其父节点指向当前结点的祖父结点。
        if (($brother->left == NULL || $brother->left->IsRed == FALSE) && ($brother->right == NULL || $brother->right->IsRed == FALSE)) {
          $brother->IsRed = TRUE;
          $node = $parent;
          $parent = $node->parent;
        } else {
          //case3:兄弟结点是黑色,兄弟结点的左子节点是红色,右子节点为黑色
          //将兄弟结点涂红,将兄弟节点的左子节点涂黑,然后对兄弟结点做右旋处理(经过这一步,情况转换为兄弟结点颜色为黑色,右子节点为红色的情况)
          if ($brother->right == NULL || $brother->right->IsRed == FALSE) {
            $brother->IsRed = TRUE;
            $brother->left->IsRed = FALSE;
            $this->R_Rotate($brother);
            //将情况转换为其他情况
            $brother = $parent->right;
          }
          //case4:兄弟结点是黑色,且兄弟结点的右子节点为红色,左子节点为任意颜色
          //将兄弟节点涂成父节点的颜色,再把父节点涂黑,将兄弟结点的右子节点涂黑,然后对父节点做左旋处理
          $brother->IsRed = $parent->IsRed;
          $parent->IsRed = FALSE;
          $brother->right->IsRed = FALSE;
          $this->L_Rotate($parent);
          //到了第四种情况,已经是最基本的情况了,可以直接退出了
          $node = $this->root;
          break;
        }
      } //node是父节点的右子节点
      else {
        $brother = $parent->left;
        //case1:兄弟结点颜色是红色(父节点和兄弟孩子结点都是黑色)
        //将父节点涂红,将兄弟结点涂黑,然后对父节点进行右旋处理(经过这一步,情况转换为兄弟结点颜色为黑色的情况)
        if ($brother->IsRed == TRUE) {
          $brother->IsRed = FALSE;
          $parent->IsRed = TRUE;
          $this->R_Rotate($parent);
          //将情况转化为其他的情况
          $brother = $parent->left; //在右旋处理后,$parent->left指向的是原来兄弟结点的右子节点
        }
        //以下是兄弟结点为黑色的情况
        //case2:兄弟结点是黑色,且兄弟结点的两个子节点都是黑色
        //将兄弟结点涂红,将当前结点指向其父节点,将其父节点指向当前结点的祖父结点。
        if (($brother->left == NULL || $brother->left->IsRed == FALSE) && ($brother->right == NULL || $brother->right->IsRed == FALSE)) {
          $brother->IsRed = TRUE;
          $node = $parent;
          $parent = $node->parent;
        } else {
          //case3:兄弟结点是黑色,兄弟结点的右子节点是红色,左子节点为黑色
          //将兄弟结点涂红,将兄弟节点的左子节点涂黑,然后对兄弟结点做左旋处理(经过这一步,情况转换为兄弟结点颜色为黑色,右子节点为红色的情况)
          if ($brother->left == NULL || $brother->left->IsRed == FALSE) {
            $brother->IsRed = TRUE;
            $brother->right = FALSE;
            $this->L_Rotate($brother);
            //将情况转换为其他情况
            $brother = $parent->left;
          }
          //case4:兄弟结点是黑色,且兄弟结点的左子节点为红色,右子节点为任意颜色
          //将兄弟节点涂成父节点的颜色,再把父节点涂黑,将兄弟结点的右子节点涂黑,然后对父节点左左旋处理
          $brother->IsRed = $parent->IsRed;
          $parent->IsRed = FALSE;
          $brother->left->IsRed = FALSE;
          $this->R_Rotate($parent);
          $node = $this->root;
          break;
        }
      }
    }
    if ($node != NULL) {
      $this->root->IsRed = FALSE;
    }
  }
  /**
   * (对内)获取树的深度
   * @param $root 根节点
   * @return 树的深度
   */
  private function getdepth($root)
  {
    if ($root == NULL) {
      return 0;
    }
    $dl = $this->getdepth($root->left);
    $dr = $this->getdepth($root->right);
    return ($dl > $dr ? $dl : $dr) + 1;
  }
  /**
   * (对外)获取树的深度
   * @param null
   * @return null
   */
  public function Depth()
  {
    return $this->getdepth($this->root);
  }
}
?>

更多关于PHP相关内容感兴趣的读者可查看本站专题:《PHP数据结构与算法教程》、《php程序设计算法总结》、《php字符串(string)用法总结》、《PHP数组(Array)操作技巧大全》、《PHP常用遍历算法与技巧总结》及《PHP数学运算技巧总结》

希望本文所述对大家PHP程序设计有所帮助。

(0)

相关推荐

  • php FLEA中二叉树数组的遍历输出

    但是要怎样遍历这个方法产生的二叉树数组呢?以下是我的做法: 复制代码 代码如下: <?php function preTree($cat){ foreach ($cat as $c){ ?> <p><a href="http://<?=$c['poper_site']?>"><?=$c['poper']?></a>:<?=t($c['content'])?></p> <?php if(

  • php实现的二叉树遍历算法示例

    本文实例讲述了php实现的二叉树遍历算法.分享给大家供大家参考,具体如下: 今天使用php来实现二叉树的遍历 创建的二叉树如下图所示 php代码如下所示: <?php class Node { public $value; public $child_left; public $child_right; } final class Ergodic { //前序遍历:先访问根节点,再遍历左子树,最后遍历右子树:并且在遍历左右子树时,仍需先遍历根节点,然后访问左子树,最后遍历右子树 public s

  • PHP完全二叉树定义与实现方法示例

    本文实例讲述了PHP完全二叉树定义与实现方法.分享给大家供大家参考,具体如下: 若设二叉树的深度为h,除第 h 层外,其它各层 (1-h-1) 的结点数都达到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树. PHP代码实现(暂时实现添加节点.层次遍历节点,删除节点后续更新) <?php class Node{ public $value; public $leftNode; public $rightNode; } /* 找到空节点 */ function findEmpyt

  • PHP实现二叉树的深度优先与广度优先遍历方法

    本文实例讲述了PHP实现二叉树的深度优先与广度优先遍历方法.分享给大家供大家参考.具体如下: #二叉树的广度优先遍历 #使用一个队列实现 class Node { public $data = null; public $left = null; public $right = null; } #@param $btree 二叉树根节点 function breadth_first_traverse($btree) { $traverse_data = array(); $queue = arr

  • PHP构造二叉树算法示例

    树(Tree)在数据结构还是很重要的,这里表示二叉树用括号表示法表示.先写一个二叉树节点类: // 二叉树节点 class BTNode { public $data; public $lchild = NULL; public $rchild = NULL; public function __construct($data) { $this->data = $data; } } 然后构造二叉树: function CreateBTNode(&$root,string $str) { $s

  • PHP Class&Object -- 解析PHP实现二叉树

    二叉树及其变体是数据结构家族里的重要组成部分.最为链表的一种变体,二叉树最适合处理需要一特定次序快速组织和检索的数据. 复制代码 代码如下: <?php// Define a class to implement a binary treeclass Binary_Tree_Node {    // Define the variable to hold our data:    public $data;    // And a variable to hold the left and ri

  • PHP实现的线索二叉树及二叉树遍历方法详解

    本文实例讲述了PHP实现的线索二叉树及二叉树遍历方法.分享给大家供大家参考,具体如下: <?php require 'biTree.php'; $str = 'ko#be8#tr####acy#####'; $tree = new BiTree($str); $tree->createThreadTree(); echo $tree->threadList() . "\n";从第一个结点开始遍历线索二叉树 echo $tree->threadListReserv

  • PHP Class&Object -- PHP 自排序二叉树的深入解析

    在节点之间再应用一些排序逻辑,二叉树就能提供出色的组织方式.对于每个节点,都让满足所有特定条件的元素都位于左节点及其子节点.在插入新元素时,我们需要从树的第一个节 点(根节点)开始,判断它属于哪一侧的节点,然后沿着这一侧找到恰当的位置,类似地,在读取数据时,只需要使用按序遍历方法来遍历二叉树. 复制代码 代码如下: <?phpob_start();// Here we need to include the binary tree classClass Binary_Tree_Node() { 

  • PHP实现绘制二叉树图形显示功能详解【包括二叉搜索树、平衡树及红黑树】

    本文实例讲述了PHP实现绘制二叉树图形显示功能.分享给大家供大家参考,具体如下: 前言: 最近老师布置了一个作业:理解并实现平衡二叉树和红黑树,本来老师是说用C#写的,但是我学的C#基本都还给老师了,怎么办?那就用现在最熟悉的语言PHP来写吧! 有一个问题来了,书上在讲解树的时候基本上会给出形象的树形图.但是当我们自己试着实现某种树,在调试.输出的时候确只能以字符的形式顺序地输出.这给调试等方面带来了很大的不便.然后在各种百度之后,我发现利用PHP实现二叉树的图形显示的资源几乎是零!好吧,那我就

  • JavaScript实现的DOM树遍历方法详解【二叉DOM树、多叉DOM树】

    本文实例讲述了JavaScript实现的DOM树遍历方法.分享给大家供大家参考,具体如下: 二叉 DOM 树的遍历 function Tree() { var Node = function(key){ this.key = key; this.left = null; this.right = null; } root =null; } 前序遍历 首先访问根结点,然后遍历左子树,最后遍历右子树 Tree.prototype.preOrderTraverse = function(callbac

  • Android开发 OpenGL ES绘制3D 图形实例详解

    OpenGL ES是 OpenGL三维图形API 的子集,针对手机.PDA和游戏主机等嵌入式设备而设计. Ophone目前支持OpenGL ES 1.0 ,OpenGL ES 1.0 是以 OpenGL 1.3 规范为基础的,OpenGL ES 1.1 是以 OpenGL 1.5 规范为基础的.本文主要介绍利用OpenGL ES绘制图形方面的基本步骤. 本文内容由三部分构成.首先通过EGL获得OpenGL ES的编程接口;其次介绍构建3D程序的基本概念;最后是一个应用程序示例. OpenGL E

  • C语言判定一棵二叉树是否为二叉搜索树的方法分析

    本文实例讲述了C语言判定一棵二叉树是否为二叉搜索树的方法.分享给大家供大家参考,具体如下: 问题 给定一棵二叉树,判定该二叉树是否是二叉搜索树(Binary Search Tree)? 解法1:暴力搜索 首先说明一下二叉树和二叉搜索树的区别.二叉树指这样的树结构,它的每个结点的孩子数目最多为2个:二叉搜索树是一种二叉树,但是它有附加的一些约束条件,这些约束条件必须对每个结点都成立: 结点node的左子树所有结点的值都小于node的值. 结点node的右子树所有结点的值都大于node的值. 结点n

  • IOS 中CALayer绘制图片的实例详解

    IOS 中CALayer绘制图片的实例详解 CALayer渲染内容图层.与UIImageView相比,不具有事件响应功能,且UIImageView是管理内容. 注意事项:如何使用delegate对象执行代理方法进行绘制,切记需要将delegate设置为nil,否则会导致异常crash. CALayer绘制图片与线条效果图: 代码示例: CGPoint position = CGPointMake(160.0, 200.0); CGRect bounds = CGRectMake(0.0, 0.0

  • R语言学习ggplot2绘制统计图形包全面详解

    目录 一.序 二.ggplot2是什么? 三.ggplot2能画出什么样的图? 四.组装机器 五.设计图纸 六.机器的零件 1. 零件--散点图 1) 变换颜色 2) 拟合曲线 3) 变换大小 4) 修改透明度 5) 分层 6) 改中文 2. 零件--直方图与条形图 1) 直方图 2) 润色 3) 条形图 3. 零件--饼图 4. 零件--箱线图 5. 零件--小提琴图 6. 零件打磨 7. 超级变变变 8. 其他常用零件 七.实践出真知 八.学习资源 九.参考资料 一.序 作为一枚统计专业的学

  • Flutter利用Canvas绘制精美表盘效果详解

    目录 前言 初始化 面板 刻度 刻度线 刻度值 指针 时针 分针 秒针 动起来 前言 趁着周末空闲时间使用 Flutter 的 Canvas制作了一个精美表盘. 最终实现的效果还不错,如下: 前面说到使用 Canvas 实现该表盘效果,而在 Flutter 中使用 Canvas 更多的则是继承 CustomPainter 类实现 paint 方法,然后在 CustomPaint 中使用自定义实现的 CustomPainter. 比如这里创建的 DialPainter 使用如下: @overrid

  • Python Matplotlib绘制动画的代码详解

    目录 matplotlib 动画 人口出生率 男女人口总数 雨滴 matplotlib 动画 我们想制作一个动画,其中正弦和余弦函数在屏幕上逐步绘制.首先需要告诉matplotlib我们想要制作一个动画,然后必须指定想要在每一帧绘制什么.一个常见的错误是重新绘制每一帧的所有内容,这会使整个过程非常缓慢.相反地,只能更新必要的内容,因为我们知道许多内容不会随着帧的变化而改变.对于折线图,我们将使用set_data方法更新绘图,剩下的工作由matplotlib完成. 注意随着动画移动的终点标记.原因

  • Java实现规则几何图形的绘制与周长面积计算详解

    目录 1.背景 2.开发工具 3.数据存储设计 4.项目功能设计 5.部分代码展示 6.项目结构 7.总结 1.背景 规则几何图形问题求解的程序是对根据输入规则几何图形的一些特定的参数来实现对规则几何图形的面积和周长求解,以及根据输入的参数对对他们进行绘制相应的图形. 在程序中通过规则几何的类型来输入相应的参数有程序得到相应的解和图形.这从而达到了对图形的计算便利计算和直观的求出图形,从而帮助计算人员拥有更高的计算效率. 关键字:Java,swing,规则几何图形,文件操作 2.开发工具 本程序

  • Python可视化绘制图表的教程详解

    目录 1.Matplotlib 程序包 2.绘图命令的基本架构及其属性设置 3.Seaborn 模块介绍 3.1 未加Seaborn 模块的效果 4.描述性统计图形概览 4.1制作数据 4.2 频数分析 python 有许多可视化工具,但本书只介绍Matplotlib.Matplotlib是一种2D的绘图库,它可以支持硬拷贝和跨系统的交互,它可以在python脚本,IPython的交互环境下.Web应用程序中使用.该项目是由John Hunter 于2002年启动,其目的是为python构建MA

随机推荐