奇技淫巧-STL 库 & ACM算法小技巧(持续更新中~~~)

STL 库中的奇技淫巧

STL 是惠普实验室开发的一系列软件的统称,可以理解为一些容器的集合。STL的目的是标准化组件,这样就不用重新开发,可以使用现成的组件。STL 现在是C++的一部分,因此不用额外安装什么。

STL 中的库

在 C++标准中,STL 被组织为下面的 17 个头文件:<algorithm>、<deque>、<functional>、<iterator>、<array>、<vector>、<list>、<forward_list>、<map>、<unordered_map>、<memory>、<numeric>、<queue>、<set>、<unordered_set>、<stack>和<utility>

#include<algorithm>

它主要是对数组类型(或类似)的数据结构进行操作。

for_each(start , end , function)

解释

这个我只在 C++ primer 见过。意思是,从某个容器的 start 指针,到这个容器的 end 指针,均执行 function 函数。

实例
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int ia[] = { 1,2,3 };
vector<int>ivec(ia, ia + sizeof(ia) / sizeof(int));
void print(int& x)
{
	cout << x << endl;
}
int main()
{
	for_each(ivec.begin(), ivec.end(), print);
	return 0;
}

输出结果

1
2
3  

相当于:将这个容器内的一个元素传入第一个未被占用的参数。缺参数?看一下这个例子。

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int ia[] = { 1,2,3 };
vector<int>ivec(ia, ia + sizeof(ia) / sizeof(int));
struct print
{
	const char* _prefix;
	print(const char* prefix) :_prefix(prefix) {}
	void operator()(int elem)
	{
		cout << _prefix << elem << endl;
	}
};
int main()
{
	for_each(ivec.begin(), ivec.end(), print("Element:"));
	return 0;
}

输出结果

Element:1
Element:2
Element:3

is_sorted(start,end)

解释

返回一个 bool 值,判断这个区间是否排序。

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,3,4,2,5 };
	cout << is_sorted(a, a + 5);
}

输出结果

0

is_sorted_until(start,end)

解释

std::is_sorted_until用于查找范围 [first,last) 中的第一个未排序元素。它将迭代器返回到范围中的第一个未排序元素,因此在first和返回的迭代器之间的所有元素都进行了排序。

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,3,4,2,5 };
	cout << is_sorted_until(a, a + 5) << endl;
	cout << *is_sorted_until(a, a + 5);
}
输出结果
0078F870    
2

sort(start,end,function) 和 stable_sort(start,end,function)

解释

使得 start~end 按 function 规则排序。无返回值。复杂度为稳定 O(nlogn),其中 sort 为不稳定排序。function 当且仅当该容器为数组且 function 未定义时默认为从小到大。

#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,3,4,2,5 };
	sort(a, a+5);
	for (int i = 0; i < 5; i++)
		cout << a[i] << " ";
}
输出
1 2 3 4 5

lower_bound(start,end,val) & upper_bound(start,end,val)

解释
  • lower_bound(start,end,val):返回一个不小于 value 的最小指针
  • upper_bound(start,end,val):返回一个大于 value 的最小指针。
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,3,4,2,5 };
	cout << lower_bound(a, a + 5, a[1]) << endl;
	cout << *lower_bound(a, a + 5, a[1]) << endl;
	cout << upper_bound(a, a + 5, a[1]) << endl;
	cout << *upper_bound(a, a + 5, a[1]) << endl;
}
输出结果
012FF7A4
3
012FF7A8
4

heap

heap(堆)就是用数组实现的二叉树,所以它没有使用父指针或者子指针。堆根据 “堆属性” 来排序,“堆属性” 决定了树中节点的位置。
堆的常用方法:

  • 构建优先队列
  • 支持堆排序
  • 快速找出一个集合中的最小值(或者最大值)。
heap属性

堆属性分为两种:

  • 最大堆。在最大堆中,父节点的值比每一个节点的值都要大。
  • 最小堆。在最小堆中,父节点的值比每一个子节点的值都要小。

但是heap没有迭代器,heap的所有元素都必须遵循特别的 complete binary tree(完全二叉树,整棵binary tree除了最底层的叶节点之外是填满的,而最底层的叶节点由左至右不得有空隙)排序规则,所以heap不提供遍历功能,也不提供迭代器。

在这里插入图片描述

堆和普通树的区别
  • 节点的顺序:
    • 在二叉搜索树中,左子节点必须比父节点小,右子节点必须比父节点大。
    • 但是,在堆中并非如此。在最大堆中两个子节点都必须比父节点小,而在最小堆中,它们都必须比父节点大。
  • 内存占用
    • 普通树占用的内存空间比它们存储的数据要多,必须为节点对象以及左右子节点指针分配足够的内存。
    • 堆仅仅使用一个数据来村塾数组,且不使用指针。
heap算法
push_heap()

push_heap()要求新加入的元素一定要放在最下一层作为叶节点,并填补在由左至右的第一个空格,也就是把新元素插入在底层vector的end()处,如下图所示。

在这里插入图片描述

pop_heap()

在max-heap中,最大值必然在根节点,pop操作取走根节点之后,为了满足complete binary tree的条件,需要将最下一层最右边的叶节点拿掉,而取走的根节点其实是移至底部容器vector的最后一个元素。为满足max-heap的条件,执行下溯程序:

  1. 将根节点(最大值被取走后形成一个“洞”)填入失去生存空间的叶节点值,
  2. 再将它拿来和其两个子节点比较键值,并与较大子节点对调位置,直到这个“洞”的键值大于左右两个子节点,或者到下放至叶节点为止。如下图所示:

在这里插入图片描述

注意:pop_heap之后,最大值只是被置放在底部容器的最尾端,尚未被取走。如果要取其值,可使用底部容器(vector)所提供的back()操作函数。如果要移除它,可使用底部容器(vector)所提供的pop_back()操作函数。

sort_heap()

每次使用pop_heap可获得heap中键值最大的元素,如果持续对整个heap做pop_heap操作,每次将操作范围从后向前缩减一个元素,因为pop_heap会把键值最大的元素放在底部容器的最尾端,当整个程序执行完毕时,就形成一个递增序列,如下图所示。

在这里插入图片描述

注意:排序后,原来的heap就不再是一个合法的heap。

make_heap()

这个算法用来将一段现有的数据转化为一个heap。

例1 底层以vector完成
#include<vector>
#include<iostream>
#include<algorithm>

using namespace std;

void TestHeap1()
{
    int arr[9] = { 0, 1, 2, 3, 4, 8, 9, 3, 5 };
    vector<int> v1(arr, arr + 9);
    cout << "vector data:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    //make_heap
    make_heap(v1.begin(), v1.end());
    cout << "make heap后:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    //push_heap
    v1.push_back(7);
    push_heap(v1.begin(), v1.end());
    cout << "push 7 heap后:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    //pop_heap
    pop_heap(v1.begin(), v1.end());
    cout << "pop heap:";
    cout << v1.back() << endl; //最大值9被放在vector的最尾端,但是没有取走
    v1.pop_back();
    cout << "pop back后:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;

    //sort_heap
    sort_heap(v1.begin(), v1.end());
    cout << "sort heap后:";
    for (int i = 0; i < v1.size(); i++)
    {
        cout << v1[i] << " ";
    }
    cout << endl;
}

int main()
{
    TestHeap1();
    system("pause");
    return 0;
}
输出结果
vector data:0 1 2 3 4 8 9 3 5
make heap后:9 5 8 3 4 0 2 3 1
push 7 heap后:9 7 8 3 5 0 2 3 1 4
pop heap:9
pop back后:8 7 4 3 5 0 2 3 1
sort heap后:0 1 2 3 3 4 5 7 8
例2 底层以array完成
#include<iostream>
#include<algorithm>
 
using namespace std;
 
void TestHeap2()
{
    int arr[9] = { 0, 1, 2, 3, 4, 8, 9, 3, 5 };
    make_heap(arr, arr + 9);
    cout << "make heap后:";
    for (int i = 0; i < 9; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;

    //sort_heap
    sort_heap(arr, arr + 9);
    cout << "sort make后:";
    for (int i = 0; i < 9; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
    //经过排序之后的heap,不再是个合法的heap

    //重新再做一个heap
    make_heap(arr, arr + 9);
    cout << "再一次make heap后:";
    for (int i = 0; i < 9; i++)
    {
        cout << arr[i] << " ";
    }
    cout << endl;
 
    //pop_heap
    pop_heap(arr, arr + 9);
    cout << "pop heap后,查看arr[8]的值:";
    cout << arr[8] << endl;
}
 
int main()
{
    TestHeap2();
    system("pause");
    return 0;
}
输出结果
make heap后:9 5 8 3 4 0 2 3 1
sort make后:0 1 2 3 3 4 5 8 9
再一次make heap后:9 8 5 3 3 4 2 1 0
pop heap后,查看arr[8]的值:9

最大(max/max_element)/最小操作(min/min_element)。

解释
  • max(num1,num2)
  • min(num1,num2);
  • max_element(start,end)
  • min_element(start,end);
#include<vector>
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
	int a[5] = { 1,3,4,2,5 };
	cout << max_element(a, a + 5) << endl;
	cout << *max_element(a, a + 5) << endl;
	cout << min_element(a, a + 5) << endl;
	cout << *min_element(a, a + 5) << endl;
}
输出结果
00AFF7A4
5
00AFF794
1

#include<vector>

是在 C++标准模板库中的部分内容,它是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。可以为一大堆有序容器建立容器, 这是一个动态数组,节约空间。

vector<int>vec;			//创建

vec.push_back(a);		//尾部插入元素:a 为一个元素

vec[0];				//访问:使用数组[]

vec.size();			//求大小

vec.clear();			//清空

vec.insert(pos, val);	        //v.插入:在 pos 的位置的前面插入 val 元素

vec.reverse(start, end);        //反转:将[start,end)之间的元素顺序颠倒。需要头文件<algorithm>

vec.erase(pos);			//vi.删除:删除第 pos + 1 个元素。
vec.erase(start, end);	        //或者:删除[start, end)之间的所有元素。
					
//迭代器:
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++)cout << *it << endl;

#inlcude<map>

map 建立的是一个映射,可应用于离散化模型。

插入

// 定义一个map对象
map<int, string> mapStudent;
 
// 第一种 用insert函數插入pair
mapStudent.insert(pair<int, string>(000, "student_zero"));
 
// 第二种 用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type(001, "student_one"));
 
// 第三种 用"array"方式插入
mapStudent[123] = "student_first";
mapStudent[456] = "student_second";if (it != m.end())m.erase(it);

查找

map<int, int>::iterator it;
it = m.find(num);

删除

if (it != m.end())m.erase(it);

#inlcude<set>

STL 对这个序列可以进行查找,插入删除序列中的任意一个元素,而完成这些操作的时间同这个序列中元素个数的对数成比例关系,并且
当游标指向一个已删除的元素时,删除操作无效。

而一个经过更正的和更加实际的定义应该是:一个集合(set)是一个容器,它其中所包含的元素的值是唯一的。这在收集一个数据的具体值的时候是有用的。集合中的元素按一定的顺序排列,并被作为集合中的实例。

一个集合通过一个链表来组织,在插入操作和删除操作上比向量(vector)快,但查找或添加末尾的元素时会有些慢。具体实现采用了红黑树的平衡二叉树的数据结构。

//插入:
s.insert(num);
//删除:
s.erase(num);
//查找:
s.find(num);
//集合运算:
set set_intersection(set1, set2); //交集
set set_union(set1, set2); //并集
set set_difference(set1, set2); //差集

ACM算法小技巧

判断是否为整数

使用Math.round(取最近的整数)、Math.ceil(向上取整)、Math.floor(向下取整)判断

整数取整后还是等于自己。利用这个特性来判断是否是整数,Math.floor示例,如下

#include <iostream>
#include<cmath>
bool function (double obj) {
    return floor(obj) == obj;
}
int main()
{
    double t;
    std::cin >> t;
    std::cout << function(t);
}
输入输出
输入 3
1
输入 3.3
0

万能头文件

#include<bits/stdc++.h>

快读读入技巧

int read() //快读
{
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9')
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=x*10+c-'0';
		c=getchar();
	}
	return x*f;
}


inline int read() //我喜欢快读
{
	int num = 0;
	char c;
	bool flag = false;
	while ((c = getchar()) == ' ' || c == '\n' || c == '\r');
	if (c == '-') flag = true;
	else
		num = c - '0';
	while (isdigit(c = getchar()))
		num = num * 10 + c - '0';
	return (flag ? -1 : 1) * num;
}

欧几里得算法

欧几里得算法又称辗转相除法,是指用于计算两个非负整数a,b的最大公约数。应用领域有数学和计算机两个方面。计算公式 g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b) = gcd(b,a mod b) gcd(a,b)=gcd(b,amodb)

inline int gcd(int x,int y)
{
	if(y==0) return x;
	return gcd(y,x%y);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值