二叉搜索树+set和map

前言

现在我们开始进行对树的学习,这一节我们主要讲二叉搜索树和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树,如果可以的话,还可以讲一下红黑树

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值