Perl 前綴樹實現(2)

在前一篇 Perl 前綴樹實現 中用hash table的方法實現了前綴樹,算法導論中用數組來實現,方法基本相同,下邊用鏈表的方法來實現,遍歷算法可以用到其他樹結構遍歷。

代碼:

use Data::Dumper;

# 默認只支持字符串的輸入
sub trie_tree{
    my $root = {
        char=>undef,
        next=>undef,
        child=>undef,
        count=>0,
    };

    for(@_){
        insert($root, $_);
    }
    return $root;
}

# 方便起見,每次只處理一個連續字符串
sub insert{
    my ($tree,$str) = @_;
    # 不包含字符串情況下返回
    if($str !~ /^[a-zA-Z]+$/){
        return;
    }
    else{
        # 所有節點都存小寫字符
        my @tmp = split //,lc $str;

        my $root = $tree;
        for(@tmp){
            # 子節點爲空或者第一個子節點字符大於插入字符
            if(! $root->{child} || $root->{child}->{char} gt $_){
                $root->{child} = {
                    char=>$_,
                    next=>$root->{child},
                    child=>undef,
                    count=>0,
                };
                $root = $root->{child};
            }
            else{
                $root = $root->{child};

                while($root->{char} ne $_){
                    if($root->{next} && $root->{next}->{char} le $_){
                        $root = $root->{next};
                    }
                    else{ last; }
                }

                if($root->{char} ne $_){
                    $root->{next} = {
                        char=>$_,
                        next=>$root->{next},
                        child=>undef,
                        count=>0,
                    };
                    $root = $root->{next};
                }                   
            }
        }
        $root->{count}++;
    }
}

# 返回值
# undef 完整字符串未出現
# 0 查找字符串作爲前綴字符串出現
# N 字符串出現N次
sub search{
    my ($tree,$str) = @_;

    if($str !~ /^[a-zA-Z]+$/){
        return;
    }
    else{
        my @tmp = split //,lc $str;

        my $root = $tree;
        for(@tmp){
            if(! $root->{child}){
                return;
            }
            else{
                $root = $root->{child};

                while($root->{char} ne $_){
                    if($root->{next}){
                        $root = $root->{next};
                    }
                    else{ last; }
                }

                if($root->{char} ne $_){
                    return
                }                   
            }
        }
        return $root->{count};
    }
}

sub travel{
    my ($tree) = @_;

    if(!$tree->{child}){
        return
    }
    else{
        my @node = ($tree->{child});

        while(@node){
            if($node[-1]->{count} > 0){
                print join '', map {$_->{char}} @node;
                print ' => ',$node[-1]->{count},"\n";
            }
            while($node[-1]->{child}){                   
                push @node, $node[-1]->{child};
                if($node[-1]->{count} > 0){
                    print join '', map {$_->{char}} @node;
                    print ' => ',$node[-1]->{count},"\n";
                }
            }

            while(@node){
                my $t = pop @node;
                if($t->{next}){
                    push @node, $t->{next};
                    last;
                }
            }
        }
    }
}

my $tree = trie_tree(qw(this is just a testing));
insert($tree,'and');
insert($tree,'testing');
travel($tree);

做如下測試,插入40W長度爲10的字串,觀察所用內存及時間,明顯可以看到這個版本所佔用的內存更多而且時間更長。當然,由於使用了更多的hash table來表示每個單獨的節點,會佔用更多的內存,如果使用C來實現的話,相信內存使用不到Perl的1/10。

my $tree = trie_tree(qw(this is just a testing));
for(0..400000){
    my $tmp = join '', map { chr ((int rand(26)) + ord('a')) } 1..rand(10);
    insert($tree, $tmp);
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章