C++语法基础(进入算法世界的前置吟唱)

三元运算符

三元运算符只支持表达式而不支持语句

一.表达式:

定义:由操作符和操作数构成的式子。

操作数就是常量或变量。

操作符则是+,=,/,*,/=,(),[],&,&&等等等等,也可以叫做运算符。

三元表达式注意点:

....
int main() {
    // cout <<  三元表达式的注意点,每行单独 cout << 一个三元表达式后,在这行后面就不要再输出其他内容了。
    cout << a && b ? 1 : 0 << " "; // 会报错
    cout << " "; // 不会报错
    cout << (a || b) ? 1 : 0; // 和 cout << a || b ? 1 : 0; 不一样
    cout << (a & !b || !a &b) ? 1 : 0;
    cout << !(a | b) ? 1 : 0;
}

注:任何表达式都是有返回值的。可以理解为表达式本身也有值。

关闭输入输出同步流

ios::sync_with_stdio(false);

常用函数

ceil 返回的是大于x的最小整数 若a / b (均为int类型)则可以用 (a + b - 1) / b 来实现向上取整

floor(x) 返回的是小于或等于x的最大整数

一些等价写法

!= -1 和 ~

循环语句

1.while的多种用法

while (true) {
    cin >> x;
    if (!x) break;
} 

	
while (cin >> x && x) {}	// 上个while语句中的cin >> x是可以写到while()里的,cin本身是有返回值的,如果返回0,表示没有读到值(读到了结束的位置,即读到了文件结束符EOF(-1),返回0或者false,表示输入结束)。

while (cin >> x, x) {}	// 逗号表达式的值等于最后一个表达式的值

// scanf读到结束会返回-1

while也可以用来控制循环次数,比如知道了循环次数n,使用while(n–)即可控制循环次数(因为先判断后才减一),但是循环体里若要用到n,循环体里的 n 已经执行过判断后的 i-- 了,如最开始 n == 3 , 经过判断且 n-- 后,首次进入循环体里的 n 会是 2 ,所以最好不要这样,不如用for循环。

2.判断奇数

判断一个整数是否为奇数时,不能用 i % 2 == 1 ,当 i 为负奇数时,会返回负数(当取余操作返回一个非零的结果时,它与左操作数具有相同的正负符号。因此,用 % 2 判断是否为奇数时,不为奇数时返回得到0,为奇数时,可以得到 -1 和 1。),因此无法判断。

因此,可以使用 i % 2 (i % 2 在判断语句里等价于 i % 2 != 0 )。

还可以使用 (i & 1) != 0 (因为奇数的二进制最后一位为1,偶数的二进制最后以为0。)

数组

数组中是会存其长度的

定义在函数外数(全局变量)的数组会保存在堆空间,若未初始化默认值都会是0。

定义在函数内的数组会保存在栈空间,若未初始化默认值是随机的。

感觉还可以把数组的下标理解为前面有几个数

最好不要用变量来定义一个数组的长度,有的编译器可能不支持,最好用一个常量。

1.初始化方法

①一维数组初始化方法

int a[3] = {0, 1, 2};
int b[] = {0, 1, 2};

int c[5] = {0, 1, 2};	//定义了一个长度是5的数组,没有赋值的元素,默认值为0。
int f[10] = {0};	//定义了一个长度为10的数组,元素值均为0。

②多维数组初始化方法

多维数组在C++中是通过转化成一位数组实现的

int b[3][4] = {{1, 2, 3, 4}, {2, 2, 3, 4}, {3, 2, 3, 4}};
int b[3][4] = {{1, 2, 3, 4}, 
               {2, 2, 3, 4},
               {3, 2, 3, 4}};

③memset函数

用里的memset函数,如memset(a, 0, 40),其中a为数组名,第二位为数组中每一个字节所要赋的值(只有赋0或者负数的时候才好实现存入什么初始化为什么的功能,如第二位为1,是将每一个字节赋值为0000001),40为所要初始化的长度(0是int类型,若想将数组的10个元素初始化为Int类型的数据,则要将初始化的长度设置为40),第三位也可以直接用sizeof()来设置(size of 可以不加括号,如size of a)。

memset(a, -1, size of a);	//将a数组的所有元素均初始化为-1

//memset会比下面这种方法初始化快很多
for (int i = 0; i < 1000; i++) a[i] = -1;

字符串

1.强制类型转换

常用ASCII码:A~Z : 65~90 a~z : 97~122 0 ~9 : 48~57

2.字符数组

字符串就是字符数组加上结束符’\0’

用字符串来初始化字符数组时要注意,每个字符串结尾会暗含一个’\0’字符,因为字符数组的长度至少要比字符串的长度多1。

#include <iostream>
using namespace std;
int main() {
	// 只是一个普通的字符数组
	char a1[] = {'C', '+', '+'};
    // 长度为4的字符数组,存储的字符串的长度是3。
	char a2[] = {'C', '+', '+', '\0'}
	// 如果定义数组大小为3,则会报错。
	char a3[4] = "C++";
	// 有可能会输出两个C++,会一直读到a2数组的结束符(a1和a2开辟的地址恰巧连续)。
	cout << a1;	
}

3.字符数组的输入和输出

输入

数组名→首元素地址

scandf和cin 一般是读到空格、回车或者是结束符就会停止

如果想读入一行字符串(就算有空格):gets() 不安全,已经被淘汰了,可以用fgets(参数①,参数②,参数③) 函数,参数①为要读入的字符串名,参数②为读入的字符长度的最大值,参数③为读入的文件,可以为stdin。如 fgets(s, 100, stdin); // 读入一行到字符串或字符数组里

cin.getline(①,②); // 读用字符数组初始化的字符串

getline(参数①,参数②),如getline(cin, string); // 读String类型的字符串

char s[100]此类的字符数组形式的字符串,在使用scanf输入时,只需要scanf(“%s”, s); 即可,s不需要加$。

string无法用scanf读,cin可以读。

输出

// puts();
char str[100] = {'z', 'e', 'c'};
puts(str); // zec
cout << str;	// zec

4.字符数组的常用操作

引入头文件 #include <string.h> 或 #include

①求长度 strlen(str) // 不会包含\0

②比较 strcmp(a, b) // 按照字典序来比较,a > b 成立返回1,不成立返回0。

注意:若string想要使用 strcmp(); 则应该使用c_str(),如strcmp(a.c_str(), b.c_str()); 。

③复制 strcpy(a, b) // 将字符串b复制给从a开始的字符数组

5.遍历字符数组中的字符

#include <iostream>
#include <string.h>
using namespace std;

int main() {
	char a[100] = "hello world";
    // strlen()最好不要放到判断的部分,不然效率很低,每次判断都要执行一次strlen()。放到初始化部分里,则可以只用执行一次即可。
	for (int i = 0, len = strlen(a); i < len; i++)	
		cout << a[i];
	return 0;
}
// 也可以不用求字符串长度,for (int i = 0; a[i]; i++)	

6.标准库类型 string

有,可以不用写。

①定义和初始化

#include <iostream>
#include <cstring>
using namespace std;
int main() {
    string s1;	// 空字符串
    string s2 = s1;	// s2时s1的一个副本
    string s3 = "chao"	// s3是该字符串字面值的一个副本
    string s4(10, 'c')	// s4的内容是:cccccccccc
}

string 类型的数据可以直接使用 cin 和 cout 来输入和输出。无法使用 scanf 来输入,可以使用 printf 来输出,如printf(“%s\n”, s1.c_str());,不过不能用 printf 直接输出string。

输入:

string s1, s2;
getline(cin, s1);	// getline有空格也没事
cin.getline(s2, 100);
cin >> s1;	// 只能读入第一个字符串

②操作

①empty();

②size(); // 复杂度为o(1),在string里专门有一个存放string长度的变量,执行很快;size 是无符号整数,因此 s.size() <= -1 一定成立。

③:比较,支持 > < >= <= == != 等所有比较操作,按字典序进行比较。

④:两个string可以相加

⑤:字面值和string对象相加

在做加法运算时,字面值和字符都会被转化成string对象,因此直接相加就是把这些字面量串联起来。

字符串进行相加操作时,必须确保每个加法运算符的两侧的运算对象至少有一个是string类型。

⑥:substr(); 如 a.substr(i, len); 第一个参数为开始的位置,第二个参数为所要截取的长度。如 a.substr(i) 从位置i开始一直截取到最后。

③string对象中的字符

如何处理string对象中的字符?

将string对象当成字符数组来处理或使用基于范围的for语句

string s = "Hello world";
for (int i = 0; i < s.size; i++) cout << s[i] << endl;
for (char c : s) cout << c << endl;

for (char $c : s) c++	// 可以改变s的值,上面char c : s 则不行。

函数

声明的时候可以只写函数类型

①数组形参

在函数中队数组中的值进行修改,会影响函数外面的数组。

一维数组形参的写法:

// 三种等价写法
void print(int *a) {};
void print(int a[]) {};
void print(int a[10]) {};

多维数组的写法:

// 除了第一维之外,其余维度的大小必须指定。
void print(int(*a)[10]) {};
void print(int a[][10]) {};

②:函数参数默认值

只能给最后面连续的几个参数定义(也可以全都定义默认值)

int abc(int a = 0, int b = 0, int c = 0) {};
int abc(int a, int b = 0, int c = 0) {};
int abc(int a, int b, int c = 0) {};
int abc(int a = 0, int b) {};	// 报错

③:sizeof()

void asize(int a) {}

结构体、类、指针、引用

struct Person {
	int age, height;
	double money;
	
	person() {};
	
	person(int a_age, int _height, double _money) {
		age = _age;
		height = _height;
		money = _money;
	}
};

结构体运算符重载

// 如该题:三元组排序,定义一个结构体,根据int x 的大小升序排列。
#include <iostream>
#include <iomanip>
#include <vector>
#include <algorithm>
using namespace std;

const int N = 10000;

struct Data {
        int x;
        double y;
        string z;
        bool operator< (const Data &t) const { // 结构体中重载小于号
            return x < t.x;
        }
    }a[N];    

int main() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) cin >> a[i].x >> a[i].y >> a[i].z;
    sort(a, a + n); // sort(begin(), end());
    for (int i = 0; i < n; i++) {
        cout << fixed << setprecision(2) << a[i].x << ' ' << a[i].y << ' ' << a[i].z << endl;
    }
}

指针

堆从下往上开辟空间

栈从上往下开辟空间

指针指向存放变量的值的地址,因此我们可以通过指针来修该变量的值。类似通过数组下标来修改数组里面的值。

数组名是一种特殊的指针,指针可以做运算。

// 存放一个数据的地址,即可实现指向该数据。
int a = 10;
int* p = &a;	// 定义了一个int类型的指针p,p指向a的地址
cout << *p; // 10, *相当于一种操作符。p指向a,然后通过*p即可访问a的值。
cout << 10;
*p = 23;
cout << *p; // 23
cout << a; // 23

int** q = &p; // 指针的指针,此处指针q为存放指针p的地址。
int*** o = &q;

引用

引用和指针类似,相当于给变量起个别名。

int a = 23;
int &p = a; // 引用、别名

链表

如定义一个链表的节点的结构体,val则为该节点的值,next则为一个存放了该节点指向的下一个节点的地址的指针,即next的值为下一个节点的地址,next指向下一个节点。

struct Node {
	int val;
	Node* next; // 定义一个指针,存放下一个节点的地址,注意节点的地址和节点存放的地址的区别。
    
    Node(int_val) : val(_val), next(NULL) {};
};

Node a = node(1);
a.next, a.val; // 如果调用是一个变量,不是一个指针,需要这样。

// C++中可以用new:生成一个结构体,然后把这个结构体的指针放到p里
Node* p = new Node(1); // 加new返回的是地址,不加new返回的是值。
p->next = p; // next指针,是可以指向另外一个地址的,此处p指向自己。且此处p为一个指针,所以要用->调用。可以理解为用->来访问其成员变量。

auto q = new node(2); // 此处 auto 能自动推断出q为Node*
p->next = q; // 让p指向q,然后q会指向一个空指针。

auto o = new node(3); 
q->next = o; // 让q指向p,然后o会指向一个空指针。

头结点:一般会把头节点的地址存到 head 里面,一般说的是第一个节点的地址,而不是第一个节点本身(值)。

遍历单链表:

Node* p = new Node(1); // 指针只p存了该节点的地址,指针p所指向的内容为node结构体类型。Node* p 表示p是一个返回Node结构体类型数据的指针,。
Node* q = new Node(2);
Node* o = new Node(3);
p->next = q; // 将q指向的节点(因为指针q存放了该节点的地址,则指针q可以指向该节点,如上面*p即可访问指针p指向的数据的值)的地址赋给p指向的节点存放的地址,即可以实现p存放的节点指向q存放的节点。
q->next = o;

Node* head = p; // 把指针p指向的节点的地址(指针p的值是一个节点的地址)给指针head。

// 链表的删除:将这个点跳过去或者遍历时遍历不到这个点
// 删除节点,如删除val = 1的节点
head->next = head->next-next;

// 在链表中添加节点
Node* u = new Node(4); // 刚创建出来时只会指向一个空指针
u->next = head; // head中本来存放的是指针p的值(即指针p指向的节点(之前的头节点(存放在指针head中))的地址),即此时将之前的头结点的地址赋给指针u,即可以实现此时u指向的节点指向之前的头节点(此时指针u存放的是之前的头节点的地址)。
head = u; // 更新指针head存放的头节点的地址

for (Node* i = head;i != null; i->next) { // head的值是node结构体类型数据(此处为一个节点)的地址,将这个值赋给i,则i指向的值自然也为一个node结构体类型的数据,因此此处也要 Node* i。指针i存放的是一个node结构体类型的数据的地址(此处即一个节点的地址),该节点的地址的值则为该节点(该地址中存放了该节点),因为再通过->即可实现访问该结构体类型数据的成员变量(访问该节点存放的下一个节点的地址)。
	cout << i->val << endl;
}

空节点的写法:

0 NULL nullptr

STL容器、位运算与常用库

STL容器

除了队列,优先队列,栈,其它所有的STL元素都有 clear() 函数。

vector

在数组结尾插入或删除是o(1),在数组开头插入或删除是o(n)。

#include <iostream>
#include <vector>
using namespace std;
int main() {
	vector<int> a;
	vector<int> b[23];
    vector<vector<int> c; // 相当于一个二维数组
	a.size(); // 返回vector的实际长度(包含的元素个数),时间复杂度o(1)。
    a.empty(); // 返回bool类型,表明vector是否为空,时间复杂度o(1)。
    a.clear(); // 把当前vector的元素清空
    
    // 迭代器(一般用得少)
    //vector的迭代器是随机访问迭代器,可以把vector的迭代器与一个整数相加减,其行为和指针的移动相似。可以把vector的两个迭代器相减,其结果也和指针相减类似得到两个迭代器对应下标之间的距离。
    
    // 声明了一个保存int类型数据的vector的迭代器
    vector::iterator it = a.beagin(); // begin函数返回指向vector中第一个元素的迭代器。例如a是一个非空的vector,则*a.begin()与a[0]的作用相同。
    *it; // 即可以取it的值
    
    // 所有的容器都可以视作一个 前闭后开 的结构, end函数返回vector的尾部(最后一个元素的下一个位置,相当于没有),即第n个元素再往后的边界。*a.end()与a[n]都是越界访问,其中n == a.size()。
    
    // front()取第一个元素
    cout << a.front << a[0] << *a.begin() << endl; // 三种写法都等价
    // back()取最后一个元素
    cout << a.back() << a[a.size() - 1] << endl; // 两种等价写法
    
    a.push_back(23); // 在最后插入一个元素
    a.pop_back(); // 删除最后一个元素
         
	struct Rec {
		int x, y;
	};
	vector<Rec> c; // 其类型也可以是一个结构体
    
    // vector自带比较运算,按照字典来比较,先比较两个数里的第一个位置,如果一样再比较第二个位置。
}
遍历vector
 //下面代码都实现了遍历 vector<int>a,并输出所有的元素。
for (int i = 0; i < a.size(); i++) cout << a[i] << endl;
for (vector<int>::iterator it = a.begin(); it != a.end(); it++) cout << *it << endl;
for (auto i = a.begin(); i != a.end(); i++) cout << *i << endl;
for (int x : a) cout << x << ' ';  

queue(队列)和优先队列(堆)

进行插入操作的端称为队尾,进行删除操作的端成为队头。

队列:先进先出

#include主要包括循环队列queue和优先队列priority_queue(出的时候会优先出最大值)两个容器。

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

int main() {
    queue<int> q;
    queue<double> a;
    
    q.push(1); // 插入元素
    q.pop(); // 弹出元素,并返回该元素。
    q.front(); // 返回对头元素
    q.back(); // 返回队尾元素
    struct Rec {
        int a, b;
    	bool operator< (const Rec& t) const { // 重载的写法
        	return a < t.a;
    	}  
  	}; // 若要定义结构体类型的priority_queue,结构体中必须重载小于号。如果用的是小根堆的话,就要重载大于号。
    queue<Rec> b;
    
    priority_queue<int> a; // 默认是大根堆
    priority_queue<int,vector<int>,greater<int>> b; // 小根堆
    priority_queue<pair<int,int>> q;
    
    a.push(1); // 插入一个数
    a.top(); //取最大值
    a.pop(); // 删除最大值
    
    // 清空一个队列,初始化即可。
    q = queue<int>();
}

stack

栈,先进后出。

函数栈和这个栈不一样,函数栈只是逻辑上是一个栈,是用汇编实现的。

#include <iostream>
#include <stack>
int main() {
    stack<int> stk;
    stk.push(1);
    stk.top();
    stk.pop();
}

双端队列deque

两头都可进可出,均为o(1)。

支持随机存储

#include <deque>
...
deque<int> a;
a.begin(), a.end();
a.front(), a.back();
a.push_back(1), a.push_front(2);
a[0];
a.pop_back(), a.pop_front();
a.clear();

set

底层是用平衡树实现的

用来实现动态维护一个有序的集合(只有key)

#include

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

int main() {
	set<int> a; // 元素不能重复
	multiset<int> b; // 元素可以重复
    multiset<double> c;
    struct Rec {
        int x, y;
        bool operator< (const Rec& t) const { // 必须要重载小于号
            return x < t.x;
        }
    }
    
    // 与vector类似
    a.size();
    a.empty();
    a.clear();
    
    // set也支持迭代器,set和multiset的迭代器称为“双向访问迭代器”,不支持随机访问,支持 * 解除引用,只支持 "++" 和 "--"两个与算术相关的操作。
    set<int>::iterator it; // 若it+,则it会指向下一个元素(只在元素从小到大排序的结果中,排在it下一名的元素),it--,同理。
    it = a.begin(); // 指向集合中最小元素的迭代器
    it++, it--; 
    ++it, --it;
    a.end(); // set和vector一样是一个前闭后开的容器,end()是用来指向集合中最大元素的下一个位置的迭代器,因为--a.end()指向的是集合中最大的元素的迭代器。
    
    a.insert(x); // 若元素已存在,则不会重复插入元素。时间复杂度o(logn)。
   a.find(x); // 在集合中查找等于x的元素,并返回指向该元素的迭代器,若不存在则返回 a.end()。时间复杂度为o(logn)。
    if (a.find(x) == a.end()); // 判断x在a中是否存在
    
    // lower_bound/upper_bound,这两个函数的用法与find类似,但查找的条件略有不同,时间复杂度为o(logn)。
    // 注意都是查找最小的一个,但一个是大于等于,一个是大于。
    a.lower_bound(x); // 查找大于等于x的元素中最小的一个,并返回相应的迭代器。 
    a.upper_bound(x); // 查找大于x的元素最小的一个,并返回相应的迭代器。
    
    a.erase(it); // 若it是一个迭代器,则表示从s中删除迭代器it指向的元素,时间复杂度为o(logn)。
    a.erase(x); // 若x是一个元素,则表示从a中删除所有等于x的元素。时间复杂度为o(k+logn)。
    
    
    a.count(x); // 返回集合中和x相等的元素的个数,时间复杂度为o(k+logn),k为元素的个数。
    
}

map

一种映射

其内部是现实一颗以key为关键字的红黑树

map的key和value可以是任意类型,其中key必须定义小于号运算符。

int main() {
    // map容器是一个键值对key-vlue的映射
	map<int, int> a; // 其类型为一个二元组
	a[1] = 2;
	a[1000] = 23;
    cout << a[1];
	cout << a[1000];
	
	map<string, int> b;
    b["zec"] = 2;
    cout << b["zec"];
    
	map<string, vector<int>> c;
	c["zec"] = vector<int>();
    cout << a["zec"].size() << endl;
    c["zec"] = vector<int>({0, 1, 2, 3, 4});
    cout << a["yxc"].[2] << endl; // 3
    a.insert({"lzy", {0, 1, 2, 3, 4}}); // 插入一个二元组
    
    // size/empty/clear/begin/end 均与set类似
    // insert/erase 与set类似,但其参数均为pair<key_type, <value_type>。
    // a.find(x) 在变量名为h的map中查找key为x的二元组
}

pair

pair就是一个二元组

int main() {
	pair<int, string> a, b;
	a = make_pairJ(4, "abc");
    
    if (a == b) {}; // 可进行比较,如 < > <= >= != 等,pair自带比较运算。比较时是双关键字比较,先比较first,再比较second。
    
    cout << a.first; // 4
    cout << a.second; // abc
}

unordered_set

unordered_set 和 unorder_multiset

底层是用哈希表实现的

用法和set一样,除了没有 lower_bound 和 upper_bound 。其他相同的用法,unordered_set 的操作的时间复杂度均为o(1)。

缺点:不支持二分

#include <unordered_set>
...
int main() {
   unordered_set<int> a;
   unordered_multiset<int> b; // 可以存储重复的元素
}

unorder_map

#include <unorder_map>
...
int main() {
    unorder_map<int,int> a;
	// unorder_multimap 不常用
}

bitset

用来进行位运算的容器

#include <bitset>
...
int main() {
	bitset<1000> a; // 定义的是长度
	a[0] = 1;
	a[1] = 1;
    a.set(23); // 把第23位设置成1
    a.reset(23); // 把第23位设置成0
	cout << a.count() << endl;
}

位运算

/*
  (AND)与 &

  (OR)或 |

  (NOT)取反 ~

  (XOR)异或 ^ 其运算可以看成不进位的加法

  进行位运算时,每一位分别进行位运算即可。
  
  << 左移k位等价于乘以2的k次方 
  >> 右移k位等价于除以2的k次方
*/

常用操作

x >> k & 1; // 求x的第k位数字(从右往左,第0位开始,右移k位,把该数二进制下的这个要求的位的数左移到最低位,即可和1(0...0...01)最低位的1进行比较,与出来的结果就是要求的位上的数(由于1除最低外全都是0,因此对应比较的位也都是0,因此不用管,就比较最低位就行了,因此要把要求的位移到最低位)。
/*
  如7的二进制是1011,1的二进制是0...0...01
  
  不左移,求该数二进制下的第0位数字,1011 & 0001 ,后三位101每一位和000每一位分别进行与运算,必定都是0,再看第0位1和1进行与运算,为1,则求出第0位数字为1。
  
  左移1位,为0101,与0001进行与运算,由于0001后三位都是0,和0101进行与运算,后三位也必定都为0,而此时的第0位1(原来的第一位)和0001的第0位1进行与运算,得到1(也就是所要求的位上的值,因为后面几位和1的后几位0与运算必定为0,然后让要求的这位左移到第0位,则可以和1与,与运算的结果为1。若这位为0,则和1与运算的结果为0,若为1,则和1与运算的结果与1,因此能实现。
*/

// -x 取反再加1:~x + 1(0...0...01)
lowbit(x) = x & -x; //返回x的最后一位1(从左往右)
// 如:10010 则会得到00010

算法库algorithm

1.reverse

reverse(a.begin(), a.end()); // 反转一个vector
reverse(a + 1, a + 1 + n);

2.unique

判重,把不重复和重复了但不多余的数(如三个1中的一个1)放到end前。

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

#include <iostream>
#include <algorithm>
#include <vector>
int main {
    int a[] = {1, 1, 2, 2, 3, 3, 4};
	int m = unique(a, a + 7) - a; // 闭 开
	cout << m;
    
    for (int i = 0 ; i < m; i++) {
        cout << a[i] << ' '; // 1 2 3 4
    }
    
    vector<int> b({1, 1, 2, 2, 3, 3, 4});
    // int n = unique(a.begin(), a.end()) - b.end();
	a.erase(unique(a.begin(), a.end()), a.end()); // 效果和上面一样,erase的区间(左闭右开)为unique返回的尾迭代器和原来a的end,则实现了删除了重复且多余的数。
    for (auto x : a) cout << x << ' ' << endl; // 1 2 3 4
}


3.random_shuffle

4.sort

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
	vector<int> a({1, 2, 3, 4, 5});
    sort(a.begin(), a.end()); // 升序
    sort(a.begin(), a.end(), greater<int>); // 降序
    
    // 第三个参数还可以自定义
    bool cmp(int a, int b) { // 若a>b成立,则a排在b前面。(a是否应该排在b前面)
        return a > b;
    }
    sort(a.begin(), a.end(), cmp);
    
    //还可以给结构体排序
}

5.lower_bound/upper_bound

6.next_permutation

可以求出比当前数组排列大的下一个排列

#include <algorithm>
...
int main() {
    vector<int> nums;
    next_permutation(nums.begin(), nums.end());
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值