基础知识:
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;
}
}
};
获取全部单词:
前序遍历,放进一个栈,前序遍历压入节点,在后序遍历时弹出节点。
void get_all_word_from_trie(TrieNode *node, string &word, vector<string> &word_list){
for (int i = 0; i < TRIE_MAX_CHAR_NUM; i++){
if (node->child[i]){
word.push_back(i + 'a');
if (node->child[i]->is_end){
word_list.push_back(word);
}
get_all_word_from_trie(node, word, word_list);
word.erase(word.length()-1, 1);
}
}
}
插入
一个循环word遍历树的过程
void insert(const char *word, TrieNode root){
TrieNode *ptr = &root;
while(word){
int pos = *word - 'a';
if (!ptr->child[pos]){
ptr->child[pos] = new TrieNode();
}
ptr = ptr->child[pos];
word++;
}
ptr->is_end = true;
}
搜索
与插入类似,没找到节点返回false,到word最后字符,返回is_end的值。
bool search(const char *word, TrieNode root){
TrieNode *ptr = &root;
while(word){
int pos = *word - 'a';
if(!ptr->child[pos]){
return false;
}
ptr = ptr->child[pos];
word++;
}
return ptr->is_end;
}
前缀查询
搜索是否存在前缀,与字符串搜索类似,最后不是返回is_end而是存在就返回true。
bool startsWith(const char *word, TrieNode *root){
TrieNode *ptr = root;
while(word){
int pos = *word - 'a';
if(!ptr->child[pos]){
return false;
}
ptr = ptr->child[pos];
word++;
}
return true;
}
208. Implement Trie (Prefix Tree)
题意:
实现一个字典树,实现插入、查找、查找前缀的功能。
代码:
#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 {
public:
/** Initialize your data structure here. */
Trie() {
}
/** Inserts a word into the trie. */
void insert(string word) {
TrieNode *ptr = &root;
for(int i = 0; i < word.length(); i++){
int pos = word[i] - 'a';
if (!ptr->child[pos]){
ptr->child[pos] = new TrieNode();
}
ptr = ptr->child[pos];
}
ptr->is_end = true;
}
/** Returns if the word is in the trie. */
bool search(string word) {
TrieNode* ptr = &root;
for(int i = 0; i < word.length(); i++){
int pos = word[i] - '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(int i = 0; i < prefix.length(); i++){
int pos = prefix[i] - 'a';
if (!ptr->child[pos]){
return false;
}
ptr = ptr->child[pos];
}
return true;
}
private:
TrieNode root;
};
211. Add and Search Word - Data structure design
题意:
实现一个数据结构,可以以正则表达式的形式搜索单词。就是.可以代表任意的字母。实现数据结构的添加和查找功能。
解题思路:
实现一个tire数,如果遇到字母正常遍历word,遇到.深度优先搜索遍历所有的节点。
代码:
#define TRIE_MAX_CHAR_NUM 26
struct TrieNode{
TrieNode* child[TRIE_MAX_CHAR_NUM];
bool is_end = false;
TrieNode() : is_end(false){
for (int i = 0; i < TRIE_MAX_CHAR_NUM; i++){
child[i] = 0;
}
}
};
class WordDictionary {
public:
/** Initialize your data structure here. */
WordDictionary() {
}
~WordDictionary() {
for (int i = 0; i < node_vec.size(); i++){
delete node_vec[i];
}
}
/** Adds a word into the data structure. */
void addWord(string word) {
TrieNode *ptr = &root;
for (int i = 0; i < word.length(); i++){
int pos = word[i] - 'a';
if (!ptr->child[pos]){
ptr->child[pos] = new_node();
}
ptr = ptr->child[pos];
}
ptr->is_end = true;
}
/** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */
bool search(string word) {
TrieNode *ptr = &root;
return search_trie(word, ptr, 0);
}
bool search_trie(string word,TrieNode* node, int i){
if (word[i] == '\0'){
if (node->is_end){
return true;
}
return false;
}
if (word[i] == '.'){
for (int j = 0; j < TRIE_MAX_CHAR_NUM; j++){
if (node->child[j] && search_trie(word, node->child[j], i+1)){
return true;
}
}
}
else{
int pos = word[i] - 'a';
if (node->child[pos] && search_trie(word, node->child[pos], i+1)){
return true;
}
}
return false;
}
private:
TrieNode* new_node(){
TrieNode* node = new TrieNode();
node_vec.push_back(node);
return node;
}
TrieNode root;
vector<TrieNode *> node_vec;
};
并查集:
概念:
通过森林的形式存集合,每个点记录父节点和子节点的个数,父节点等于自己则是根节点,在相同的集合的节点有相同的根节点。
初始化:
class DisjointSet{
public:
DisjointSet(int n){
for (int i = 0; i < n; i++){
_id.push_back(i);
_size.push_back(1);
}
count = n;
}
private:
vector<int> _id;
vector<int> _size;
int count;
};
路径压缩:
在查询时,隔一个把它连到它的父节点的父节点。
在并操作时,将子节点树少的节点连到子节点数多的根节点,使数尽可能的平衡。
查找操作:
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[j] = i;
_size[i] += _size[j];
}
else{
_id[i] = j;
_size[j] += _size[i];
}
count--;
}
547. Friend Circles
解题思路1:
利用并查集,返回集合个数。
代码:
class DisjointSet{
public:
DisjointSet(int n){
for (int i = 0; i < n; i++){
_size.push_back(1);
_id.push_back(i);
}
_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[j] = i;
_size[i] += _size[j];
}
else{
_id[i] = j;
_size[j] += _size[i];
}
_count--;
}
int count(){
return _count;
}
private:
vector<int> _id;
vector<int> _size;
int _count;
};
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
DisjointSet disjoint_set(M.size());
for (int i = 0; i < M.size(); i++){
for (int j = i+1; j < M.size(); j++){
if (M[i][j]){
disjoint_set.union_(i, j);
}
}
}
return disjoint_set.count();
}
};
解题思路2:
深度优先搜素的方法:
遍历节点(如果visit数组为0),深度优先搜索该节点,设置一个visit数组,遍历过的节点的visit标记为1,计数需要经过多少次遍历可以遍历完所有的节点。
代码:
class Solution {
public:
int findCircleNum(vector<vector<int>>& M) {
vector<int> visit(M.size(), 0);
int result = 0;
for (int i = 0; i < visit.size(); i++){
if (visit[i] == 0){
DFS(M, visit, i);
result++;
}
}
return result;
}
void DFS(vector<vector<int>>& M, vector<int> &visit, int node){
visit[node] = 1;
for (int i = 0; i < M.size(); i++){
if (M[node][i] == 1 && visit[i] == 0){
DFS (M, visit, i);
}
}
}
};
线段树
线段树是一个完全二叉树