前言
现在我们开始进行对树的学习,这一节我们主要讲二叉搜索树和set和map的使用,这两个的使用我们只讲一些,然后就是一些练习题,综合使用stl
1. key类型的二叉搜索树的实现
//实现二叉搜索树
template<class K>
struct BSNode
{
BSNode<K>* _left;
BSNode<K>* _right;
K _key;
typedef BSNode<K> Node;
BSNode(const K&key)
:_key(key)
,_left(nullptr)
,_right(nullptr)
{}
};
template<class K>
class BSTree
{
public:
typedef BSNode<K> Node;
bool insert(const K& key)
{
if (_root == nullptr)
{
_root = new Node(key);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//但要注意_root==nullptr的时候
if (key > parent->_key)
{
parent->_right = new Node(key);
}
else
{
parent->_left = new Node(key);
}
return true;
}
//写个中序遍历
//_root是this指针里面的东西,所以不好搞,不好递归,所以采用调用函数的方法
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//搜索某个数据
Node* Search(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//删除某个数据
bool Erase(const K& key)
{
if (_root == nullptr)
{
return false;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到了要删除的数据
//删除的话就分为三种//
//第一种就是没有孩子直接删除
//第二种就是有一个孩子的话就直接连在父亲的后面
//第三种就是有两个孩子
//其中第一种和第二种可以合并。因为可以把没有孩子当做空指针的孩子
if (cur->_left == nullptr)
{
if (parent == nullptr)//说明删的是根
{
_root = cur->_right;
delete cur;
return true;
}
//先看cur在parent的左还是右
if (cur->_key < parent->_key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)//说明删的是根
{
_root = cur->_left;
delete cur;
return true;
}
if (cur->_key < parent->_key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
return true;
}
else//现在左右孩子都不为空
{
//如果是头结点,也不用单独考虑,我们这样设计的话
//我们直接找到右孩子的最小值放在cur,然后删除这个最小值节点就可以了,最小值就是一直往左走就可以了
Node* min = cur->_right;
Node* min_parent = cur;
while (min->_left)
{
min_parent = min;
min = min->_left;
}
cur->_key = min->_key;
//删除min节点
//因为min的左孩子一定为空,所以很好删除
if (min_parent == cur)//说明没走
{
min_parent->_right = min->_right;
}
else
{
min_parent->_left = min->_right;
}
delete min;
return true;
}
}
}
return false;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " ";
_InOrder(root->_right);
}
Node* _root=nullptr;
};
这种类型的二叉搜索树只有一个模版参数,只能用于那种在不在的问题
比如门禁系统(刷卡看找不找得到),检查英文小说中是否有错误单词
(把所有单词存入二叉树中,来一个单词就去树中找,看找不找得到)
主要作用就是看这个东西在不在二叉树中
2. key/value类型的二叉搜索树的实现
和上面那个很类似
template<class K,class V>
class BSTree
{
public:
typedef BSNode<K,V> Node;
bool insert(const K& key, const V& value)
{
if (_root == nullptr)
{
_root = new Node(key,value);
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return false;
}
}
//但要注意_root==nullptr的时候
if (key > parent->_key)
{
parent->_right = new Node(key,value);
}
else
{
parent->_left = new Node(key,value);
}
return true;
}
//写个中序遍历
//_root是this指针里面的东西,所以不好搞,不好递归,所以采用调用函数的方法
void InOrder()
{
_InOrder(_root);
cout << endl;
}
//搜索某个数据
Node* Search(const K& key)
{
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
return cur;
}
}
return nullptr;
}
//删除某个数据
bool Erase(const K& key)
{
if (_root == nullptr)
{
return false;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur)
{
if (key > cur->_key)
{
parent = cur;
cur = cur->_right;
}
else if (key < cur->_key)
{
parent = cur;
cur = cur->_left;
}
else
{
//找到了要删除的数据
//删除的话就分为三种//
//第一种就是没有孩子直接删除
//第二种就是有一个孩子的话就直接连在父亲的后面
//第三种就是有两个孩子
//其中第一种和第二种可以合并。因为可以把没有孩子当做空指针的孩子
if (cur->_left == nullptr)
{
if (parent == nullptr)//说明删的是根
{
_root = cur->_right;
delete cur;
return true;
}
//先看cur在parent的左还是右
if (cur->_key < parent->_key)
{
parent->_left = cur->_right;
}
else
{
parent->_right = cur->_right;
}
delete cur;
return true;
}
else if (cur->_right == nullptr)
{
if (parent == nullptr)//说明删的是根
{
_root = cur->_left;
delete cur;
return true;
}
if (cur->_key < parent->_key)
{
parent->_left = cur->_left;
}
else
{
parent->_right = cur->_left;
}
delete cur;
return true;
}
else//现在左右孩子都不为空
{
//如果是头结点,也不用单独考虑,我们这样设计的话
//我们直接找到右孩子的最小值放在cur,然后删除这个最小值节点就可以了,最小值就是一直往左走就可以了
Node* min = cur->_right;
Node* min_parent = cur;
while (min->_left)
{
min_parent = min;
min = min->_left;
}
cur->_key = min->_key;
//删除min节点
//因为min的左孩子一定为空,所以很好删除
if (min_parent == cur)//说明没走
{
min_parent->_right = min->_right;
}
else
{
min_parent->_left = min->_right;
}
delete min;
return true;
}
}
}
return false;
}
private:
void _InOrder(Node* root)
{
if (root == nullptr)
{
return;
}
_InOrder(root->_left);
cout << root->_key << " "<<root->_value<<endl;
_InOrder(root->_right);
}
Node* _root = nullptr;
};
这个的主要作用就是通过一个值找另一个值,比如字典(通过中文找英文)车库收费系统(一个记录车牌号,一个记录进入时间,最后通过车牌号找到进入时间加上结束时间算车费),统计单词出现次数,一个记录单词,一个记录次数
BSTree<string, string> t;
t.insert("右", "right");
t.insert("左", "left");
t.insert("上", "up");
t.insert("下", "down");
string s;
while (cin >> s)
{
auto x = t.Search(s);
if (x != nullptr)
{
cout << x->_key << " " << x->_value << endl;
}
else
{
cout << "查找失败" << endl;
}
}
这个是字典查找
string arr[] = { "cccc","asqdq","aaaa","bbbbbb","cccc","aaaa" };
BSTree<string ,int> t;
for (auto x : arr)
{
auto p = t.Search(x);
if (p == nullptr)
{
t.insert(x, 1);
}
else
{
p->_value++;
}
}
t.InOrder();
这个是统计次数
3. 默认构造函数的实现
3.1 拷贝构造函数
BSTree() = default;
BSTree(const BSTree& t)
{
_root = copy(t._root);
}
Node* copy(Node* root)
{
if (root == nullptr)
{
return nullptr;
}
//先拷贝根,在拷贝左右子树
Node* tmp = new Node(root->_key, root->_value);
tmp->_left = copy(root->_left);
tmp->_right = copy(root->_right);
return tmp;
}
3.2 析构函数
void destroy(Node* root)
{
if (root == nullptr)
{
return;
}
destroy(root->_left);
destroy(root->_right);
delete root;
}
3. set的使用
set是key类型的红黑树
3.1 insert
第一个insert的返回值是一个pair类型的类模板,这里先不讲
set<int> s ;
s.insert(1);
s.insert(3);
s.insert(5);
s.insert(7);
s.insert(9);
s.insert(3);
s.insert(4);
s.insert(2);
set<int>::iterator it = s.begin();
while (it != s.end())
{
cout << *it << " ";
it++;
}
cout << endl;
可以看出insert插进去的,相同的值就不会插进去了
然后迭代器走的就是中序遍历
3.2 erase
然后erase就是删除一个值,一个迭代器,和迭代器区间
下面演示两个实例,第一个
删除最小值
s.erase(s.begin());
判断某个值在不在
因为erase的第二个函数的返回值如果为0个就说明删除失败
int x;
cin >> x;
int ret=s.erase(x);
if (ret == 0)
{
cout << "不存在" << endl;
}
3.3 find
find找到了就会返回该个迭代器
auto a = find(s.begin(), s.end(), 3);
auto b = s.find(3);
第一个是库里面的find,主要是通过迭代器加加来找东西的,所以时间复杂度为O(n)
第二个是通过红黑树的特性来查找的,所以为O(logN)
find如果没找到的话,就会返回end的迭代器
3.4 count
count是查找某个数据,然后返回该数据的个数,但在这里不是0就是1
是0说明没有这个数据,是1说明有
然后就是这个的时间复杂度是O(n),因为要找个数嘛,所以要中序遍历,因为相同的值在一起,这样才更好计数
3.5 lower_bound和upper_bound
直接看代码
int main()
{
std::set<int> myset;
std::set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++) myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90
itlow = myset.lower_bound(30); // ^
itup = myset.upper_bound(60); // ^
myset.erase(itlow, itup); // 10 20 70 80 90
std::cout << "myset contains:";
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
itlow = myset.lower_bound(30);
itup = myset.upper_bound(60);
仔细看这两段代码
lower_bound返回的是大于等于那个值的迭代器,所以返回的是30的迭代器,如果传的值是25,那么返回的就是大于等于25的迭代器,还是30
upper_bound返回的是大于那个值的迭代器,就是返回大于60的迭代器,那么就是70,然后erase的区间又是左闭右开,所以就相当于时30,60的闭区间被销毁了
4. multiset
multiset的头文件也是set,也是set的一种,只不过区别就是,它面对insert相同的值不是不插入了,而是要插入,插入在相同的值的左右孩子都可以
int main()
{
multiset<int> s;
s.insert(1);
s.insert(2);
s.insert(4);
s.insert(6);
s.insert(7);
s.insert(4);
s.insert(8);
s.insert(43);
s.insert(3);
s.insert(5);
s.insert(3);
s.insert(1);
s.insert(3);
for (auto x : s)
{
cout << x << ' ';
}
return 0;
}
这个就可以看出来,相同的值也可以插入树
4.1 find
这里的find找一个值,就是返回中序遍历的第一个值的迭代器
下面是实现一个函数,打印所有的你要查找的x值
int x;
cin >> x;
auto it = s.find(x);
while (it != s.end() && *it == x)
{
cout << *it << " ";
it++;
}
然后根据这个我们就可以求出count的实现了
4.2 erase
然后这里的erase删除一个x是删除所有值为x的节点
s.erase(3);
for (auto x : s)
{
cout << x << ' ';
}
4.3 equal_range
上一个set没讲这个函数,我们在这里讲
这个函数是你传入一个x值,然后这个函数返回所有含有这个值的迭代器区间,区间存在一个pair的类模板中
这个类模板有两个可以访问的成员变量,一个first类型对应为T1,一个second类型对应为T2,在这里就是,first为迭代器开始,second为迭代器末尾
pair<multiset<int>::iterator, multiset<int>::iterator>ran=s.equal_range(3);
auto it = ran.first;
while (it != ran.second)
{
cout << *it << " ";
it++;
}
cout << endl;
5. map
map就是key/value类型的了
5.1 insert
这里我们可以看出,insert的值是一个value_type的值,而value_type就是pair
而pair也是一个模版
map<string, string> m;
pair<string,string> p("left", "左");
m.insert(p);
m.insert(pair<string, string>("right", "右"));
所以说有上面两种插入方法
还有一种插入方法就是make_pair
m.insert(make_pair("up", "上"));
这个make_pair就是通过两个参数来推出pair的类型,进而建立pair类型
m.insert({"down","下"});
还有一种方法就是,直接有{},因为万物均可用{}初始化,先用{}构造make_pair,再来构造pair
map<string, string> m = { {"left", "左"},{"right", "右"},{"up", "上"},{"down","下"} };
5.2 迭代器
auto it = m.begin();
while (it != m.end())
{
cout << (*it).first << ":" << (*it).second << endl;
++it;
}
因为这里的数据类型是pair,key和value就存在pair中,所以对迭代器解引用得到的就是pair,但这样比较麻烦了,可以直接用->
auto it = m.begin();
while (it != m.end())
{
cout << it->first << ":" << it->second << endl;
++it;
}
这个->我们前面已经讲过了,这里就不讲了
接下来我们来写范围for
for (const auto& x : m)
{
cout << x.first << ":" << x.second << endl;
}
cout << endl;
for (const auto& [x,y] : m)
{
cout << x << ":" << y<< endl;
}
cout << endl;
这里还有一种范围for的遍历方法,这里是要C++17才可以支持的
这个就相当于把first给了x,second给了y
5.3 find
这里我们发现find找的是key然后返回迭代器,没找到就返回末尾的迭代器
map<string, string> m = { {"left", "左"},{"right", "右"},{"up", "上"},{"down","下"} };
string str;
while (cin >> str)
{
auto it = m.find(str);
if (it == m.end())
{
cout << "没找到" << endl;
}
else
{
cout << "存在" << it->first << ":" << it->second << endl;
}
}
5.4 operator[]
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countTree;
for (const auto& str : arr)
{
// 先查找水果在不在搜索树中
// 1、不在,说明水果第一次出现,则插入<水果, 1>
// 2、在,则查找到的节点中水果对应的次数++
//BSTreeNode<string, int>* ret = countTree.Find(str);
auto ret = countTree.find(str);
if (ret == countTree.end())
{
countTree.insert({ str, 1 });
}
else
{
ret->second++;
}
}
如上图我们可以利用这个程序来计算数组各个元素个数
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countTree;
for (const auto& str : arr)
{
countTree[str]++;
}
其实还可以这样计数,那么我们就可以推测了operator[key]得到的是value,然后如果不存在的话,就会新建立一个key
可以看出operator[key]的返回值就是mapped_type类型也就是value
然后[]里面有这个实现逻辑,就是会用k和mapped_type的默认值传递给insert
而insert这个函数,如果插入成功的话,就会返回你插入成功位置的迭代器,为ture,插入失败的话,也就是已经有这个key值了,就会插入失败,然后就会返回以前那个相同的key值的迭代器,为false
然后insert的返回值的first就是迭代器,迭代器的second就是value,于是就这样实现了
所以operator[]传入key返回value的引用,没有这种key就自己建立,有这种key就可以改变value值
所以operator有插入作用,修改作用,插入+修改作用
string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
map<string, int> countTree;
for (const auto& str : arr)
{
countTree[str]++;
}
countTree["hello"];
countTree["苹果"] = 3;
countTree["left"] = 10;
countTree[“hello”];对应的value就是用的默认值
6. multimap
multimap<string, int> mul;
mul.insert({ "aaa",1 });
mul.insert({ "aaa",2 });
mul.insert({ "aaa",1 });
mul.insert({ "bbb",2 });
mul.insert({ "bbb",2 });
map<string, int> m;
m.insert({ "aaa",1 });
m.insert({ "aaa",2 });
multimap与map的区别就是允许数据冗余
multimap对于key相同的,不管value相不相同都会插入
map对于key相同就不会插入了
因为map就是通过比较key来插入的
7. 练习题
7.1 环形链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
typedef ListNode Node;
class Solution {
public:
bool hasCycle(ListNode* head) {
//这里我们用map和set解决
//用 map来统计节点次数
map<Node*, int> m;
Node* cur = head;
while (cur)
{
m[cur]++;
if (m[cur] == 2)//同一个节点有两次说明有环,因为节点地址都不一样嘛
{
return true;
}
cur = cur->next;
}
//cur为空也说明不为环
return false;
}
};
7.2 随机链表的复制
/*
// Definition for a Node.
class Node {
public:
int val;
Node* next;
Node* random;
Node(int _val) {
val = _val;
next = NULL;
random = NULL;
}
};
*/
class Solution {
public:
Node* copyRandomList(Node* head) {
//这里我们还是用map来解决问题
//先浅浅的拷贝一个链表,不拷贝random
map<Node*, Node*> m;
Node* cur = head;
Node* copy_head = nullptr;
Node* copy_tail = nullptr;
while (cur)
{
if (copy_tail == nullptr)
{
copy_head = copy_tail = new Node(cur->val);
}
else
{
Node* new_node = new Node(cur->val);
copy_tail->next = new_node;
copy_tail = new_node;
}
m.insert({ cur ,copy_tail });
cur = cur->next;
}
//开始链接random
Node*cur1 = copy_head;
Node*cur2 = head;
while (cur1&&cur2)
{
if (cur2->random == nullptr)
{
cur1->random = nullptr;
}
else
{
cur1->random = m[cur2->random];//就这样就连接好random了
}
cur1 = cur1->next;
cur2 = cur2->next;
}
return copy_head;
}
};
7.3 前K个高频单词
//class my_compare
//{
//public:
// bool operator()(pair<string, int> p1, pair<string, int> p2)
// {
// return p1.second > p2.second;//因为要降序
// }
//};
//
//class Solution {
//public:
// vector<string> topKFrequent(vector<string>& words, int k) {
// //先把每个string统计好次数
// map<string, int> m;
// for (auto x : words)
// {
// ++m[x];
// }
// //这样就统计好次数了
// // 先存入vector中再排序//因为sort只能排连续的//所以传入pair
// vector<pair<string,int>> tmp;
// for (auto x : m)
// {
// tmp.push_back(x);
// }
// //接下来按照次数来排序//我们用库里面的排序算法
// //sort(tmp.begin(), tmp.end());//但这样不行,因为这样默认排的序是pair的first,如果first相同就比second,所以我们要自己写仿函数
// sort(tmp.begin(), tmp.end(), my_compare());//传入对象进去就可以了,这里是匿名对象
// //排序排好了,现在只需要取前k个就可以了
// vector<string> ret;
// for (int i = 0; i < k; i++)
// {
// ret.push_back(tmp[i].first);
// }
// return ret;
// }
//};
//但是这样还不够,因为要求string次数相同的也要按照字节序来比较
//虽然我们的map插入的时候就是按照字节序比较的,相同次数的,字节序前的在前面,但是我们sort是快排,是不稳定的,
//所以可以考虑stable_sort,这个是稳定的
//class my_compare
//{
//public:
// bool operator()(pair<string, int> p1, pair<string, int> p2)
// {
// return p1.second > p2.second;//因为要降序
// }
//};
//
//class Solution {
//public:
// vector<string> topKFrequent(vector<string>& words, int k) {
// //先把每个string统计好次数
// map<string, int> m;
// for (auto x : words)
// {
// ++m[x];
// }
// //这样就统计好次数了
// // 先存入vector中再排序//因为sort只能排连续的//所以传入pair
// vector<pair<string, int>> tmp;
// for (auto x : m)
// {
// tmp.push_back(x);
// }
// //接下来按照次数来排序//我们用库里面的排序算法
// //sort(tmp.begin(), tmp.end());//但这样不行,因为这样默认排的序是pair的first,如果first相同就比second,所以我们要自己写仿函数
// stable_sort(tmp.begin(), tmp.end(), my_compare());//传入对象进去就可以了,这里是匿名对象
// //排序排好了,现在只需要取前k个就可以了
// vector<string> ret;
// for (int i = 0; i < k; i++)
// {
// ret.push_back(tmp[i].first);
// }
// return ret;
// }
//};
//或者我们更改比较规则,让次数相同的还要比较字节序
class my_compare
{
public:
bool operator()(pair<string, int> p1, pair<string, int> p2)
{
return (p1.second > p2.second)||(p1.second == p2.second&&p1.first<p2.first);//因为要降序//而且字节序小的在前面
//这样写就是次数大的在前面,字节序小的在前面
}
};
class Solution {
public:
vector<string> topKFrequent(vector<string>& words, int k) {
//先把每个string统计好次数
map<string, int> m;
for (auto x : words)
{
++m[x];
}
//这样就统计好次数了
// 先存入vector中再排序//因为sort只能排连续的//所以传入pair
vector<pair<string, int>> tmp;
for (auto x : m)
{
tmp.push_back(x);
}
//接下来按照次数来排序//我们用库里面的排序算法
//sort(tmp.begin(), tmp.end());//但这样不行,因为这样默认排的序是pair的first,如果first相同就比second,所以我们要自己写仿函数
sort(tmp.begin(), tmp.end(), my_compare());//传入对象进去就可以了,这里是匿名对象
//排序排好了,现在只需要取前k个就可以了
vector<string> ret;
for (int i = 0; i < k; i++)
{
ret.push_back(tmp[i].first);
}
return ret;
}
};
7.4 两个数组的交集
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//我们先将其存入两个set
//因为这样就可以去重了
set<int> s1(nums1.begin(), nums1.end());
set<int> s2(nums2.begin(), nums2.end());
//set就已经排好序了
vector<int> ret;
auto it1 = s1.begin();
auto it2 = s2.begin();
while (it1 != s1.end() && it2 != s2.end())
{
//小的加加,相同就都加加
if (*it1 < *it2)
{
it1++;
}
else if (*it2 < *it1)
{
it2++;
}
else
{
ret.push_back(*it1);
it1++;
it2++;
}
}
return ret;
}
};
7.5 单词识别
#include <iostream>
#include<map>
#include<vector>
#include<algorithm>
using namespace std;
class my_compare
{
public:
bool operator()(pair<string, int> p1, pair<string, int> p2)
{
return (p1.second > p2.second)||(p1.second == p2.second&&p1.first<p2.first);//因为要降序//而且字节序小的在前面
//这样写就是次数大的在前面,字节序小的在前面
}
};
class Solution {
public:
void topKFrequent(vector<string>& words) {
//先把每个string统计好次数
map<string, int> m;
for (auto x : words)
{
++m[x];
}
//这样就统计好次数了
// 先存入vector中再排序//因为sort只能排连续的//所以传入pair
vector<pair<string, int>> tmp;
for (auto x : m)
{
tmp.push_back(x);
}
//接下来按照次数来排序//我们用库里面的排序算法
//sort(tmp.begin(), tmp.end());//但这样不行,因为这样默认排的序是pair的first,如果first相同就比second,所以我们要自己写仿函数
sort(tmp.begin(), tmp.end(), my_compare());//传入对象进去就可以了,这里是匿名对象
//开始打印
for(auto x:tmp)
{
cout<<x.first<<":"<<x.second<<endl;
}
}
};
int main() {
string str;
vector<string> tmp;
while (cin >>str) { // 注意 while 处理多个 case
auto it=str.begin();
while(it!=str.end())
{
if((*it)>='A'&&(*it)<='Z')//大写转小写
{
(*it)+=32;
}
if((*it)<'a'||(*it)>'z')//删除非字母
{
it=str.erase(it);//小心迭代器失效
}
else {
it++;
}
}
tmp.push_back(str);
}
Solution().topKFrequent(tmp);
}
这道题要结合7.3那道题
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<map>
using namespace std;
总结
下一节开始讲AVL树,如果可以的话,还可以讲一下红黑树