【C++】优先级队列、仿函数和反向迭代器

news2024/10/6 10:39:49

​🌠 作者:@阿亮joy.
🎆专栏:《吃透西嘎嘎》
🎇 座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
在这里插入图片描述

目录

    • 👉priority_queue 的介绍👈
    • 👉priority_queue 的使用👈
    • 👉仿函数👈
    • 👉priority_queue 的模拟实现👈
    • 👉反向迭代器👈
    • 👉总结👈

👉priority_queue 的介绍👈

  1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。
  2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。
  3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类.priority_queue 提供一组特定的成员函数来访问其元素,元素从特定容器的尾部弹出,其称为优先队列的顶部。
  4. 底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:
    • empty():检测容器是否为空
    • size():返回容器中有效元素个数
    • front():返回容器中第一个元素的引用
    • push_back():在容器尾部插入元素
    • pop_back():删除容器尾部元素
  5. 标准容器类 vector 和 deque 满足这些需求。默认情况下,如果没有为特定的 priority_queue 类实例化指定容器类,则使用 vector。
  6. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap 和 pop_heap 来自动完成此操作。

👉priority_queue 的使用👈

优先级队列默认使用 vector 作为其底层存储数据的容器。在 vector 上又使用了向上调整算法和向下调整算法将 vector 中元素构造成堆的结构,因此 priority_queue 就是堆。所有需要用到堆的地方,都可以考虑使用priority_queue。注意:默认情况下 priority_queue 是大堆。

函数声明接口说明
priority_queue()构造一个空的优先级队列
priority_queue(first, last)迭代器区间构造优先级队列
empty( )检测优先级队列是否为空,是返回 true,否则返回 false
top( )返回优先级队列中最大元素(最小元素),即堆顶元素
push(x)在优先级队列中插入元素 x
pop()删除优先级队列中最大元素(最小元素),即堆顶元素
size()返回优先级队列中元素的个数

代码示例:

#include <queue>	// 优先级队列priority的头文件
#include <iostream>
using namespace std;
#include <functional>	// greater算法的头文件

int main()
{
	// 默认情况下,创建的是大堆,其底层是按照小于号比较的
	priority_queue<int> pq1; 
	pq1.push(3);
	pq1.push(2);
	pq1.push(8);
	pq1.push(5);
	pq1.push(1);

	while (!pq1.empty())
	{
		cout << pq1.top() << " ";
		pq1.pop();
	}
	cout << endl;

	// 如果要创建小堆,将第三个模板参数换成greater比较方式,其底层是按照大于号比较的
	priority_queue<int, vector<int>, greater<int>> pq2;
	pq2.push(3);
	pq2.push(2);
	pq2.push(8);
	pq2.push(5);
	pq2.push(1);

	while (!pq2.empty())
	{
		cout << pq2.top() << " ";
		pq2.pop();
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

如果在priority_queue中放自定义类型的数据,用户需要在自定义类型中提供 > 或者 < 的重载。

#include <queue>	// 优先级队列priority的头文件
#include <functional>	// greater算法的头文件
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};
void TestPriorityQueue()
{
	// 大堆,需要用户在自定义类型中提供<的重载
	priority_queue<Date> q1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	while (!q1.empty())
	{
		cout << q1.top() << "  ";
		q1.pop();
	}
	cout << endl;

	// 如果要创建小堆,需要用户提供>的重载
	priority_queue<Date, vector<Date>, greater<Date>> q2;
	q2.push(Date(2018, 10, 29));
	q2.push(Date(2018, 10, 28));
	q2.push(Date(2018, 10, 30));
	while (!q2.empty())
	{
		cout << q2.top() << "  ";
		q2.pop();
	}
	cout << endl;
}

int main()
{
	TestPriorityQueue();
	return 0;
}

在这里插入图片描述

优先级的队列使用起来没有什么难的,接下来我们来做一道题目,顺便回顾一下堆。

数组中的第K个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

在这里插入图片描述

解决这道题有两种思路,第一种就是用数组的元素建大堆,然后 pop 掉 k - 1 个元素,此时堆顶的元素就是第 k 大的数。这种思路的时间复杂度为O(N + k * lgN),当 N 很大时,就会需要非常多的空间。这时候,我们可以考虑第二种思路就是用前 k 个数建只包含 k 个数的小堆,再遍历数组剩下的元素,比堆顶元素大则替换掉堆顶元素,再向下调整堆。这种思路的时间复杂度为O(k + (N - k) * lgk)注:顶元素不允许修改,因为堆顶元素修改可以会破坏堆的特性。

class Solution 
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        // 建n个数的大堆
        priority_queue<int> pq(nums.begin(), nums.end());
        // pop掉前k-1个大的数
        while(--k)  pq.pop();
        return pq.top();
    }
};

在这里插入图片描述

class Solution 
{
public:
    int findKthLargest(vector<int>& nums, int k) 
    {
        // 建k个数的小堆,从下标k开始遍历数组
        // 如果num[i]大于堆顶的数据,那么nums[i]入堆
        // 遍历结束,堆顶的数就是第k大的数
        priority_queue<int, vector<int>, greater<int>> pq(nums.begin(), nums.begin() + k);
        for(size_t i = k; i < nums.size(); ++i)
        {
            if(nums[i] > pq.top())
            {
                pq.pop();
                pq.push(nums[i]);
            }
        }
        return pq.top();
    }
};

在这里插入图片描述

👉仿函数👈

仿函数也称为函数对象,它是具有operator()的类对象,可以想函数一样来使用它。

#include <iostream>
using namespace std;

// 仿函数/函数对象
template <class T>
class Less
{
public:
	template <class T>
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};

template <class T>
class Greater
{
public:
	template <class T>
	bool operator()(const T& left, const T& right)
	{
		return left > right;
	}
};

int main()
{
	// 仿函数用起来就像是函数
	// 有名对象
	Less<int> lessFunc;
	cout << lessFunc(1, 2) << endl;
	// lessFunc(1, 2) <==> lessFunc.operator()(1, 2);
	
	// 匿名对象
	cout << Greater<int>()(1, 2) << endl;

	return 0;
}

在这里插入图片描述

这样一看,好像仿函数也没有特别大的又是,和函数指针也没有什么大的区别嘛!其实不然,函数指针需要写函数的参数和返回值类型。所以仿函数用起来还是比函数指针舒服的。

#include <iostream>
using namespace std;

// 仿函数/函数对象
template <class T>
class Less
{
public:
	template <class T>
	bool operator()(const T& left, const T& right)
	{
		return left < right;
	}
};

template <class T>
class Greater
{
public:
	template <class T>
	bool operator()(const T& left, const T& right)
	{
		return left > right;
	}
};

template <class T, class Compare>
void BubbleSort(T* a, int n, Compare com)
{
	for (int i = 0; i < n - 1; ++i)
	{
		int exchange = 0;
		for (int j = 1; j < n - i; ++j)
		{
			// if (a[i] < a[i - 1])
			if (com(a[j], a[j - 1]))
			{
				swap(a[j], a[j - 1]);
				exchange = 1;
			}
		}

		if (exchange == 0)
			break;
	}
}

int main()
{
	Less<int> lessFunc;
	int arr[] = { 2, 7,3, 1, 5, 9 ,10,20 };
	BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), lessFunc);
	for (auto e : arr)
		cout << e << " ";
	cout << endl;

	BubbleSort(arr, sizeof(arr) / sizeof(arr[0]), Greater<int>());
	for (auto e : arr)
		cout << e << " ";
	cout << endl;

	return 0;
}

在这里插入图片描述

注:没有成员变量的类的大小是 1 个字节,所以传参时加不加引用都没有关系。如果参数用 const 修饰了,那么仿函数也要用 const 来修饰。

如果库提供的仿函数不符合我们的需求,我们可以自己写!

#include <queue>	// 优先级队列priority的头文件
#include <functional>	// greater算法的头文件
#include <iostream>
using namespace std;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

struct PDateLess
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 < *d2;
	}
};

struct PDateGreater
{
	bool operator()(const Date* d1, const Date* d2)
	{
		return *d1 > *d2;
	}
};

void TestPriorityQueue()
{
	priority_queue<Date*, vector<Date*>, PDateLess> q3;
	q3.push(new Date(2018, 10, 29));
	q3.push(new Date(2018, 10, 28));
	q3.push(new Date(2018, 10, 30));
	while (!q3.empty())
	{
		cout << *q3.top() << "  ";
		q3.pop();
	}
	cout << endl;

	priority_queue<Date*, vector<Date*>, PDateGreater> q4;
	q4.push(new Date(2018, 10, 29));
	q4.push(new Date(2018, 10, 28));
	q4.push(new Date(2018, 10, 30));
	while (!q4.empty())
	{
		cout << *q4.top() << "  ";
		q4.pop();
	}
	cout << endl;
}

int main()
{
	TestPriorityQueue();
	return 0;
}

在这里插入图片描述

现在学习到的仿函数还只是冰山一角,以后还会有更多的仿函数要学!!!

👉priority_queue 的模拟实现👈

// PriorityQueue.h
#pragma once

namespace Joy
{
	// 仿函数
	template <class T>
	struct less
	{
		bool operator()(const T& left, const T& right)
		{
			return left < right;
		}
	};

	template <class T>
	struct greater
	{
		bool operator()(const T& left, const T& right)
		{
			return left > right;
		}
	};

	template <class T, class Container = vector<T>, class Compare = less<T>>
	class priority_queue
	{
	public:

		priority_queue()
			: _con()
		{}

		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
			: _con(first, last)
		{
			// 注意:这里要使用int!如果使用size_t,如果只有一个元素会出现Bug
			int size = _con.size();
			for (int i = (size - 2) / 2; i >= 0; --i)
				adjust_down(i);
		}

		void push(const T& val)
		{
			_con.push_back(val);
			adjust_up(_con.size() - 1);
		}

		void pop()
		{
			//swap(_con[0], _con[_con.size() - 1]);
			swap(_con.front(), _con.back());
			_con.pop_back();
			adjust_down(0);
		}

		bool empty() const
		{
			return _con.empty();
		}

		size_t size() const
		{
			return _con.size();
		}

		const T& top() const
		{
			assert(!empty());
			return _con[0];
		}

	private:
		Container _con;	// 底层容器

		// 向上调整算法
		void adjust_up(size_t child)
		{
			size_t parent = (child - 1) / 2;

			while (child > 0)
			{
				
				//if(_con[parent] < _con[child])
				if (Compare()(_con[parent], _con[child]))	// 匿名对象
				{
					swap(_con[parent], _con[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
					break;
			}
		}

		void adjust_down(size_t parent)
		{
			size_t child = 2 * parent + 1;	
			size_t size = _con.size();
			
			while (child < size)
			{
				// 找出较大的孩子
				if (child + 1 < size && Compare()(_con[child], _con[child + 1]))
					++child;

				if (Compare()(_con[parent], _con[child]))
				{
					swap(_con[parent], _con[child]);
					parent = child;
					child = 2 * parent + 1;
				}
				else
					break;
			}
		}
	};
}

// Test.cpp
#include <vector>
#include <iostream>
using namespace std;
#include "PriorityQueue.h"

int main()
{
	//priority_queue<int> pq; 默认是大堆
	//Joy::priority_queue<int> pq;
	Joy::priority_queue<int, vector<int>, greater<int>> pq;
	pq.push(3);
	pq.push(2);
	pq.push(8);
	pq.push(5);
	pq.push(1);

	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;

	return 0;
}

在这里插入图片描述

优先级队列的实现就不讲解了,和用C语言最大的区别就是不需要自己造轮子了,直接使用 vector 适配出优先级队列。还有就是使用了仿函数,可以根据传入的仿函数生成大堆还是小堆。如果还有相关的实现细节不了解的话,可以看这篇文章:堆的实现!

👉反向迭代器👈

方向迭代器其实也是一种适配器,它可以适配出各种容器的反向迭代器,其主要的是将正向迭代器进行了封装,反向迭代器 ++ 就复用正向迭代器的 - -,反向迭代器 - - 就复用正向迭代器的 ++。

#pragma once

template <class Interator, class Ref, class Ptr>
class ReverseInterator
{
public:
	typedef ReverseInterator<Interator, Ref, Ptr> Self;

	ReverseInterator(Interator it)
		: _it(it)
	{}

	Self& operator++()
	{
		--_it;
		return *this;
	}

	Self operator++(int)
	{
		auto tmp = _it;
		--_it;
		return tmp;
	}

	Self& operator--()
	{
		++_it;
		return *this;
	}

	Self operator--(int)
	{
		auto tmp = _it;
		++_it;
		return tmp;
	}

	Ref operator*()
	{
		auto tmp = _it;
		return *(--tmp);
	}
	
	// 返回当前对象的地址
	Ptr operator->()
	{
		return &(operator*());
	}

	bool operator!=(const Self& s) const
	{
		return _it != s._it;
	}

	bool operator==(const Self& s) const
	{
		return _it == s._it;
	}

private:
	Interator _it;	// 对正向迭代器进行封装
};

在这里插入图片描述

在这里插入图片描述
因为rbegin()是用end()来构造的,rend()使用begin()来构造的,所以反向迭代器的解引用需要创建一个临时对象tmp,再返回*(--tmp)

测试反向迭代器

在这里插入图片描述

在这里插入图片描述

注:以上的反向迭代器是用我们自己模拟实现的 list 来测试的!

👉总结👈

本篇博客主要讲解了什么是优先级队列、优先级队列的使用和模拟实现、仿函数以及反向迭代器的实现等等。那么以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家!💖💝❣️

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/149508.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

工具及方法 - 字幕下载网站

1&#xff0c;射手网&#xff08;伪&#xff09; 首页 - 射手网(伪) - assrt.net - 字幕下载&#xff0c;字幕组&#xff0c;中文字幕&#xff0c;美剧字幕&#xff0c;英剧字幕&#xff0c;双语字幕&#xff0c;新番字幕 2&#xff0c;国外网站。 Subscene - Passionate abo…

sentinel-热点参数限流

Pages 60 Sentinel 官方网站 OpenSergo 微服务治理 文档 Read Me新手指南Sentinel 介绍FAQRoadmap如何使用工作原理流量控制集群流控&#xff08;分布式流控&#xff09;网关流控熔断降级热点参数限流系统自适应限流黑白名单控制实时监控数据动态规则控制台生产环境使用 Sent…

【自学Python】Python IDLE使用

Python IDLE使用 Python IDLE使用教程 在 Windows 上安装好 Python 之后&#xff0c;Python 都会提供一个 Python 命令行工具&#xff0c;就叫 IDLE。IDLE 是一个 Python Shell&#xff0c;Python Shell 可以用于与 Python 进行交互。 Python IDLE使用 打开Python IDLE 首…

分布式基础篇4 —— 基础篇完结(谷粒商城)

分类维护一、三级分类后端实现准备工作跨域问题关闭 ESLint 检查前端实现二、分类删除前端完善分类列表后端实现——删除配置发送请求代码片段前端实现——删除三、分类增加前端实现四、分类修改五、拖拽菜单拖拽效果实现拖拽数据收集拖拽功能完成拖拽功能完善六、批量删除品牌…

粒子系统-常用子模块

目录 Emission &#xff08;发射&#xff09; Shape &#xff08;形状&#xff09; Cone &#xff08;锥形&#xff09; Velocity over Lifetime &#xff08;运动&#xff09; Limit Velocity over Lifetime (速度限制) Force over lifetime (受力) Color over Lifetim…

【C语言航路】第十站:指针进阶(二)

目录 六、函数指针数组 七、指向函数指针数组的指针 八、回调函数 1.回调函数的概念 2.回调函数实现计算器 3.回调函数实现冒泡排序 总结 六、函数指针数组 我们已经知道了函数指针&#xff0c;它的类型是int(*)(int,int)。那么我们能否进行推广呢&#xff1f;将其推广成…

基于AD Event日志实时检测DSRM后门

01、简介每个域控制器都有一个目录还原模式&#xff08;DSRM&#xff09;帐户&#xff0c;它的密码是在安装域控时设置的&#xff0c;实际上它对应的就是sam文件里的本地管理员“administrator”&#xff0c;基本很少会被重置&#xff0c;因此有着极强的隐蔽性。攻击者通过获取…

面试篇之NoSQL

面试篇之NoSQL一、redis持久化1.1、字节1.2、微软1.3、题解1.3.1、RDB快照1.3.2、AOF日志1.3.3、最佳实践二、redis删除策略2.1、字节2.2、阿里云2.3、题解2.3.1、过期删除2.3.2、内存淘汰2.3.3、LRU2.3.4、LFU三、redis高可用3.1、字节3.2、美团3.3、微软3.4、题解3.4.1、主从…

前端性能优化(六):传输加载优化

目录 一&#xff1a;启用压缩 Gzip 二&#xff1a;启用 Keep Alive 三&#xff1a;HTTP 资源缓存 3.1.HTTP 缓存方案 3.2.各大网站缓存策略参考 四&#xff1a;Service Worker 五&#xff1a;HTTP2 的性能提升 5.1.HTTP2 优势 5.2.开启 HTTP2 5.3.Server Push&#x…

【Spring(五)】带你深入了解bean的生命周期

1.5 bean的生命周期 bean的实例化已经说完了&#xff0c;我们最后再来讲讲bean的生命周期。 我们主要来围绕着bean生命周期控制来学习&#xff0c;那么什么是生命周期呢&#xff1f;其实就是一个东西从创建到消亡的完整过程&#xff0c;比如人从出生到死亡的整个过程&#xff…

【MySQL进阶】深入理解redoLog日志

【MySQL进阶】深入理解redoLog日志 文章目录【MySQL进阶】深入理解redoLog日志一&#xff1a;redo日志概述二&#xff1a;redo日志格式三&#xff1a;Mini-Transaction1&#xff1a;以组的形式写入redo日志2&#xff1a;Mini-Transaction的概念四&#xff1a;redo日志的写入过程…

富淼转债,优彩转债上市价格预测

富淼转债基本信息转债名称&#xff1a;富淼转债&#xff0c;评级&#xff1a;A&#xff0c;发行规模&#xff1a;4.5亿元。正股名称&#xff1a;富淼科技&#xff0c;今日收盘价&#xff1a;18.25&#xff0c;转股价格&#xff1a;20.26。当前转股价值 转债面值 / 转股价格 * …

(3分钟速览)SLAM中的三大金刚-H E F Matrix

编辑切换为居中添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff09;三个矩阵的自由度&#xff1a;单应矩阵H 自由度8基础矩阵F 自由度7本质矩阵E 自由度5或者8&#xff0c;这个是根据使用旋转矩阵还是旋转向量编辑切换为居中添加图片注释&#xff0c;不超过 14…

每日一题之Vue的异步更新实现原理是怎样的?

最近面试总是会被问到这么一个问题&#xff1a;在使用vue的时候&#xff0c;将for循环中声明的变量i从1增加到100&#xff0c;然后将i展示到页面上&#xff0c;页面上的i是从1跳到100&#xff0c;还是会怎样&#xff1f;答案当然是只会显示100&#xff0c;并不会有跳转的过程。…

计算机基础——计算机应用领域以及未来发展趋势

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 一.计算机应用领域 1.信息管理 2.过程控制 3.计算机辅助技术 1&#xff09…

一天天过去,每天该如何过?

刚跨了年&#xff0c;又快到春节&#xff0c;日子就这样一天天过去。如何过好这一生是个大命题&#xff0c;不如简化点考虑下如何过好一天&#xff1f;人的时间大体分为两类&#xff1a;主动的&#xff0c;可以自由支配&#xff1b;被动的&#xff0c;等着被安排。过去我在互联…

MCU-51:LCD1602详解

目录一、LCD1602简介1.1 显示原理1.2 引脚及应用电路1.3 技术参数1.4 引脚功能1.5 指令集1.6 连接方式二、时序图2.1 写时序2.2 读时序三、代码演示3.1 LCD1602.c3.2 示例注意&#xff1a;一定要看一、LCD1602简介 LCD1602&#xff08;Liquid Crystal Display&#xff09;液晶…

能量加油站Java上

1、final 在 Java 中有什么作用&#xff1f; 1、final 修饰的类叫最终类&#xff0c;该类不能被继承。2、final 修饰的方法不能被重写3、final 修饰的变量叫常量&#xff0c;常量必须初始化&#xff0c;初始化之后值就不能被修改 2、Math.round() 指向上取整 补充 Double.do…

01背包问题再探

原题&#xff1a; 有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。 第 i 件物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整…

盖子的c++小课堂——第五讲:for 循环

前言 hi&#xff0c;大家好&#xff0c;我是盖子的盖&#xff0c;最近大家都放假了吗&#xff0c;反正我还没有&#xff0c;我们期末考才刚开始考呜呜呜&#xff0c;真羡慕那些放假了的童鞋们~~(╥╯^╰╥)~~ 好啦&#xff0c;废话不多说&#xff0c;开始今天的小课堂吧~~ 上…