字典树用于统计次数、在一堆字符串中查找特定字符串等环境中,效率较高。通常从根节点开始,每个节点存入字符,该字符的下层节点仍然有一个map,用于存放第二个节点的字符,以此类推。
如图的字典树,有两个字符串,abd和ac。当然,可以在每个节点上增加结束标识,用于标识他也是一个完整的字符串。比如,在b节点的表示字段上标识结束,那么就有三个字符串,ab,ac和abd。
//字典树
struct stNode
{
bool bOver = false; //是否可以结束节点
std::map<char, stNode*> m_mapNode; //下层节点
};
int main()
{
std::vector<std::string> veVal = {"qwe", "qsd", "qsda", "aer"};
stNode *pstHead = new stNode();
for (auto &it : veVal)
{
stNode *pstNode = pstHead;
for (int i = 0; i < it.size(); ++i)
{
bool bOver = i == it.size() - 1 ? true : false;
auto itfind = pstNode->m_mapNode.find(it.at(i));
if(itfind == pstNode->m_mapNode.end())
{
stNode *pstTmp = new stNode();
pstNode->m_mapNode.insert(std::make_pair(it.at(i), pstTmp));
itfind = pstNode->m_mapNode.find(it.at(i));
if (bOver)
pstNode->bOver = true;
pstNode = pstTmp;
cout << "11111111111 " << it.at(i) << endl;
}
else
{
if (bOver)
pstNode->bOver = true;
pstNode = itfind->second;
cout << "22222222222 " << it.at(i) << endl;
}
}
}
std::string strInfo = "aer";
stNode *pstInfo = pstHead;
for (int i = 0; i < strInfo.size(); ++i)
{
auto itfind = pstInfo->m_mapNode.find(strInfo.at(i));
if (itfind == pstInfo->m_mapNode.end())
{
cout << "no" << endl; //找不到
break;
}
if (i == strInfo.size() - 1)
{
if (pstInfo->bOver)
{
cout << "yes" << endl; //找到
}
else
{
cout << "hhh" << endl; //字符存在 长度小于匹配长度
}
}
pstInfo = itfind->second;
}
return 0;
}
原本项目中的代码使用大量if elseif进行判断,但是当量较大时,if elseif采用依次遍历的方式比较,效率较低。但是switch天生不支持字符串的比较,也确实比较麻烦。关于if else和switch的区别,switch case实际上是维系了一个连续的内存空间,类似于数组,每次对比一个值,先判断是否超过最大数组下标,没有超过就直接用数组去调用。以空间换时间。当然,如果数值中间跨度较大的时候,不建议采用。例如,case 1: case 100: case 108,这样的语句建议使用if else。同理,如果我们一定要比较字符串,可以先手工哈希一次,这样就可以借助switch case来比较。
优缺点:
if elseif 可能需要遍历所有的分支查找,switch case因为生成了查找表,所以不用遍历,效率较高。
if elseif 允许任何情况的查找,switch case在查找分支相差较大的时候,浪费空间。例如,case 1: case 100:实际生成了101位的跳转表。
if elseif 允许非常量查找,switch case不支持。例如,if (10 < x && x < 100)
if elseif在多分支的时候,效率比较低,并且是逐个比较,因为在日常的代码编写过程中应当人为有意的,尽量将可能满足的分支写在靠前面的地方。虽然分支较多的时候效率较低,但是if elseif的应用场景更加灵活。switch case内部以空间换时间,在查找上效率更高。对比看来,在很多情况下,字典树是一种比较好的方法。