一、二叉搜索树的应用
- 暴力查找
- 排序 + 二分查找 ➡️ 底层是数组,插入和删除代价大【O(N)】
- 搜索树 ➡️ 二叉搜索树【效率最坏是O(N)】➡️ 平衡树(AVL树、红黑树)【O(logN)】➡️ 多叉平衡搜索树(B树系列)
- 哈希系列
……
key 模型 ➡️ 在不在?(如门禁系统、车库收费系统)
key/value 模型 ➡️ 通过一个值(key)找到另一个值(value)➡️ 如简单字典
std::string str;
while (std::cin >> str) //调用的是std::operator>>(string)
//std::istream重载了一个operator bool,自定义类型也可以重载成内置类型,istream可以隐式类型转换成bool值
{
auto ret = dict.Find(str);
if (ret) {
std::cout << "对应官方名称:" << ret->_val << std::endl;
}
else {
std::cout << "未查询到该词语,请重新输入" << std::endl;
}
}
//结束方式:
//1. ^C:杀掉进程
//2. ^Z加上换行:正常结束
在C++中,
std::cin
是一个输入流对象,它重载了>>
运算符用于从输入流中提取数据。当你使用std::cin >> str
时,这个表达式会尝试从输入流中读取一个值并将其存储在变量str
中。
这个表达式返回的是一个std::istream
对象的引用,即std::cin
自身。在C++中,流对象可以被用作条件表达式,在这种情况下,它们会被隐式转换为布尔值(调用std::ios::operator bool
)。以下是转换规则:
- 当流成功读取数据时,流的状态是好的,它在布尔上下文中会被转换为
true
。- 当遇到输入错误或到达文件末尾(EOF)时,流的状态会变为失败,它在布尔上下文中会被转换为
false
。
统计事物出现频次:
std::string alphabet[] = { "A", "B", "C", "D", "B", "C", "D", "C", "D", "D"};
key_val::BSTree<std::string, int> cntTree;
for (const auto& e : alphabet) {
auto ret = cntTree.Find(e);
if (nullptr == ret) {
cntTree.Insert(e, 1);
}
else {
ret->_val++;
}
}
//统计出现频次
cntTree.InOrder();
set:key 搜索树
map:key-value 搜索树
序列式容器:vector list …
关联式容器:set map …
二、set
底层是搜索树,所以实际上是“去重 + 排序”
1. 遍历和排序
set<string> s2;
s2.insert("Sup");
s2.insert("Arm");
s2.insert("Dra");
s2.insert("Whi");
s2.insert("Spi");
set<string>::iterator it = s2.begin();
while (it != s2.end()) {
cout << *it << " ";
++it;
}
cout << endl;
// 倒序排列
for (auto rit2 = s2.rbegin(); rit2 != s2.rend(); ++rit2) {
cout << *rit2 << " ";
}
cout << endl;
// 倒序排列
int arr[] = { 2,6,7,8,4 };
set<int> s3(arr, arr + sizeof(arr) / sizeof(arr[0]));
set<int>::reverse_iterator rit3 = s3.rbegin();
while (rit3 != s3.rend()) {
cout << *rit3 << " ";
++rit3;
}
cout << endl;
key_val::BSTree<string, int> bs1;
bs1.Insert("un", 1);
bs1.Insert("deux", 2);
bs1.Insert("trois", 3);
bs1.Insert("quatre", 4);
bs1.Insert("cinq", 5);
bs1.InOrder();
key_val::BSTree<string, int> bs2(bs1);
bs2.InOrder();
2. 查找和删除
find erase
set<int> s1;//去重加排序
s1.insert(2);
s1.insert(0);
s1.insert(5);
s1.insert(4);
s1.insert(4);
s1.insert(4);
s1.insert(1);
//删除最小值
s1.erase(s1.begin());
for (auto& e : s1) {
cout << e << " ";
}
cout << endl;
int x;
cin >> x;
//(1)删除一个不存在的值不会报错
//int num = s1.erase(x);
//if (0 == num) {
// cout << x << "不存在" << endl;
//}
//(2)删除一个不存在的值会报错
//s1.erase(s1.find(x));
//(3)(2)的优化版
auto pos = s1.find(x);
if (pos != s1.end()) {
s1.erase(pos);
}
else {
cout << x << "不存在" << endl;
}
//两种不同find查找值的效率
//1.算法的find 暴力查找 O(N)
auto pos1 = find(s1.begin(), s1.end(), x);
//2.set的find 平衡搜索树 O(logN)
auto pos2 = s1.find(x);
//单纯判断是否存在
if (s1.count(x)) {
cout << x << "存在" << endl;
}
else {
cout << x << "不存在" << endl;
}
lower_bound 和 upper_bound
set<int> s4;
for (int i = 1; i <= 10; i++) {
s4.insert(i * 100);
}
for (auto e = s4.begin(); e != s4.end(); e++) {
cout << *e << " ";
}
cout << endl;
auto itlow = s4.lower_bound(350);
auto itup = s4.upper_bound(800);
//删除[lower_bound(), upper_bound()]之间的数据
s4.erase(itlow, itup);
for (auto m = s4.begin(); m != s4.end(); m++) {
cout << *m << " ";
}
std::multiset
允许冗余键值,不去重
std::multiset<int> s5;//不去重 排序
s5.insert(2);
s5.insert(2);
s5.insert(0);
s5.insert(5);
s5.insert(5);
s5.insert(4);
s5.insert(4);
s5.insert(4);
s5.insert(1);
for (auto e = s5.begin(); e != s5.end(); e++) {
cout << *e << " ";
}
相等的值继续插入,插入在左边或右边都可以,因为旋转后插入在右边也可能会旋转到左边。
multiset 和 set 的区别:
(1)find 查找 x,如果有多个 x 在树中,返回中序第一个 x 。
(2)插入时,允许重复的值插入
三、map
value_type pair<const key_type, mapped type>
创建 插入 遍历
map<string, string> dict;
// value_type是pair
//1.使用有名对象
pair<string, string> kv1("吒", "aespa");
dict.insert(kv1);
//2.使用匿名对象
dict.insert(pair<string, string> ("鲸", "NewJeans"));
//推荐以下两种:
//3.利用make_pair,传实参自动推导
dict.insert(make_pair("炽", "LE SSERAFIM"));
dict.insert(make_pair("芙", "IVE"));
//4.花括号,隐式类型转换
dict.insert({ "娃", "(G)I-DLE" });
//外层{}是初始化列表,里层{}是隐式类型转换
//map<string, string> dict = { {"吒", "aespa"}, {"鲸", "NewJeans"}, {"炽", "LE SSERAFIM"}, {"芙", "IVE"}, {"娃", "(G)I-DLE"} };
auto it = dict.begin();
while (it != dict.end()) {
//cout << (*it).first << ", " << (*it).second << endl;
cout << it->first << ", " << it->second << endl;
++it;
}
cout << endl;
//注意效率,for(auto e : dict)全是深拷贝
for (const auto& e : dict) {
cout << e.first << " : " << e.second << endl;
}
cout << endl;
//C++17 结构化绑定
/*for (auto& [x, y] : dict) {
cout << x << " : " << y << endl;
}
cout << endl;*/