一,字典树
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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
二,并查集
图的深搜
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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。