时间序列线段树

来源:互联网 发布:java中io流详解视频 编辑:程序博客网 时间:2024/06/06 01:41

可以以O(log2(n))的时间复杂度处理任何日期序列的区间点修改和统计问题,例如维护区间和、区间内最大最小值平均数、区间内最大公因数、区间内的逆序数个数。以下为一个维护时间序列RMQ(区间最小值)的例子

<?php/** * Description: 时间序列线段树 * */define("INFINITE", pow(2, 64));class TSSegTree {    private $begin;    private $end;    private $segTree = array();    private $delay_mark = array();    public static $debug_count = 0;    public function __construct($begin, $end, $init_arr=array()) {        $this->begin = $begin;        $this->end   = $end;        $this->build(0, $this->begin, $this->end, $init_arr);    }    private function lchild($father) {        return $father * 2 + 1;    }    private function rchild($father) {        return $father * 2 + 2;    }    // 取两个日期的中间日期    private function middate($begin, $end) {        $interval = (strtotime($end)-strtotime($begin)) / 86400;        $interval = intval($interval / 2);        return $this->nextnday($begin, $interval);    }    private function nextnday($date, $n=1) {        $operator = $n >= 0? '+' : '-';        return date('Y-m-d', strtotime("{$date} {$operator}{$n} day"));    }    // 更新当前节点值    public function pushUp($root) {        $lchild = $this->lchild($root);        $rchild = $this->rchild($root);        $this->segTree[$root] = min($this->segTree[$lchild], $this->segTree[$rchild]);    }    // 构建线段树    public function build($root = 0, $begin, $end, $init_arr=array()) {        $this->delay_mark[$root] = 0;        if (strtotime($begin) == strtotime($end)) {            $this->segTree[$root] = $init_arr[$begin];        } else {            $mid = $this->middate($begin, $end);            $this->build($root * 2 + 1, $begin, $mid, $init_arr);            $this->build($root * 2 + 2,  $this->nextnday($mid), $end, $init_arr);            $this->pushUp($root);        }    }    // 区间查询    public function query($root, $nbegin, $nend, $qbegin, $qend) {        if (strtotime($qbegin) > strtotime($nend) ||              strtotime($qend) < strtotime($nbegin) ) {                return INFINITE;        }        if (strtotime($qbegin) <= strtotime($nbegin) &&              strtotime($qend) >= strtotime($nend) ) {              return $this->segTree[$root];        }        $this->pushDown($root);        $mid = $this->middate($nbegin, $nend);        return min($this->query($this->lchild($root), $nbegin, $mid, $qbegin, $qend),              $this->query($this->rchild($root), $this->nextnday($mid), $nend, $qbegin, $qend ));    }    // 单节点更新    public function updateNode($root, $nbegin, $nend, $index, $val) {        if (strtotime($nbegin) == strtotime($nend)) {            if (strtotime($index) == strtotime($nbegin)) {                $this->segTree[$root] += $val;                // or other update method            }            return;        }        $mid = $this->middate($nbegin, $nend);        if (strtotime($index) <= strtotime($mid)) {            $this->updateNode($this->lchild($root), $nbegin, $mid, $index, $val);        } else {            $this->updateNode($this->rchild($root), $this->nextnday($mid), $nend, $index, $val);        }        $this->pushUp($root);        return;    }    // 向子节点更新标志域    public function pushDown($root) {        if ($this->delay_mark[$root] != 0) {            $this->delay_mark[$this->lchild($root)] += $this->delay_mark[$root];            $this->delay_mark[$this->rchild($root)] += $this->delay_mark[$root];            // change value            $this->segTree[$this->lchild($root)] += $this->delay_mark[$root];            $this->segTree[$this->rchild($root)] += $this->delay_mark[$root];            $this->delay_mark[$root] = 0;        }    }    // 区间更新    public function updateRange($root, $nbegin, $nend, $ubegin, $uend, $val) {        if ($ubegin > $nend || $uend < $nbegin) {            return;        }        if ($ubegin <= $nbegin && $uend >= $nend) {            $this->segTree[$root] += $val;            $this->delay_mark[$root] += $val;            return;        }        $this->pushDown($root);        $mid = $this->middate($nbegin, $nend);        $this->updateRange($this->lchild($root), $nbegin, $mid, $ubegin, $uend, $val);        $this->updateRange($this->rchild($root), $this->nextnday($mid), $nend, $ubegin, $uend, $val);        $this->pushUp($root);    }    // 输出值    public function dumpTree($root, $begin, $end) {        echo "{$begin} ~ {$end} : root: {$root}, val: {$this->segTree[$root]} ";        echo "\r\n";        if (strtotime($begin) == strtotime($end)) {            return;        } else {            $mid = $this->middate($begin, $end);            $this->dumpTree($this->lchild($root), $begin, $mid);            $this->dumpTree($this->rchild($root), $this->nextnday($mid), $end);        }        return;    }}function test() {    $dataset = array(        '2017-01-01' => 6,        '2017-01-02' => 2,        '2017-01-03' => 5,        '2017-01-04' => 8,        '2017-01-05' => 12,        '2017-01-06' => 9,        '2017-01-07' => 10,        '2017-01-08' => 11    );    $tree = new TSSegTree('2017-01-01', '2017-01-08', $dataset);    $tree->updateNode(0, '2017-01-01', '2017-01-08', '2017-01-04', 2);    $result = $tree->query(0, '2017-01-01', '2017-01-08', '2017-01-03', '2017-01-06');    echo $result;    $tree->dumpTree(0, '2017-01-01', '2017-01-08');    $tree->updateRange(0, '2017-01-01', '2017-01-08', '2017-01-03', '2017-01-06', -1);    $result = $tree->query(0, '2017-01-01', '2017-01-08', '2017-01-03', '2017-01-06');    echo $result;    echo "\r\n";    $tree->dumpTree(0, '2017-01-01', '2017-01-08');}test();?>


0 0
原创粉丝点击