美文网首页线段树
并查集、字典树和线段树

并查集、字典树和线段树

作者: 漫游之光 | 来源:发表于2019-06-15 19:45 被阅读0次

这三个数据结构用的不多,使用范围不是很广,但这三个数据结构都是树的结构,所以就写在一起了。

并查集

并查集主要是为了解决动态连通性问题,这里主要是利用了一个技巧,就是用一个数组来表示连通的集合。这样,合并就抽象为将一个集合的值变为另一个集合。判断两个变量是否连通抽象为两个变量是否属于同一个集合。这里的实现是使用一个数组来记录父节点,如果父节点是本身,那么这个值就代表一个集合。这里进行了一点优化,就是在合并的时候,将小树合并到大树。

class UnionFind{
public:
    UnionFind(int n):cnt(n){
        for(int i=0;i<n;i++){
            id.push_back(i);
            size.push_back(1);
        }
    }

    int find(int p){
        while(p != id[p]){
            p = id[p];
        }
        return p;
    }

    void merge(int p, int q){
        int i = find(p);
        int j = find(q);
        if(i == j){
            return;
        }
        if(size[i] < size[j]){
            id[i] = j;
            size[j] += size[i];
        }else{
            id[j] = i;
            size[i] += size[j]; 
        }
        cnt--;
    }

    int count(){
        return cnt;
    }

private:
    vector<int> id;
    vector<int> size;
    int cnt;
};

字典树

字典树的实现个人认为还是比较简单,主要是递归的应用,其实这里是可以不使用递归的,但是为了简单起见,使用递归可以使得思路更加清晰。这里需要注意的一点是,使用连接代表字母,而不是使用结点代表字母;结点只是表示从root到该结点的之间组成的单词是否存在。

class Trie {
public:
    Trie():root(NULL){}

    ~Trie(){
        delete root;
    }

    void insert(string word) {
        root = put(root,word,0);
    }
    
    bool search(string word) {
        return get(root,word,0);
    }
    
    bool startsWith(string prefix) {
        return find(root,prefix,0);
    }

    void delWord(string word){
        root = del(root,word,0);
    }

private:
    class Node{
    public:
        Node():isEnd(false){
            for(int i=0;i<26;i++){
                child[i] = NULL;
            }
        }
        ~Node(){
            for(int i=0;i<26;i++){
                if(child[i] != NULL){
                    delete child[i];
                }
            }
        }
        Node *child[26];
        bool isEnd;
    };

    Node *root;

    Node* put(Node *node, string word, int n){
        if(node == NULL){
            node = new Node();
        }    
        if(n == word.size()){
            node->isEnd = true;
        }else{
            int pos = word[n] - 'a';
            node->child[pos] = put(node->child[pos],word,n+1);
        }
        return node;
    }

    bool get(Node *node, string word, int n){
        if(node == NULL){
            return false;
        }
        if(n == word.size()){
            return node->isEnd;
        }else{
            int pos = word[n] - 'a';
            return get(node->child[pos],word,n+1);
        }
    }

    bool find(Node *node, string word, int n){
        if(node == NULL){
            return false;
        }
        if(n == word.size()){
            return true;
        }else{
            int pos = word[n] - 'a';
            return find(node->child[pos],word,n+1);
        }
    }

    Node* del(Node *node, string word, int n){
        if(node == NULL){
            return NULL;
        }
        if(n == word.size()){
            node->isEnd = false;
        }else{
            int pos = word[n] - 'a';
            node->child[pos] = del(node->child[pos],word,n+1);
        }
        if(node->isEnd == true){
            return node;
        }
        for(int i=0;i<26;i++){
            if(node->child[i] != NULL){
                return node;
            }
        }
        delete node;
        return NULL;
    }
};

线段树

线段树是一个完全二叉树,它的基本含义是,用结点所在位置表示一个范围,二叉树的根结点位置表示从left到right的范围,结点的值代表这个范围之上的属性,如最大值。所以,这里的二叉树采用的是数组的形式实现。线段树适用的场景是,需要实时查询某个范围的值,并且需要更新某个值的场景。下面是具体的实现,结点表示某个范围的最大值。

/* 线段树数组一般是原数组的四倍 */
void buildTree(vector<int> &v, vector<int> &num, int pos, int left, int right){
    if(left == right){
        v[pos] = num[left];
        return;
    }
    int mid = (right + left) / 2;
    buildTree(v,num,pos*2+1,left,mid);
    buildTree(v,num,pos * 2 + 2,mid+1,right);
    v[pos] = max(v[pos * 2 +1],v[pos * 2 + 2]);
}

int searchTree(vector<int> &v,int pos, int left, int right, int qleft, int qright){
    if(qleft > right || qright < left){
        return 0;
    }
    if(qleft <= left && qright >= right){
        return v[pos];
    }
    int mid = (left + right) / 2;
    return max(searchTree(v,pos * 2 + 1,left,mid,qleft,qright),searchTree(v,pos*2+2,mid+1,right,qleft,qright));
}

void updateTree(vector<int> &v,int pos,int left,int right,int index,int value){
    if(index == left && index == right){
        v[pos] = value;
        return;
    }
    int mid = (right + left) / 2;
    if(index <= mid){
        updateTree(v,pos * 2 +1 ,left,mid,index,value);
    }else{
        updateTree(v,pos * 2 +2,mid+1,right,index,value);
    }
    v[pos] = max(v[pos*2+1],v[pos * 2 + 2]);
}

相关文章

  • 并查集、字典树和线段树

    这三个数据结构用的不多,使用范围不是很广,但这三个数据结构都是树的结构,所以就写在一起了。 并查集 并查集主要是为...

  • 高级数据结构1—Trie(字典树)

    这个高级数据结构系列我会写三中在编程里常见的三种数据机构,分别是Trie 树,并查集和线段树。 trie树(字典树...

  • Swift 数据结构与算法实现

    用 Swift 实现了 Trie 字典树、并查集、堆和优先队列、哈希表、红黑树、集合与映射、链表、数组、栈、队列、...

  • 算法12-字典树和并查集

    《算法文章汇总》[https://www.jianshu.com/p/fc7c0e8cc5cb] 字典树,即Tri...

  • C++ 实现并查集

    并查集 并查集实际上就是并集和查集的过程。集(集合)可以理解为一棵树,即一个根结点连着无数个子节点。 例题解析 输...

  • 神奇的数据结构---并查集

    并查集(Union Find)何为集,集合,用树表示;何为并,集合的合并(union);何为查,查询元素所属集合(...

  • c++中并查集实现

    何谓并查集 并查集实际上就是并集和查集的过程。那么什么是集呢?你可以把他近似地理解为一棵树。即一个根结点连着无数个...

  • 并查集的第二次理解

    并查集的本质是树形结构,正因为是树形结构,所以并查集内部不能够成环。这里还要说明无向图和树的关系:树是一种特殊的图...

  • 并查集

    什么是并查集?并查集是一种树型的数据结构,可以解决连接问题,网络中节点间的链接状态。它和之前的树不太一样,它是子孩...

  • 红黑树和TRIE树以及并查集

    红黑树:https://www.cnblogs.com/skywang12345/p/3245399.htmlTR...

网友评论

    本文标题:并查集、字典树和线段树

    本文链接:https://www.haomeiwen.com/subject/kvehxctx.html