C++ 语法基础(3) STL容器与位运算

STL容器

vector

vector是变长数组,支持随机访问,不支持在任意位置O(1)插入

vector定义

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> a;			//定义一个vector数组
	vector<int> b[10];		//相当于第一维长10,第二位长度动态变化的int数组
	struct res
	{
		int x,y; 
	};
	vector<res>	c;    		//自定义的类型也可放在vector中	 
	return 0;
}

size函数:返回vector的实际长度(包含的元素个数),时间复杂度为O(1)
empty函数:返回一个bool类型,表明vector是否为空,时间复杂度为O(1)
clear函数:把vector元素清空。

迭代器就像STL容器的“指针”,可以用星号“*”操作符解除引用。
一个保存int的vector的迭代器声明方法为 vector<int>::iterator it;
vector的迭代器是“随机访问迭代器”,可以把vector的迭代器与一个整数相加减,其行为和指针的移动类似。可以把vector的两个迭代器相减,其结果也和指针相减类似,得到两个迭代器对应下标之间的距离

所有的容器都可以视作一个“前闭后开”的结构.
begin函数:返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()a[0]的作用相同。
end函数:返回vector的尾部,即第n个元素再往后的“边界”。*a.end()a[n]都是越界访问,其中n=a.size()

vector的遍历

#include <iostream>
#include <vector>

using namespace std;

int main()
{
	vector<int> a({1,2,3,4,5});			//定义一个vector数组
	
	for(int i=0;i<a.size();i++)		cout << a[i] << ' ';		
	cout << endl;
	
	for(vector<int>::iterator i = a.begin();i!=a.end();i++)		cout << *i << ' ';  // 迭代器遍历 迭代器可以用auto简写
	
	cout << endl; 
	
	for(int x : a)			cout << x << ' ';
	cout << endl;
	
	return 0;
}

vector 常用函数

front函数:返回vector的第一个元素,等价于a.begin() 和 a[0]
back函数:返回vector的最后一个元素,等价于
==a.end() 和 a[a.size() – 1]

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> a({1,2,3,4,5});			//定义一个vector数组
	
	cout << a.front() << ' ' << a[0] << ' ' << *a.begin() << endl;
	cout << a.back() << ' ' << a[a.size()-1] << endl; 
	
	return 0;
}

push_back(x):把元素x插入到vector 的尾部。
pop_back()删除vector 的最后一个元素。

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> a({1,2,3,4,5});			//定义一个vector数组
	
	for(auto x: a)		cout << x << ' ';
	cout << endl;
	
	a.push_back(6);    //末尾添加元素6
	for(auto x: a)		cout << x << ' ';
	cout << endl;
	
	a.pop_back();		//删除末尾元素
	a.pop_back();
	for(auto x: a)		cout << x << ' ';
	cout << endl;
	
	return 0;
}

queue

头文件queue主要包括循环队列queue和优先队列priority_queue两个容器。

queue定义

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	queue<int> a;    		//定义队列 
	queue<int> b;
	struct Rec
	{
		int x,y,z;	
	};
	queue<Rec> c;
	
	priority_queue<int> d; //大根堆
	priority_queue<int,vector<int>,greater<int>> e;  //小根堆 
	priority_queue<pair<int,int>> f;			//二元组pair也可存于队列中 
	
	return 0;
}

注意:
自己定义的结构体存于优先队列,需要重载比较方法
大根堆需要重载小于号
小根堆需要重载大于号

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	struct Rec
	{
		int x,y;
		bool operator< (const Rec& t) const		//重载小于号 
		{
			return x < t.x;
		}
	};
	
	priority_queue<Rec> a;
	a.push({1,2});
	
	struct Rec2
	{
		int x,y;
		bool operator> (const Rec2 &t) const		//重载大于号 
		{
			return x > t.x;
		}
	};
	priority_queue<Rec2,vector<Rec2>,greater<Rec2>> b;
	b.push({3,4}); 

	return 0;
}

queue遍历

queue常用函数

普通队列:
push(x) 从队尾插入x
pop() 从队头弹出
frontx() 返回队头元素
back() 返回队尾元素

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	queue<int> q; 
	
	q.push(1);		//队尾插入元素 
	q.push(2);
	q.push(3);
	cout << q.front() << endl;		//队首元素 
	cout << q.back() << endl;		//队尾元素 
	
	q.pop();		//从队首删除一个元素 
	cout << q.front() << endl;		//队首元素 
	cout << q.back() << endl;		//队尾元素 
	
	return 0;
}

priority_queue优先队列
push(x):向队列中插入元素x
pop(): 删除优先队列中最大值
top:返回优先队列的最大值

#include <iostream>
#include <queue>
using namespace std;

int main()
{
	priority_queue<int> q;			//定义大根堆 
	q.push(3);
	q.push(1);						//插入元素 
	q.push(2);
	
	cout << q.top() << endl; 		//查看最大值
	q.pop();						//弹出最大值
	cout << q.top() << endl; 		//查看最大值
	
	return 0;
}

注:queue,priority_queue,stack没有clear()函数
清空操作(重新定义):

q = queue<int>();

stack

栈的定义与操作

#include <iostream>
#include <stack>
using namespace std;

int main()
{
	stack<int> stk;			//定义栈 
	stk.push(1);			//添加元素 
	stk.push(2);
	stk.push(3);
	
	cout << stk.top() << endl;		//返回栈顶元素 
	
	stk.pop();						//删除栈顶元素 
	cout << stk.top() << endl;
	
	return 0;
}

双端队列

双端队列deque是一个支持在两端高效插入或删除元素的连续线性存储空间。它就像是vector和queue的结合。与vector相比,deque在头部增删元素仅需要O(1)的时间;与queue相比,deque像数组一样支持随机访问。

双端队列的定义和操作

[] 随机访问
begin/end:返回deque的头/尾迭代器
front/back :队头/队尾元素
push_back: 从队尾入队
push_front :从队头入队
pop_back:从队尾出队
pop_front :从队头出队
clear: 清空队列

#include <iostream>
#include <deque>

using namespace std;

int main()
{
	deque<int>	a;
	a.begin(),a.end();		//返回deque的头/尾迭代器
	a.front(),a.back();		//返回队头/队尾元素
	
	a.push_back(1),a.push_front(2);		//从队尾插入元素,从队首插入元素
	a.pop_back(),a.pop_front();		//从队尾删除元素,从队首删除元素
	a.clear()     //清空元素
		
	return 0;
}

Set

头文件set主要包括set和multiset两个容器,分别是“有序集合”和“有序多重集合”,即前者的元素不能重复,而后者可以包含若干个相等的元素。set和multiset的内部实现是一棵红黑树,它们支持的函数基本相同。

set定义

注意:当set集合中的元素为结构体时,该结构体必须实现运算符‘<’的重载

#include <iostream>
#include <set>
using namespace std;

const int N = 1e5+10;
int cnt[N],a[N];
int main()
{
	set<int>  a;		//set的定义
	struct Rec
	{
		int x,y;
		bool operator< (const Rec &t) const			//需要重载小于运算符 
		{
			return x<t.x;
		}
	};	
	set<Rec> b;
	return 0;
}

set的常用操作

clear() 删除set容器中的所有的元素

empty() 判断set容器是否为空

size() 返回当前set容器中的元素个数

set和multiset的迭代器称为“双向访问迭代器”,不支持“随机访问”,支持星号(*)解除引用,仅支持”++”和–“两个与算术相关的操作。

set<int>::iterator it;

若把it++,则it会指向“下一个”元素。这里的“下一个”元素是指在元素从小到大排序的结果中,排在it下一名的元素。同理,若把it–,则it将会指向排在“上一个”的元素。

insert(x) 把一个元素x插入到集合set中,时间复杂度O(logn)。
find(x) 在集合s中查找等于x的元素,并返回指向该元素的迭代器。若不存在,则返回s.end()。

lower_bound,upper_bound这两个函数的用法与find类似,但查找的条件略有不同,时间复杂度为 O(logn)。
lower_bound(x)查找大于等于x的元素中最小的一个,并返回指向该元素的迭代器
upper_bound(x) 查找大于x的元素中最小的一个,并返回指向该元素的迭代器

erase(x)
若x是一个迭代器,则erase(x) 是指从s中删除迭代器x指向的元素,时间复杂度为O(logn)
若x是一个元素,erase(x) 是指从s中删除所有等于x的元素,时间复杂度为O(k+logn),其中k是被删除的元素个数。

count(x)返回集合s中等于x的元素个数,时间复杂度为 O(k +logn),其中k为元素x的个数。

unordered_set

与set类似,但效率更高,但因为无序,所以不支持二分,没有upper_boundlower_bound 函数

unordered_multiset

在unordered_set 基础上允许存重复元素

unordered_set<int> a;     		 //哈希表     不能存储重复元素
unordered_multiset<int> b;		 //哈希表     能存储重复元素

map

map的定义

自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。

//第一种         insert方法
map.insert({key,value});
// 第二种       用"array"方式插入
map[key] = value;
#include <iostream>
#include <map>
#include <vector>

using namespace std;
 
int main()
{
	map<string,int> a;		//定义map 
	map<string,vector<int>> b;
	
	a["yxc"] = 1;			//类似于数组操作
	vector<int> c({1,2,3});
	b.insert({"acwing",c});  //insert方法
	
	return 0;
}

map的常用操作

size()empty()clear()begin()end()均与set类似。
find(x) 在map中查找key为x的二元组。

[]操作符
map[key] 返回key映射的value的引用,时间复杂度为O(logn)。
[]操作符可以很方便地通过key来得到key对应的value,还可以对map[key]进行赋值操作,改变key对应的value。

unodered_map

与map类似,区别在于无序,因此操作的复杂度更低,效率更高

unodered_map <int> a;

pair

pair是将2个数据组合成一组数据,两个值可以分别用pair.first和pair.second访问。
比较运算,先比较第一个元素,按照字典序,然后再比较第二个元素

#include <iostream>

using namespace std;
 
int main()
{
	pair<string,int> a;
	pair<string,int> b;
	a = {"acwing",1};
	b = make_pair("yxc",2); 
	
	cout << a.first << ' ' << a.second << endl;
	cout << b.first << ' ' << b.second << endl;
	
	return 0;
}

位运算与常用库函数

位运算

&
|
~
^ 异或
>> 右移
<< 左移

常用操作:
(1) 求x的第k位数字

x >> k & 1

(2)返回x的最后一位1
返回x的二进制表达式中最低位的1所对应的值

lowbit(x) = x & -x

常用库函数

(1) reverse 翻转

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
 
int main()
{
	vector<int> a({1,2,3,4,5});
	reverse(a.begin(),a.end());			//翻转vector 
	
	for(int x : a)	cout << x << ' ';
	cout << endl;
	
	int b[] = {1,2,3,4,5};
	reverse(b,b+5);						//翻转数组 
	for(int x : b)	cout << x << ' ';
	cout << endl;
	
	return 0;
}

(2) unique 去重
返回去重之后的尾迭代器(或指针),仍然为前闭后开,即这个迭代器是去重之后末尾元素的下一个位置。该函数常用于离散化,利用迭代器(或指针)的减法,可计算出去重后的元素个数。

#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;
 
int main()
{
	int a[] = {1,2,2,3,4,4,5};
	vector<int> b({1,2,2,3,4,4,5});
	
	int m = unique(a,a+7) - a;      //计算去重后元素还有几个 
	 
	cout << m << endl;
		 
	for(int i=0;i<m;i++)	cout << a[i] << ' ';		//输出去重后的数组 
	cout << endl;
	
	b.erase(unique(b.begin(),b.end()),b.end());       //去重并删除后面重复的元素 
	for(int x : b)		cout << x << ' ';
	
	return 0;
}

(3) random_shuffle随机打乱
用法与reverse相同

#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime> 

using namespace std;

int main()
{
	vector<int> a({1,2,3,4,5});
	
	srand(time(0));			//设置随机数种子 
	random_shuffle(a.begin(),a.end());		//随机打乱 
	
	for(int x:a)		cout << x << ' ';
	cout << endl;
	return  0;
}

(4) sort
对两个迭代器(或指针)指定的部分进行快速排序。可以在第三个参数传入定义大小比较的函数,或者重载“小于号”运算符。
数组的排序:
从小到大:

sort(a.begin(),a.end());	//从小到大排序 

从大到小:

sort(a.begin(),a.end(),greater<int>());		//从大到小排序 
#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime> 

using namespace std;

int main()
{
	vector<int> a({3,1,4,5,2});
	
	sort(a.begin(),a.end());	//从小到大排序 
	for(int x:a)		cout << x << ' ';
	cout << endl;
		
	sort(a.begin(),a.end(),greater<int>());		//从大到小排序 
	for(int x:a)		cout << x << ' ';
	cout << endl; 
	
	return 0;
}

结构体自定义排序
方法一:自定义比较函数cmp

#include <iostream>
#include <algorithm>

using namespace std;

struct Rec
{
	int x,y;	
}a[5];

bool cmp(Rec a,Rec b)		//a是否应该排在b的前面 
{
	return a.x<b.y;
}

int main()
{
	
	for(int i=0;i<5;i++)
	{
		a[i].x = -i;
		a[i].y = i;
	}
	
	for(int i=0;i<5;i++)	printf("(%d,%d) ",a[i].x,a[i].y);
	cout << endl;
	
	sort(a,a+5,cmp);
	for(int i=0;i<5;i++)	printf("(%d,%d) ",a[i].x,a[i].y);
	cout << endl;
	
	return 0;
}

方法二:在结构体内重载比较运算符

#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime> 

using namespace std;

struct Rec
{
	int x,y;	
	bool operator< (const Rec &t) const
	{
		return x < t.x;
	} 
}a[5];

int main()
{
	
	for(int i=0;i<5;i++)
	{
		a[i].x = -i;
		a[i].y = i;
	}
	
	for(int i=0;i<5;i++)	printf("(%d,%d) ",a[i].x,a[i].y);
	cout << endl;
	
	sort(a,a+5);
	for(int i=0;i<5;i++)	printf("(%d,%d) ",a[i].x,a[i].y);
	cout << endl;
	
	return 0;
}

(5)lower_bound/upper_bound 二分(在排好序的基础上)
lower_bound 的第三个参数传入一个元素x,在两个迭代器(指针)指定的部分上执行二分查找,返回指向第一个大于等于x的元素的位置的迭代器(指针)。
upper_bound的用法和lower_bound大致相同,唯一的区别是查找第一个大于x的元素。当然,两个迭代器(指针)指定的部分应该是提前排好序的。

#include <iostream>
#include <algorithm>
#include <vector>
#include <ctime> 

using namespace std;

int main()
{
	int a[] = {1,2,3,4,5,6};
	int *p = lower_bound(a,a+6,3);   //返回大于等于3的位置的迭代器 
	cout << *p << endl; 
	
	int *q = upper_bound(a,a+6,3);	 //返回大于3的位置的迭代器 
	cout << *q << endl;
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值