编程语言C/C++(七)—— STL(二)

set

实现原理:
set 的特性是,所有元素都会根据元素的键值自动被排序。set 的元素不像 map 那样可以同时拥有实值(value)和键值(key),set 元素的键值就是实值,实值就是键值,set不允许两个元素有相同的值。
set 底层是通过红黑树(RB-tree)来实现的,由于红黑树是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的 STL 的 set 即以 RB-Tree 为底层机制。又由于 set 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 set 操作行为,都只有转调用 RB-tree 的操作行为而已。

用法:
一些用法与上面的类似,这里就不全部列出来了

	初始化:
		  set<int> s;
		  set<int, mycompare> s;  
		  //mycompare其实就是排序规则,查找等操作全部是按照它来,最好用仿函数吧
		    
	赋值操作:
		  s.swap(s1);  //交换集合s和s1
		  s = s1;  
		    		
	 插入操作:
		  s.insert(10);  //往集合中插入10
		    		
	删除操作:
		  s.erase(s.begin(), s.end()-1);  //删除……,返回下一个元素的迭代器

	查找操作:
		  s.find(10);  
		  //查找元素10是否存在,如果存在,返回其迭代器,否则返回s.end()
		  
		  s.lower_bound(10); 
		   //查找第一个大于等于10的迭代器,如果没有,则返回s.end()
		   
		  s.upper_bound(10); 
		  //查找第一个大于10的迭代器,如果没有,则返回s.end()
		  
		  s.equal_range(10);  
		  //返回 s.lower_bound(10);和s.upper_bound(10);的迭代器,以对组的方式返回,一样需要判断s.end()

对组:
pair<iterator, iterator> pair<int, int>等等等…… 可以 = 赋值,另外也可以使用make_pair函数
EG: pair<set::iterator, set::iterator> ret = s.equal_range(10);
我们可以通过first 以及 second 两个公有函数来访问pair的两个值,即ret.first……

仿函数(函数对象,C++中可以使用这个来取代函数指针,毕竟挺强大的~):
仿函数的主要功能是为了搭配STL算法使用,单独使用仿函数的情况比较少。仿函数(functors)在C++标准中采用的名称是函数对象(function objects)。

仿函数主要用于STL中的算法中,虽然函数指针虽然也可以作为算法的参数,但是函数指针不能满足STL对抽象性的要求,也不能满足软件积木的要求–函数指针,无法和STL其他组件搭配,产生更灵活变化。仿函数本质就是类重载了一个operator(),创建一个行为类似函数的对象。

对于重载了()操作符的类,可以实现类似函数调用的过程,所以叫做仿函数,实际上仿函数对象仅仅占用1字节,因为内部没有数据成员,仅仅是一个重载的方法而已。实际上可以通过传递函数指针实现类似的功能,但是为了和STL内部配合使用,他提供了仿函数的特性。

函数对象超出了函数的概念,函数对象可以保存函数调用的状态:

  • 如果是普通函数且我们想获取调用次数,那我们可以使用全局变量,但是真正开发中,我们是避免去使用全局变量的,因为在多线程环境中我们需要去进行加锁解锁;

    • 而仿函数(函数对象)是一个类,我们只需在里面声明一个变量即可。
    • 像for_each、sort(algorithm)这类函数,我们也可以传入函数对象(模板的思想)

EG:

			class mycompare
			{
					public:
					bool operator()(int v1, int v2){return v1 > v2;}
			};
			int main(void)
			{
					set<int, mycompare> s;
			}
  • 一元仿函数、二元仿函数……:根据参数的个数来称呼

  • 谓词是指普通函数或重载的operator()返回值是bool类型的函数对象(仿函数)。如果operator接受一个参数,那么叫做一元谓词,如果接受两个对象,那么叫二元谓词,谓词可作为一个判断式。

multiset

multiset的特性以及用法和 set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制是 RB-tree 的 insert_equal()(即大于等于走右边) 而非 insert_unique()。

map

实现原理:
map的特性是,所有元素都会根据元素的键值自动被排序。map 的所有元素都是 pair,同时拥有实值(value)和键值(key)。pair 的第一元素被视为键值,第二元素被视为实值。

map不允许两个元素拥有相同的键值。由于 RB-tree 是一种平衡二叉搜索树,自动排序的效果很不错,所以标准的STL map 即以 RB-tree 为底层机制。又由于 map 所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 map 操作行为,都只是转调 RB-tree 的操作行为。

用法:
map的很多方法和set是类似的。

	初始化:
		map<int, string> m;
	    map<int, string, mycompare> m;  
	    //mycompare其实就是排序规则,查找等操作全部是按照它来,最好用仿函数吧

    插入:
		map<int, string> mapStu;
		mapStu.insert(pair<int, string>(3,"body1")); 
		 //第一种方法,通过pair的方式插入对象
		mapStu.insert(make_pair(1, "body2"));  
		//第二种方法,通过make_pair创建pair的方式插入对象
		mapStu.insert(map<int, string>::value_type(5, "body3")); 
		 //第三种方法,通过value_type的方式插入对象
		mapStu[2] = "body4";  
		//第四种方法,通过数组的方式插入值
		
		//**前三种方法区别不大,用数组方式插入时,如果发现key不存在,则会创建pair并插入到map中,如果存在,那么会修改其实值**
		//**注意,使用cout << mapStu[6] << endl; 时,如果通过[]方式去访问一个不存在的key,那么map会将这个访问的key插入到map中,且value会采取默认值**
					
    访问:
	    map<int, string>::iterator it;
		通过it->first 访问键值,通过it->second 访问实值
		pair<int, string> p;
		通过p.first 访问键值,通过p.second 访问实值,这里要和迭代器区分开!

    查找操作:
		map<int, string> m;
		m.find(10); 
		 //查找键值10是否存在,如果存在,返回其迭代器,否则返回m.end()
		m.lower_bound(10);  
		//查找第一个大于等于10的迭代器,如果没有,则返回m.end()
		m.upper_bound(10); 
		//查找第一个大于10的迭代器,如果没有,则返回m.end()
		m.equal_range(10); 
		 //返回 m.lower_bound(10);和m.upper_bound(10);的迭代器,以对组的方式返回,一样需要判断m.end()
multimap

multimap 的特性以及用法与 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique。

注意:
multimap的仿函数使用和set、multiset、map的不一样!!

				class Mycompare
				{
				public:
				    bool operator()(int v1, int v2)const  //这里必须加const
				    {
				        return v1>v2;
				    }
				};
				int main(void)
				{
						multimap<int,string, mycompare> m;
				}
				

另外,multimap不支持[],即不能使用s[10]=10这类操作!!

hash_set 与 hash_multiset

hash_set 底层数据结构为 hash 表,无序,不重复。hash_multiset 底层数据结构为 hash 表,无序,不重复。(貌似是用拉链法)

hash_map 与 hash_multimap

hash_map 底层数据结构为 hash 表,无序,不重复。hash_multimap 底层数据结构为 hash 表,无序,不重复。

其它
  • STL容器所提供的都是值(value)寓意,而非引用(reference)寓意,也就是说当我们给容器中插入元素的时候,容器内部实施了拷贝动作,将我们要插入的元素再另行拷贝一份放到容器中,而不是将原数据元素直接放进容器中,也就是说我们提供的元素必须那个被拷贝。

  • 通常STL不会抛出异常,需要使用者传入正确参数

  • STL容器使用时机:

    • vector:单端数组,可随机存取(有时候称为直接访问),元素搜寻速度慢,元素安插移除在尾端比较快

    • deque:双端数组,可随机存取,元素搜寻速度慢,元素安插移除在头尾两端比较快

    • list:双向链表,不可随机存取,元素搜寻速度非常慢,元素安插移除在任何位置都快

    • set:红黑树,不可随机存取,元素搜寻速度快

    • multiset:红黑树,不可随机存取,元素搜寻速度快

    • map:红黑树,对key而言是可随机存取,对key而言元素搜寻速度快

    • multimap:红黑树,不可随机存取,对key而言元素搜寻速度快

  • 函数对象(仿函数)
    很多东西前面已经讲了,这里就不重复了。

    • STL内建了一些函数对象。分为:算法类函数对象,关系类函数对象,逻辑类函数对象。这些函数对象所产生的对象,用法和一般的函数完全相同,
      当然我们还可以产生无名的临时对象来履行函数功能。使用内建函数对象,需要引入头文件 #include。

      • 函数对象适配器(重点)(functional库)
        函数对象适配器是完成一些配接工作,这些配接工作包括绑定(bind)、否定(negate),以及对一般函数或成员的修饰,使其成为函数对象。

        • 绑定适配器:bind1st、bind2nd

          • 功能:将一个二元函数对象转变为一元函数对象

          • 区别:

            • bind1st(叫bind first)是将传进来的参数绑定为函数对象的第一个参数

            • bind2nd(叫bind second)是将传进来的参数绑定为函数对象的第二个参数

            • 例子:

        class Myprint : public binary_function<int, int, void>  
      //注意,使用绑定配接器必须使我们的函数对象继承自binary_function,
      binary_function是一个模板类,我们需要传入<参数1类型, 参数二类型, 返回类型>
        {public:
        		void operator()(int v, int val) const   //注意,这里必须使用const修饰
        		{
        				cout << v + val << endl;
        		}
        }
       ……
        int addNum = 200;
        for_each(v.begin(), v.end(), bind2st(Myprint, addNum));
  • 取反适配器:not1、not2

    • 功能:对谓词的返回进行取反

    • 区别:not1是对一元谓词进行取反,而not2是对二元谓词进行取反

    • 例子:

		class Mycompare : public binary_function<int, int, bool>  
        //注意,使用not2配接器必须使我们的函数对象继承自binary_function,
        我们这里是二元的,因此是继承自binary_function
       {public:
        		bool operator()(int v1, int v2) const   
        		//注意,这里必须使用const修饰
        		{
        				return v1 > v2;
        		}
       }
        class Mycompare5 : public binary_function<int, bool>  
        //注意,这里继承的父类是unary_function
        {public:
        			bool operator()(int v1) const   
        			//注意,这里必须使用const修饰
        			{
        					return v1 > 5;
        			}
        }
        ……
        sort(v.begin(), v.end(), not2(Mycompare));
        						
        find_if(v.befin(), v.end(), not1(Mycompare5));
  • 关于find_if算法(使用方法参考上面的例子)
    这个算法的功能是返回第一个满足条件的迭代器

  • 函数对象适配器:ptr_fun

    • 功能:把函数转变为函数对象

    • 例子:

		void myPrint(int val1, int val2)
		{
				cout << val1 + val2 << endl;
		}
		……
		for_each(v.begin(), v.end(), bind2st(ptr_fun(myPrint), 10));
  • 成员函数适配器 mem_fun、mem_fun_ref

    • 功能:如果容器中存放的是对象或者对象指针,我们for_each算法打印的时候又想用类自己提供的打印函数

    • 例子

    	class Stu
    	{
    			public: void Print(int val1, int val2);
    	}
    	……
    	vector<Stu> v1;
    	for_each(v1.begin(), v1.end(), mem_fun_ref(&Stu::Print));
    	//注意使用的方法
    	vector<Stu*> v2;
    	for_each(v2.begin(), v2.end(), mem_fun(&Stu::Print));
    	//注意mem_fun和mem_fun_ref的使用,必须加&符号!!
    	//当容器内是对象时,使用mem_fun_ref;当容器内是对象指针时,使用mem_fun。
    
  • 常用的算法

    • 常用的遍历算法

      • for_each(iterator beg, iterator end, _callback);

      • transform(iterator beg1, iterator end1, iterator beg2, _callback)

        • 将指定容器区间元素搬运到另一个容器中,注意另一个容器的空间必须是初始化过的,可以使用resize,但不可以使用reserve,参考二者的区别
        • _callback是搬运的方法,例子如下:
        			class Myplus
        			{public:
        					int operator()(int val){return val; }
        			}
        			……
        			transform(v1.begin(), v1.end(), v2.begin(), Myplus());
        
    • 常用的查找算法

      • find(iterator beg, iterator, value);

        • 需要注意的是(下面也一样),自定义对象间的比较要求我们重载==运算符,如bool operator ==(const Person p)const{}
      • adjacent_find(iterator beg, iterator end, _callback);

        • 查找相邻重复元素,_callback为回调函数或者谓词,返回相邻元素的第一个位置的迭代器
      • bool binary_search(iterator beg, iterator end, value);

        • 二分查找,在无序序列中不能使用,查找成功返回true,否则返回false
      • find_if(iterator beg, iterator end, _callback);

        • 查找第一个满足条件的元素(_callback,谓词),并返回其迭代器,如果没有当然是返回end()
      • count(iterator beg, iterator end, value);

        • 统计元素的出现个数并返回(int)
      • count_if(iterator beg, iterator end, _callback);

        • 统计满足条件的元素的个数并返回(int)
    • 常用排序算法

      • merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest, _callback);

        • 将两个已排序(必须都是从小到大或从大到小)合并,并存储到另一个容器中
      • sort(iterator beg, iterator end, _callback);

        • 容器元素排序
      • random_shuffle(iterator beg, iterator end);

        • 对指定范围内的元素随机调整次序
      • reverse(iterator beg, iterator end);

        • 反转指定范围的元素
    • 常用拷贝和替换算法

      • copy(iterator beg, iterator end, iterator dest);

        • 将容器内指定范围的元素拷贝到另一容器中
      • replace(iterator beg, iterator end, oldvalue, newvalue);

        • 将容器内指定范围的旧元素修改为新元素
      • replace_if(iterator beg, iterator end, _callback, newvalue);

        • 将容器内指定范围满足条件的元素替换为新元素
      • swap(container c1, container c2);
        - 互换两个容器的元素

    • 常用算术生成算法

      • accumulate(iterator beg, iterator end, value);

        • 计算容器中=元素累计总和+value
      • fill(iterator beg, iterator end, value);

        • 向容器中添加元素
    • 常用的集合算法

      • set_intersection(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求两个集合的交集,返回一个迭代器,该迭代器指向最后一个元素的下一个元素
      • set_union(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求两个集合的并集,返回一个迭代器,该迭代器指向最后一个元素的下一个元素
      • set_difference(iterator beg1, iterator end1, iterator iterator beg2, iterator end2, iterator dest);

        • 求两个集合的差集,返回一个迭代器,该迭代器指向最后一个元素的下一个元素
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值