算法的定义+计算的资源+算法的评估+STL
算法的定义+计算的资源+算法的评估+STL
算法的定义:
算法(Algorithm)是对特定问题求解步骤的一种描述,是指令的有限序列。它有以下5特征。
输入:一个算法有零个或多个输入。程序可以没有输入,例如一个定时闹钟程序,它并不需要输入,但是能够每隔一段时间就输出一个报警。
输出:一个算法有一个或多个输出。程序可以没有输入,但是一定要有输出。
有穷性:一个算法必须在执行有穷步之后结束,且每一步都在有穷时间内完成。
确定性:算法中的每一步指令必须有确切的含义,对于相同的输入只能得到相同的输出。
可行性:算法描述的操作可以通过已经实现的基本操作执行有限次来实现。
计算的资源:
程序运行时需要的资源有两种,即计算时间和存储空间。资源是有限的,一个算法对这两个资源的使用程序可以用来衡量该算法的优劣。
时间复杂度:程序运行需要的时间。
空间复杂度:程序运行需要的存储空间。
算法的评估:
一个程序或算法的复杂度有以下可能:
O(1)
计算时间是一个常数,和问题的规模n无关。例如,用公式计算,一次计算的复杂度就是O(1);哈希算法,用hash函数在常数时间内计算出存储位置;在矩阵A[M][N]中查找第i行第j列的元素,只需要访问A[i][j]就够了。
O(log₂n)
计算时间是对数,通常是以2为底的对数,每一步计算后,问题的规模减少一倍。例如在一个长度为n的有序数列中查找某个数,用折半查找的方法只需要log₂n次就能找到。再如分治法,一般情况下,在每个步骤把规模减少一倍,所以一共有O(log2n)个步骤。
O(log₂n)和O(1)没有太大区别。
O(n)
计算时间随规模n线性增长。在很多情况下,这是算法可能达到的最优复杂度,因为对输入的n个数,程序一般需要处理所有的数。再如图问题,有V个点和E个边,大多数图的问题都需要搜索到所有的点和边,复杂度的上限就是O(V+E)。
O(nlog₂n)
这通常是算法能达到的最优复杂度。例如分治法,一共O(log₂n)个步骤,每个步骤对每个数操作一次,所以总复杂度是O(nlog₂n)。用分治法思想实现的快速排序和归并排序算法的复杂度就是O(nlog₂n)。
O(n² )
一个两重循环的算法,复杂度是O()。例如冒泡排序就是典型的两重循环。类似的复杂度有O(n³)等。
O(2的n次幂)
一般对应集合问题,例如一个集合中有n个数,有求输出它的所有子集,子集有2的n次幂个。
O(n!)
在排列问题中,如果要求输出所有的全排列,那么复杂度就是O(n!)。
在上面的复杂度分成两类:1、多项式复杂度,包括O(1)、O(n)、O(nlog₂n)、O(n²)等;2、指数复杂度,包括O(2的n次幂)、O(n!)等。
然后分享yxc大佬总结的由数据范围反推算法复杂度以及算法内容
STL
STL包含容器、迭代器、空间配置器、配接器、算法、仿函数。
1、容器
顺序式容器
顺序式容器包括vector、list、deque、queue、priority_queue、stack等,它们的特点如下。
vector: 动态数组,从末尾能够快速插入与删除,直接访问任何元素。
list: 双链表,从任何地方快速插入与删除。
deque: 双向队列,从前面或后面快速插入与删除,直接访问任何元素。
queue: 队列,先进先出。
priority_queue: 优先队列,最高优先级元素总是第一个出列。
stack: 栈,后进先出。
vector
定义:数组是基本的数据结构,有静态数组和动态数据两种类型。
vector是STL的动态数组,在运行时能根据需要改变数组的大小,能存放任何类型的对象。
还可以定义多维数组,例如定义一个二维数组:
vectora[MAXN];
常用操作
栈和stack
栈是最基本的数据结构之一,特点是“先进先出”。例如乘坐电梯时,先进电梯的最后出来;一盒泡腾片,最先放进盒子的药片位于最底层,最后被拿出来。
头文件#include<stack>
栈的有关操作:
stack<<Type>Type>s; *//定义栈,Type为数据类型,例如int,float,char等*
s.push(item); *//把item放到栈顶*
s.top(); *//返回栈顶的元素,但不会删除*
s.pop(); *//删除栈顶的元素,但不会返回。在出栈需要进行两步操作,即先top()获得栈* 顶元素,再pop()删除栈顶元素
s.size(); *//返回栈中元素的个数*
s.empty(); *//检查栈是否为空,如果为空,返回true,否则返回false*
队列和queue
队列是基本的数据结构之一,特点是“先进先出”。例如排队,先进队列的先得到服务。
头文件#include<queue>
队列的有关操作:
queue<<Type>Type>q; //定义队列,Type为数据类型,例如int、float、char等
q.push(item); //把item放进队列
q.front(); //返回对手元素,但不会删除
q.pop(); //删除队首元素
q.back(); //返回队尾元素
q.size(); //返回元素个数
q.empty(): //检查队列是否为空
优先队列和priority_queue
优先队列,顾名思义就是优先级最高的先出队。它是队列和排序的完美结合,不仅可以存储数据,还可以将这些数据按照设定的规则进行排序。每次的push和pop操作,优先队列都会动态调整,把优先级最高的元素放在前面。
优先队列的有关操作如下:
q.top();//返回具有最高优先级的元素值,但不删除该元素
q.pop();//删除最高优先级元素
q.push();//插入新元素
在STL中,优先队列是用二叉堆来实现的,在队列中push一个数或pop一个数,复杂的都是O(log₂n)。
可以用优先队列对数据排序:设定数据小的优先级高,把所有数push进行优先队列后一个个top出来,就得到了从小到大的排序。其总复杂度是O(nlog₂n)。
链表和list
STL的list是数据结构的双向链表,它的内存空间可以是不连续的,通过指针来进行数据的访问,它可以最高效率地在任意地方删除和插入,插入和删除操作是常数时间的。
list和vector的优缺点正好相反,他们的应用场景不同。
(1) vector: //插入和删除操作少,随机访问较少。
(2) list: //插入和删除频繁,随机访问较少。
关联式容器
关联式容器包括set、multiset、map、multimap等。
set: 集合,快速查找,不允许重复值。
multiset: 快速查找,允许重复值。
map: 一对一映射,基于关键字快速查找,不允许重复值。
multimap: 一对多映射,基于关键字快速查找,允许重复值。
set
set就是集合。STL的set用二叉搜索数实现,集合中的每个元素只出现一次,并且是排好序的。访问元素的时间复杂度是O(log₂n),非常高效。
set的有关操作:
set<<Type>Type>A;//定义
A.insert(item);//把item放进set
A.erase(item);//删除元素item
A.clear();//清空set
A.empty();//判断是否为空
A.size();//返回元素个数
A.find(k);//返回一个迭代器,指向键值k
A.lower_bound(k);//返回一个迭代器,指向键值不小于k的第一个元素
A.upper_bound(k);//返回一个迭代器,指向键值大于k的第一个元素
map
map是关联容器,它可以实现从键(Key)到值(Value)的映射。map效率高的原因是它用平衡二叉树搜索数来存储和访问。
头文件
#include <
begin() //返回指向map头部的迭代器
clear() //删除所有元素
count() //返回指定元素出现的次数
empty() //如果map为空则返回true
end() //返回指向map末尾的迭代器
equal_range() //返回特殊条目的迭代器对
erase() //删除一个元素
find() //查找一个元素
get_allocator() //返回map的配置器
insert() //插入元素
key_comp() //返回比较元素key的函数
lower_bound() //返回键值>=给定元素的第一个位置
max_size() //返回可以容纳的最大元素个数
rbegin() //返回一个指向map尾部的逆向迭代器
rend() //返回一个指向map头部的逆向迭代器
size() //返回map中元素的个数
swap() //交换两个map
upper_bound() //返回键值>给定元素的第一个位置
value_comp() //返回比较元素value的函数
2、sort()
STL的排序函数sort()是算法竞赛中最常用的函数之一,它的定义有以下两种:
(1)void sort(RandomAccessIterator first,RandomAccessIterator last);
(2)void sort(RandomAccessIterator first,RandomAccessIterator last,Compare comp);
sort()的比较函数
排序是对比元素的大小。sort()可以用自定义的比较函数进行排序,也可以用系统的4中函数排序,即less()、greater()、less_equal()、greater_equal()。在默认情况下,程序是按从小到大的顺序排序的,less()可不写。
#include<bits/stdc++.h>
using namespace std;
bool my_less(int i,int j)
{
return (i<j);//自定义小于
}
bool my_greater(int i,int j)
{
return (i>j);//自定义大于
}
int main()
{
vector<int>a = {9,8,7,6,1,2,3,4,5};
sort(a.begin(),a.begin()+4); //对前4个排序,输出6 7 8 9 1 2 3 4 5
//sort(a.begin(),a.end()); //从小到大排序,输出1 2 3 4 5 6 7 8 9
//sort(a.begin(),a.end(),less<int>()); //输出1 2 3 4 5 6 7 8 9
//sort(a.begin(),a.end(),my_less); //自定义排序,输出1 2 3 4 5 6 7 8 9
//sort(a.begin(),a.end(),greater<int>()); //从大到小排序,输出9 8 7 6 5 4 3 2 1
//sort(a.begin(),a.end(),my_greater); //自定义排序,输出9 8 7 6 5 4 3 2 1
for(int i = 0 ; i < a.size() ; i++)
cout << a[i] << " ";
return 0;
}
相关函数
stable_sort():当排序元素相等时,保留原来的顺序。
partial_sort():局部排序。
3、next_permutation()
STL提供求下一个排列组合的函数next_permutation()。例如3个字符a、b、c组成的序列,next_permutation()能按字典序返回6个组合,即abc、acb、bac、bca、cab、cba。
函数next_permutation()的定义有下面两种形式:
(1)bool next_permutation(BidirectionalIterator first,BidirectionalIterator last);
(2)bool next_permutation(BidirectionalIterator first,BidirectionalIterator last,Compare comp);
返回值:如果没有下一个排列组合,返回false,否则返回true。每执行next_permutation()一次, 就会把新的排列放到原来的空间里。
复杂度:O(n)。
注意,它排列的范围是[first,last),包括first,不包括last。
若使用next_permutation()的时候,初始序列一般是一个字典序最小的序列,如果不是,可以用sort()排序,得到最小序列,然后再使用next_permutation()。