高级数据结构-字典树-并查集-线段树

一,字典树

1.指针数组

2.路径存储,不是结点

3.遍历:前序遍历

4.获取单词:深搜+栈,递归深搜每一个字符(位置)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

【实现Trie树】

LeetCode 208. Implement Trie (Prefix Tree)

1.前=后,就是前结点指向后结点

在这里插入图片描述

#define TRIE_MAX_CHAR_NUM 26
struct TrieNode{
        TrieNode*child[TRIE_MAX_CHAR_NUM];
        bool is_end;
        TrieNode():is_end(false){//结构体初始化
             for(int i=0;i<TRIE_MAX_CHAR_NUM;i++){
                 child[i]=0;
             }
        }
};
class Trie {
private:
    vector<TrieNode*>_node_vec;
    TrieNode _root;
    TrieNode*new_node(){
        TrieNode*node=new TrieNode();
        _node_vec.push_back(node);//都放在一个容器中,方便析构
        return node;
    }
public:
    /** Initialize your data structure here. */
    Trie() {

    }
    ~Trie(){
        for(int i=0;i<_node_vec.size();i++){
            delete _node_vec[i];    
        }
    }
    
    /** Inserts a word into the trie. */
    void insert(string word) {
        TrieNode*ptr=&_root;
        for(char ch:word){
            int pos=ch-'a';
            if(!ptr->child[pos]){
                 ptr->child[pos]=new_node();//没有就新建一个
            }
            //有孩子就是有前缀了
            ptr=ptr->child[pos];//如果有孩子,把ptr结点指向他的孩子【这个操作怎么懵了呢?????????】
        }
        ptr->is_end=true;
    }
    
    /** Returns if the word is in the trie. */
    bool search(string word) {
        TrieNode*ptr=&_root;
        for(char ch:word){
            int pos=ch-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];//当前结点没有就继续往下走
        }
        return ptr->is_end;
    }
    
    /** Returns if there is any word in the trie that starts with the given prefix. */
    bool startsWith(string prefix) {
        TrieNode*ptr=&_root;
        for(char ch:prefix){
            int pos=ch-'a';
            if(!ptr->child[pos]){
                return false;
            }
            ptr=ptr->child[pos];
        }
        return true;
    }
};

/**
 * Your Trie object will be instantiated and called as such:
 * Trie* obj = new Trie();
 * obj->insert(word);
 * bool param_2 = obj->search(word);
 * bool param_3 = obj->startsWith(prefix);
 */

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/implement-trie-prefix-tree/solution/shi-xian-trieqian-zhui-shu-by-xia-mu-lao-dsgx/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

【添加与搜索单词】

LeetC ode 211. Add and Search Word - Data structure design

1.for(auto c:s)用法

for(auto a:b)中b为一个容器,效果是利用a遍历并获得b容器中的每一个值,但是a无法影响到b容器中的元素。

for(auto &a:b)中加了引用符号,可以对容器中的内容进行赋值,即可通过对a赋值来做到容器b的内容填充。

2.递归深搜正则匹配

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

struct Node{
    bool isEnd;
    Node*next[26];
    Node(){
        for(int i=0;i<26;i++){
            next[i]=nullptr;
        }
        isEnd=false;
    }
};
class WordDictionary {
public:
    /** Initialize your data structure here. */
    
    Node root;

    WordDictionary() {
        
    }
    
    void addWord(string word) {
        Node*cur=&root;
        for(auto c:word){
            if(cur->next[c-'a']==nullptr){
                cur->next[c-'a']=new Node();
            }
            cur=cur->next[c-'a'];//cur指向下一【相应】结点
        }
        cur->isEnd=true;
    }
    bool searchDFS(int start,string&word,Node*root){
        if(start==word.size()){
            return root->isEnd;
        }
        if(word[start]=='.'){
            for(int j=0;j<26;j++){//逐个递归
                if(root->next[j]&&searchDFS(start+1,word,root->next[j])){//如果该节点有孩子,且可向下递归,一直递归到底
                    return true;
                }
            }
        }
        else{
            char c=word[start];
            if(root->next[c-'a']==nullptr){//如果没孩子,就结束
                return false;
            }
            //如果有孩子,就向下递归
            return searchDFS(start+1,word,root->next[c-'a']);
        }
        return false;
    }
    bool search(string word) {
        return searchDFS(0,word,&root);
    }
};

/**
 * Your WordDictionary object will be instantiated and called as such:
 * WordDictionary* obj = new WordDictionary();
 * obj->addWord(word);
 * bool param_2 = obj->search(word);
 */

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/design-add-and-search-words-data-structure/solution/sou-suo-he-tian-jia-dan-ci-zi-dian-shu-b-a31p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

二,并查集

LeetC ode 547. Friend Circles
在这里插入图片描述

图的深搜

在这里插入图片描述

void DFS_graph(int u,vector<vector<int> >&graph,vector<int>&visit){
    visit[u]=1;//当前元素
    for(int i=0;i<graph[u].size();i++){
        if(visit[i]==0&&graph[u][i]==1){//当前元素和每个元素
            DFS_graph(i,graph,visit);
        }
    }
}
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        vector<int>visit(isConnected.size(),0);
        int count=0;
        for(int i=0;i<isConnected.size();i++){
            if(visit[i]==0){
                DFS_graph(i,isConnected,visit);
                count++;
            }
        }
        return count;
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/number-of-provinces/solution/sheng-fen-shu-liang-by-xia-mu-lao-zhang-tzdl1/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0.并查集概述

并查集(Union Find),又称不相交集合(Disjiont Set),它应用于N个元素的集合求并与查询问题,在该应用场景中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。虽然该问题并不复杂,但面对极大的数据量时,普通的数据结构往往无法解决,并查集就是解决该种问题最为优秀的算法。
在这里插入图片描述

在这里插入图片描述

1.数组实现并查集(查找与合并)

在这里插入图片描述

2.森林实现:使用森林存储集合之间的关系,属于同一集合的不同元素,都有一个相同的根节点,代表着这个集合。

3.路径压缩,树更加平衡

4.查找算法,合并算法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

写了一个类,初始化,成员函数,成员变量

class DisjoinSet{
public:
    DisjoinSet(int n){
        for(int i=0;i<n;i++){
            _id.push_back(i);
            _size.push_back(1);//初始单个元素都是一个集合,且大小为1
        }
        _count=n;
    }
    int find(int p){
        while(p!=_id[p]){//没找到,就往上找
            _id[p]=_id[_id[p]];//怎么往上找
            p=_id[p];//将p的父节点更新为【父节点的父节点】
        }
        return p;
    }
    void union_(int p,int q){
        int i=find(p);
        int j=find(q);
        if(i==j){
            return;
        }
        //小规模的指向大规模的,尽量平衡
        if(_size[i]<_size[j]){
            _id[i]=j;//_id[i]的根结点指向j
            _size[j]+=_size[i];
        }
        else{
            _id[j]=i;
            _size[i]+=_size[j];
        }
        _count--;
    }
    int count(){
        return _count;
    }
    
private:
    vector<int> _id;
    vector<int>_size;
    int _count;
};
class Solution {
public:
    int findCircleNum(vector<vector<int>>& isConnected) {
        DisjoinSet disjiont_set(isConnected.size());//创建对象并初始化
        for(int i=0;i<isConnected.size();i++){
            for(int j=i+1;j<isConnected.size();j++){//两两比较
                if(isConnected[i][j]){
                    disjiont_set.union_(i,j);
                }
            }
        }
        return disjiont_set.count();
    }
};

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/number-of-provinces/solution/sheng-fen-shu-liang-bing-cha-ji-by-xia-m-mf2j/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

三,线段树

0.线段树概述

线段树是一种平衡二叉搜索树(完全二叉树),它将一个线段区间划分成一些单元区间。对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b],最后的叶子节点数目为N,与数组下标对应。线段树的一般包括建立、查询、插入、更新等操作,建立规模为N的时间复杂度是O(NlogN ),其他操作时间复杂度为O(logN )。

【区域和的查询】

LeetCode 307. Range Sum Query – Mutable
在这里插入图片描述

1.平衡二叉树

2.数组下标相当于指针

3.递归更新求和

4.注意完全二叉树和数组下标的数量关系

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

void build_segment_tree(vector<int>&value,vector<int>&nums,int pos,int left,int right){
    if(left==right){
        value[pos]=nums[left];//pos相当于指针//nums[right]都一样,看图
        return;
    }
    int mid=(left+right)/2;
    build_segment_tree(value,nums,pos*2+1,left,mid);//左孩子区间[left,mid]
    build_segment_tree(value,nums,pos*2+2,mid+1,right);
    value[pos]=value[pos*2+1]+value[pos*2+2];//递归回溯每个区间都更新
}
int sum_range_segment_tree(vector<int>&value,int pos,int left,int right,int qleft,int qright){//lef,right代表当前区间
    if(qleft>right||qright<left){//不在区间返回0
        return 0;
    }
    if(qleft<=left&&qright>=right){//区间覆盖
        return value[pos];
    }
    int mid=(left+right)/2;//在范围内继续分割
    return sum_range_segment_tree(value,pos*2+1,left,mid,qleft,qright)+sum_range_segment_tree(value,pos*2+2,mid+1,right,qleft,qright);

}
void update_segment_tree(vector<int>&value,int pos,int left,int right,int index,int new_value){//更新点的同时还要更新相应路径上的和
    if(left==right&&left==index)//left==righ是指的一个位置,找到该点位置,right==index也行
    {
        value[pos]=new_value;
    return;
    }
    int mid=(left+right)/2;
    if(index<=mid){//index在左区间
        update_segment_tree(value,pos*2+1,left,mid,index,new_value); //递归左孩子和      
    }
    else{
        update_segment_tree(value,pos*2+2,mid+1,right,index,new_value);
    }
    value[pos]=value[pos*2+1]+value[pos*2+2];
}
class NumArray {
private:
    vector<int> _value;
    int _right_end;
public:
    NumArray(vector<int>& nums) {
        if(nums.size()==0){
            return;
        }
        int n=nums.size()*4;//一般线段树数组大小是原数组的4倍
        for(int i=0;i<n;i++){
            _value.push_back(0);
        }
        build_segment_tree(_value,nums,0,0,nums.size()-1);
        _right_end=nums.size()-1;//线段的右端点,即为数组长度-1
    }
    
    void update(int index, int val) {
        update_segment_tree(_value,0,0,_right_end,index,val);
    }
    
    int sumRange(int left, int right) {
        return sum_range_segment_tree(_value,0,0,_right_end,left,right);
    }
};

/**
 * Your NumArray object will be instantiated and called as such:
 * NumArray* obj = new NumArray(nums);
 * obj->update(index,val);
 * int param_2 = obj->sumRange(left,right);
 */

作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/range-sum-query-mutable/solution/xian-duan-shu-de-qiu-he-ke-xiu-gai-by-xi-g8jz/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

四,树状数组【待完成】

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值