Trie树(字典树)概述
#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:
bool isEnd;
Trie* next[26];
public:
Trie() {
isEnd = false;
memset(next, 0, sizeof(next));
}
void insert(string word) {
Trie* node = this;
for (char c : word) {
if (node->next[c-'a'] == NULL) {
node->next[c-'a'] = new Trie();
}
node = node->next[c-'a'];
}
node->isEnd = true;
}
bool search(string word) {
Trie* node = this;
for (char c : word) {
node = node->next[c - 'a'];
if (node == NULL) {
return false;
}
}
return node->isEnd;
}
bool startsWith(string prefix) {
Trie* node = this;
for (char c : prefix) {
node = node->next[c-'a'];
if (node == NULL) {
return false;
}
}
return true;
}
};
Trie树前序遍历
Trie树获取全部单词
深度优先搜Trie树,然后用栈存单词
Trie树单词插入:
LeetCode 211 添加与搜索单词数据结构设计
并查集概述:
并查集可以用数组实现:
时间复杂度O(n)
并查集得森林实现:
利用路径压缩将森林压缩得扁平(p得下一个结点=p得下一个结点得下一个结点 即可实现路径压缩)
#include"pch.h"
#include<iostream>
#include<vector>
using namespace std;
class DisjoinSet {
public:
DisjoinSet(int n) {
for (int i = 0; i < n; i++) {
_id.push_back(i);
_size.push_back(1);
}
_count = n;
}
int find(int p) {
while (p != _id[p]) {
_id[p] = _id[_id[p]];
p = _id[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;
_size[j] += _size[i];
}
else {
_id[j] = i;
_size[i] += _size[j];
}
_count--;
}
private:
vector<int> _id;
vector<int> _size;
int _count;
};
例1:朋友圈(LeetCode 547-中等)
题目:
班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。
给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。
示例:
输入:
[[1,1,0],
[1,1,0],
[0,0,1]]
输出: 2
说明:已知学生0和学生1互为朋友,他们在一个朋友圈。
第2个学生自己在一个朋友圈。所以返回2。
思想:
1.DFD
2.并查集
代码:
利用深搜思想
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
vector<int> visit(M.size(),0);
int result=0;
for(int i=0;i<M.size();i++){
if(visit[i]==0){
DFS(M,i,visit);
result++;
}
}
return result;
}
private:
void DFS(vector<vector<int>>& graph,int u, vector<int>& visit){
visit[u]=1;
for(int i=0;i<graph[u].size();i++){
if(graph[u][i]==1 && visit[i]==0){
DFS(graph,i,visit);
}
}
}
};
线段树概述:
线段树的构造:
//线段树
void build_segment_tree(vector<int> &value, vector<int> &nums, int pos, int left, int right) {
if (left == right) {
value[pos] = nums[left];
return;
}
int mid = (left + right) / 2;
build_segment_tree(value, nums, pos * 2 + 1, left, mid);
build_segment_tree(value, nums, pos * 2 + 2, mid + 1, right);
value[pos] = value[pos * 2 + 1] + value[pos * 2 + 2];
}
线段树求和:log(n)
//线段树求和
int sum_range_segment_tree(vector<int> &value,int pos,int left,int right,int gleft,int gright) {
//没交集直接返回O
if (right<gleft || left>gright){
return 0;
}
if (left>=gleft && right<=gright){
return value[pos];
}
int mid = (left + right) / 2;
return sum_range_segment_tree(value, pos * 2 + 1, left, mid, gleft, gright) +
sum_range_segment_tree(value, pos * 2 + 2, mid + 1, right, gleft, gright);
}
线段树更新: log(n)
//线段树更新
void update_segment_tree(vector<int> &value,int pos,int left,int right,int index,int new_value){
if (left == index && right == index) {
value[pos] = new_value;
return;
}
int mid = (left + right) / 2;
if (index <= mid) {
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];
}
例:区域和的查询(LeetCode307-中等)
题目:
给定一个整数数组 nums,求出数组从索引 i 到 j (i ≤ j) 范围内元素的总和,包含 i, j 两点。
update(i, val) 函数可以通过将下标为 i 的数值更新为 val,从而对数列进行修改。
示例:
Given nums = [1, 3, 5]
sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8
- 数组仅可以在 update 函数下进行修改。
- 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。
思想:
用线段树即可
代码:
class NumArray {
public:
NumArray(vector<int>& nums) {
if(nums.size()==0){
return;
}
//一般线段树数组大小是原数组大小长度的4倍
int n=nums.size()*4;
value.resize(n,0);
//构造
build_segment_tree(value,nums,0,0,nums.size()-1);
right_end=nums.size()-1;
}
void update(int i, int val) {
update_segment_tree(value,0,0,right_end,i,val);
}
int sumRange(int i, int j) {
return sum_segment_tree(value,0,0,right_end,i,j);
}
private:
//数据
vector<int> value;
int right_end;
private:
//初始化线段树
void build_segment_tree(vector<int>& value,vector<int>& nums,int pos, int left,int right){
if(left==right){
value[pos]=nums[left];
return;
}
int mid=(left+right)/2;
build_segment_tree(value,nums,pos*2+1,left,mid);
build_segment_tree(value,nums,pos*2+2,mid+1,right);
value[pos]=value[pos*2+1]+value[pos*2+2];
}
//更新线段树
void update_segment_tree(vector<int>& value,int pos,int left,int right, int index,int new_value){
if(left==index && right==index){
value[pos]=new_value;
return;
}
int mid=(left+right)/2;
if(index<=mid){
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];
return;
}
//线段树求和
int sum_segment_tree(vector<int>& value,int pos ,int left,int right,int gleft,int gright){
if(right<gleft || left>gright){
return 0;
}
if(right<=gright && left >=gleft){
return value[pos];
}
int mid=(left+right)/2;
return sum_segment_tree(value,pos*2+1,left,mid,gleft,gright)+
sum_segment_tree(value,pos*2+2,mid+1,right,gleft,gright);
}
};
/**
* Your NumArray object will be instantiated and called as such:
* NumArray* obj = new NumArray(nums);
* obj->update(i,val);
* int param_2 = obj->sumRange(i,j);
*/