13届蓝桥杯c++组日常练习问题记录

13届蓝桥杯c++组日常练习问题记录

以下内容皆收集于相关博客,如有侵权请联系我删除!

一、常用函数

1、C++ STL中 next_permutation函数的用法

1.1 输出序列{1,2,3,4}字典序的全排列。

#include <iostream>
#include<algorithm>
using namespace std;
 
int main(int argc, char** argv) {
	int a[4]={1,2,3,4};
	sort(a,a+4);
	do{
		//cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<" "<<a[3]<<endl;
		for(int i=0;i<4;i++)
		    cout<<a[i]<<" ";
		cout<<endl;
	}while(next_permutation(a,a+4));
	return 0;

1.2、排列组合数公式

image-20211208225242957

image-20211208225252107

1.3 、C++ 计算组合数(动态规划)

image-20220406222916360

int c_m_n(int m,int n) {
	vector<vector<int> >dp(m+1, vector<int>(n+1,0));
	for (int j = 0; j <= n;++j) {
		dp[j][j] = 1;
		for (int i = j + 1;i <= m;++i) {
			if (j == 0)
				dp[i][j] = 1;
			else
				dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
		}
	}
	return dp[m][n];
}

2、C++ 保留两位小数(setprecision(n)的一些用法总结)

#include <iomanip>  //不要忘了头文件
//第一种写法
cout<<setiosflags(ios::fixed)<<setprecision(2);
	
//第二种写法
cout.setf(ios::fixed);
cout<<setprecision(2);

//第三种写法
cout<<fixed<<setprecision(2);

上面的语句写一次就行了,对之后的数字都有效。

3、sort函数

1.sort函数包含在头文件为#include< algorithm>的c++标准库中,调用标准库里的排序方法可以实现对数据的排序,但是sort函数是如何实现的,我们不用考虑!

2.sort函数的模板有三个参数:

void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);

(1)第一个参数first:是要排序的数组的起始地址。

(2)第二个参数last:是结束的地址(最后一个数据的后一个数据的地址)

(3)第三个参数comp是排序的方法:可以是从升序也可是降序。如果第三个参数不写,则默认的排序方法是从小到大排序。

class Solution {
public:
    int eraseOverlapIntervals(vector<vector<int>>& intervals) {
        if (intervals.empty()) {
		    return 0;
	    }
        
        sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) {
            return a[1] < b[1];
        });
        int size = intervals.size();
        int ans = 0;
        int pre = intervals[0][1];
        for (int i = 1;i < size;++i) {
            if (intervals[i][0] < pre) {
                ++ans;
            }
            else {
                pre = intervals[i][1];
            }
	    }
        return ans;
    }
};

4、C++的STL中accumulate的用法

4.1 累加求和

accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型。

#include<numeric>
int sum = accumulate(vec.begin() , vec.end() , 42);
4.2 连接字符串

可以使用accumulate把string型的vector容器中的元素连接起来:
string sum = accumulate(v.begin() , v.end() , string(" "));
这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。

5、getline()

二.对于string类
方法一:getline(cin, str)

这说明这里的getline不是类方法。

在这里要注意的是:当 getline(cin, str);前面的输入是cin>>ss;的话,那么此处str的值时空的,因为他会读取上一行的结束符。

 1 #include <iostream>
 2 #include <string>
 3 using namespace std;
 4  
 5 int main()
 6 {
 7     string str;
 8     getline(cin, str);
 9     cout << str << endl;
10     return 0;
11 }

6、stringstream用于数据类型转换

stringtream最常用于string与各种内置数据类型的转换。
示例代码如下:

#include <iostream>
#include <sstream>
#include <string>
#include<cstdlib>
using namespace std;

int main()
{
	stringstream sstr;
	//--------int转string-----------
	int a=100;
	string str;
	sstr<<a;
	sstr>>str;
	cout<<str<<endl;
	//--------string转char[]--------
	sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,请注意在每一次转换之后都必须调用clear()成员函数。
	string name = "colinguan";
	char cname[200];
	sstr<<name;
	sstr>>cname;
	cout<<cname;
	system("pause");
}
123456789101112131415161718192021222324

string转double/int:

#include <iostream>
#include <sstream>
 
using namespace std;
 
int main()  
{  
    double  dVal;    
    int     iVal;
    string  str;
    stringstream ss;
    
    // string -> double
    str = "123.456789";  
    ss << str;
    ss >> dVal;
    cout << "dVal: " << dVal << endl;
 
    // string -> int
    str = "654321";  
    ss.clear();
    ss << str;
    ss >> iVal;
    cout << "iVal: " << iVal << endl;  
        
    return 0;  
}  

7、substr函数

原型:string substr ( size_t pos = 0, size_t n = npos ) const;
功能:获得子字符串。
参数说明:pos为起始位置(默认为0),n为结束位置(默认为npos)

S.substr(ans_l,ans)从ans_l开始的ans个字符串

返回值:子字符串

#include <iostream>
#include <string>
#include <vector>



//字符串分割函数
std::vector<std::string> split(std::string str, std::string pattern)
{
    std::string::size_type pos;
    std::vector<std::string> result;
    str += pattern;//扩展字符串以方便操作
    int size = str.size();
    for (int i = 0; i < size; i++)
    {
        pos = str.find(pattern, i);
        if (pos < size)
        {
            std::string s = str.substr(i, pos - i);
            result.push_back(s);
            i = pos + pattern.size() - 1;
        }
    }
    return result;
}

8、insert函数()

string &insert(int p0, const char *s);——在p0位置插入字符串s

string &insert(int p0, const char *s, int n);——在p0位置插入字符串s的前n个字符

string &insert(int p0,const string &s);——在p0位置插入字符串s

string &insert(int p0,const string &s, int pos, int n);——在p0位置插入字符串s从pos开始的连续n个字符

string &insert(int p0, int n, char c);//在p0处插入n个字符c

iterator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置

void insert(iterator it, const_iterator first, const_iteratorlast);//在it处插入从first开始至last-1的所有字符

void insert(iterator it, int n, char c);//在it处插入n个字符c

8、对数函数 log() 操作

首先要知道exp()函数

exp(n)值为e^n次方;

另外log函数包括两种函数 一种以e为低的log()函数

另一种为以10为底的log 10()函数;

具体用法见下面这个小程序

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	double a=9,b=10;
	cout<<log(a)<<endl;
	cout<<log(exp(a))<<endl;
	cout<<log10(b)<<endl;
	return 0;
}

另外如果自定义以m为底,求log n的值

需要double a=log(n)/log(m);

举例如下:

#include<iostream>
#include<cmath>
using namespace std;
int main()
{
	double a=2,b=2;//以2为底的对数函数 
	for(b=2;b<=16;b=b+2)
	{
	       cout<<"b="<<b<<"时,以2为底的对数函数="<<log(b)/log(a)<<endl;
	}
	return 0;
}

### 

9、如何检查浮点数是否为整数

Number 1: 向下向上取整法

int main() {
    float num1 = 10.0f;
    float num2 = 2.0f;
    double res = num1 / num2;
    if(ceil(res) == floor(res))							
        std::cout << "整数" << std::endl;
    else
        std::cout << "小数" << std::endl;
    return 0;
}
头文件 <cmath>

ceil():Computes the smallest integer value not less than arg. —— 向上取整

floor() : Computes the largest integer value not greater than arg. —— 向下取整

向上,向下取整后相同,则为整数

10、partial_sum 的使用

partial_sum 是C++ STL 算法组件中的其中一个算法,其作用是计算某个序列局部元素的和。它有四个重载函数。要使用 partial_sum 需要引用头文件 numeric。
/ 局部总和 第一个重载
	//partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置)
	partial_sum(vec.begin(), vec.end(), arr);
	cout << "1 arr : \n"; 
	for (int i = 0; i < 10; i++) { cout << arr[i] << '\t'; }//输出
	cout << endl << endl;
	
	//第二个重载
	// partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置,自定义函数)
	partial_sum(vec.begin(), vec.end(), arr, func);
	cout << "2 arr : \n";
	for (int i = 0; i < 10; i++) { cout << arr[i] << '\t'; }//输出
	cout << endl << endl;

	//第三个重载
	//partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置)
	partial_sum(vec.begin(), vec.end(), vec2.begin());
	cout << "3 vec2 : \n";
	for (int i = 0; i < 10; i++) { cout << vec2[i] << '\t'; }//输出
	cout << endl << endl;

	//第四个重载
	// partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置,自定义函数)
	partial_sum(vec.begin(), vec.end(), vec2.begin(), func);
	cout << "4 vec2 : \n";
	for (int i = 0; i < 10; i++) { cout << vec2[i] << '\t'; }//输出
	cout << endl<< endl;
————————————————
版权声明:本文为CSDN博主「思不凉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44566320/article/details/91355171

11、关于lower_bound( )和upper_bound( )的常见用法

lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。

lower_bound() 函数定义在头文件中l

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

11、c++中二进制和整数转化

#1,包含文件

#include< bitset >

#2,整数转化成二进制

int a = 63;
bitset<6> bs(a);

#3,二进制转化成整数

int b = bs.to_ullong();

#4,string转bitset

*// 推荐写法* std::bitset<6>(std::string("110101")); *// 而不是* std::bitset<6>("110101");

12、string类型反转

Using inbuilt “reverse” function:

There is a direct function in “algorithm” header file for doing reverse that saves our time when programming.

// A quickly written program for reversing a string 
// using reverse() 
#include <bits/stdc++.h> 
using namespace std; 
int main() 
{ 
	string str = "geeksforgeeks"; 

	// Reverse str[begin..end] 
	reverse(str.begin(), str.end()); 
	
	cout << str; 
	return 0; 

} 

13、 c++二分查找

1.头文件
#include <algorithm>
2.使用方法

1.binary_search:查找某个元素是否出现。

a.函数模板:binary_search(arr[],arr[]+size , indx)

b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值

c.函数功能: 在数组中以二分法检索的方式查找,若在数组(要求数组元素非递减)中查找到indx元素则真,若查找不到则返回值为假。

2.lower_bound:查找第一个大于或等于某个元素的位置。
a.函数模板:lower_bound(arr[],arr[]+size , indx):
b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
c.函数功能: 函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置(注意是地址)。如果所有元素都小于val,则返回last的位置
d.举例如下:
  一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标,则
  /注意因为返回值是一个指针,所以减去数组的指针就是int变量了/
  pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。
  pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。
  pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。
e.注意:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!

返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置

3.upper_bound:查找第一个大于某个元素的位置。
a.函数模板:upper_bound(arr[],arr[]+size , indx):
b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
c.函数功能:函数upper_bound()返回的在前闭后开区间查找的关键字的上界,返回大于val的第一个元素位置
  例如:一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:数组下标越界)
  返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置 。

14、C++ cout格式化输出(输出格式)

image-20220327144406512

关于流操纵算子的使用,来看下面的程序。

#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
    int n = 141;
    //1) 分别以十六进制、十进制、八进制先后输出 n
    cout << "1)" << hex << n << " " << dec << n << " " << oct << n << endl;
    double x = 1234567.89, y = 12.34567;
    //2)保留5位有效数字
    cout << "2)" << setprecision(5) << x << " " << y << " " << endl;
    //3)保留小数点后面5位
    cout << "3)" << fixed << setprecision(5) << x << " " << y << endl;
    //4)科学计数法输出,且保留小数点后面5位
    cout << "4)" << scientific << setprecision(5) << x << " " << y << endl;
    //5)非负数显示正号,输出宽度为12字符,宽度不足则用 * 填补
    cout << "5)" << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
    //6)非负数不显示正号,输出宽度为12字符,宽度不足则右边用填充字符填充
    cout << "6)" << noshowpos << setw(12) << left << 12.1 << endl;
    //7)输出宽度为 12 字符,宽度不足则左边用填充字符填充
    cout << "7)" << setw(12) << right << 12.1 << endl;
    //8)宽度不足时,负号和数值分列左右,中间用填充字符填充
    cout << "8)" << setw(12) << internal << -12.1 << endl;
    cout << "9)" << 12.1 << endl;
    return 0;
}

程序的输出结果是:
1)8d 141 215
2)1.2346e+06 12.346
3)1234567.89000 12.34567
4)1.23457e+06 1.23457e+01
5)+12.10000
6)12.10000
*
7)****12.10000
8)-***12.10000
9)12.10000

15、C++中将string按照空白字符分割的新方法

首先要引入头文件,C++标准库中的提供了比ANSI C的<stdio.h>更高级的一些功能,即单纯性、类型安全和可扩展性。
  库是最近才被列入C++标准的。(不要把与标准发布前被删掉的弄混了。)因此,老一点的编译器,如GCC2.95,并不支持它。如果你恰好正在使用这样的编译器而又想使用的话,就要先对它进行升级更新。
  库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作。
  注意,使用string对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。
————————————————
版权声明:本文为CSDN博主「猫小时候」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/onever_say_love/article/details/49123935

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

int main(){
    //用于存放分割后的字符串 
    vector<string> res;
    //待分割的字符串,含有很多空格 
    string word="   Hello, I want   to learn C++!   ";
    //暂存从word中读取的字符串 
    string result;
    //将字符串读到input中 
    stringstream input(word);
    //依次输出到result中,并存入res中 
    while(input>>result)
        res.push_back(result);
    //输出res 
    for(int i=0;i<res.size();i++){
        cout<<res[i]<<endl;
    }
    return 0;
}

二、常用方法和数据结构

1、 C++ 输入一行个数未知的整数

这种情况容易在在线笔试中遇到:输入一行整数,个数未知,整数之间用空格间隔,除了字符串分割提取外,可以采用如下简便方式:

int main() {
    vector<int> inputs;
    int tmp;  
    cin >> tmp;
    inputs.push_back(tmp);
    while (cin.get() != '\n') { 
        cin >> tmp; 
        inputs.push_back(tmp);
    }   
    return 0;
}

2、初始化链表

#include<iostream>
using namespace std;

class ListNode {
public:
	int val;
	ListNode* next = NULL;
};

int main() {
	int num[4] = { 3,2,0,-4 };
	//初始化链表
	ListNode* head = new ListNode();
	ListNode* p1 = new ListNode();
	ListNode* p2 = new ListNode();
	for (int i = 0;i < 4;++i) {
		p1->val = num[i];
		p1->next = p2;
		if (i == 0) head = p1;
		p1 = p2;
		p2 = new ListNode();
	}
	ListNode* p = head;
	while (p->next != NULL) {
		cout << p->val << " ";
		p = p->next;
	}
}

3 、Vector创建和初始化数组的方法:

3.1 一维vector
3.1.1 创建一维vector:
vector<int> nums;//不指定长度vector<int> nums(n); // 指定长度为n 
添加元素

nums.push_back(1);//直接从数组末端添加nums[i] = 1;//直接赋值给第i个位置
删除元素

nums.resize(nums.size-i); //直接将数组长度减小,某种方式上删掉了后面i个nums.pop_back();//删掉最后一个元素
3.1.2 数组遍历
for(int i = 0; i < nums.size(); i++){    cout<<nums[i]<<endl;}
3.1.3 其他

获得长度:nums.size()
排序(O(nlogn)):sort(nums.begin(),nums.end());
翻转:reverse(nums.begin(), nums.end());
合并两个vector:合并nums1和nums2,并将合并后的数组赋值给nums

vector<int> nums1(m),nums2(n);
vector<int> nums;
nums.resize(m+n);
merge(nums1.begin(), nums1.end(),nums2.begin(),nums2.end(),nums);
3.2 二维vector
3.2.1 创建m*n的二维vector: 直接定义
vector<vector <int> > nums(m ,vector<int>(n));    //m*n的二维vector

定义了一个vector容器,元素类型为vector<int>,初始化为包含m个vector<int>对象,每个对象都是一个新创立的vector<int>对象的拷贝,而这个新创立的vector<int>对象被初始化为包含n个0。vector<int>(n); 表示构造一个无名且含n个0的vector<int>对象。

3.2.2 动态创建m*n的二维vector

方法一:

vector<vector <int> > nums;nums.resize(m);for(int i=0;i<m;i++) nums[i].resize(n);

方法二:

vector<vector <int> > nums;

nums.resize(m,vector<(n));

方法三:

vector v[maxn]; //备注:node 是结构体; maxn 是v数组里元素的个数
3.2.3 初始化二维数组
vector<vector <int> > nums(m ,vector<int>(n,0));    //m*n的二维vector,所有元素为0

获得二维数组的行数:nums.size();
获得二维数组的列数:nums[0].size()

3.2.4 数组遍历
int m = nums.size(),n = nums[0].size();
for(int i = 0; i < m; i++)
{    
    for(int j = 0; j < n; j++)
    {        
    cout<<nums[i][j]<<endl;
    }
}

4、队列queue

queue 的生成方式和 stack 相同,下面展示如何创建一个保存字符串对象的 queue:

std::queue<std::string> words;

也可以使用拷贝构造函数:

std::queue<std::string> copy_words {words}; // A duplicate of words

stack、queue 这类适配器类都默认封装了一个 deque 容器,也可以通过指定第二个模板类型参数来使用其他类型的容器:

std::queue<std::string, std::list<std::string>>words;

底层容器必须提供这些操作:front()、back()、push_back()、pop_front()、empty() 和 size()。

4.1 queue 操作

queue 和 stack 有一些成员函数相似,但在一些情况下,工作方式有些不同:

  • front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
  • push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
  • push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
  • pop():删除 queue 中的第一个元素。
  • size():返回 queue 中元素的个数。
  • empty():如果 queue 中没有元素的话,返回 true。
  • emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
  • swap(queue &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。

queue 模板定义了拷贝和移动版的 operator=(),对于所保存元素类型相同的 queue 对象,它们有一整套的比较运算符,这些运算符的工作方式和 stack 容器相同。

5、unordered_set 和unordered_map(c++11特性)

5.1 unordered_set

unordered_set可以把它想象成一个集合,它提供了几个函数让我们可以增删查:
unordered_set::insert
unordered_set::find
unordered_set::erase

这个unorder暗示着,这两个头文件中类的底层实现----Hash。 也是因为如此,你才可以在声明这些unordered模版类的时候,传入一个自定义的哈希函数,准确的说是哈希函数子(hash function object)。

单向迭代器

哈希表的实现复杂了该容器上的双向遍历,似乎没有一种合适的方法能够做到高效快速。 因此,unorder版本的map和set只提供前向迭代器(非unorder版本提供双向迭代器)。

img

首先要include这个unordered_set头文件。
然后就是第六行我们定义了一个整型int的集合,叫myset。
后面几行,我们演示了insert/find/erase的用法。
有两点需要注意:
一是这个容器是个集合,所以重复插入相同的值是没有效果的。大家可以看到我们这里第7行和第9行插入了2次3,实际上这个集合里也只有1个3,第10行输出的结果是2。
二是find的返回值是一个迭代器(iterator),如果找到了会返回指向目标元素的迭代器,没找到会返回end()。

5.2 unordered_map

unordered_map同样也提供了增删查函数:
unordered_map::insert
unordered_map::find
unordered_map::erase
这三个函数的平均时间复杂度也是O(1)的。我们可以看一个例子:

img

首先我们看第2行,要用unordered_map你要先include相应的头文件。
在7行我们定义了一个mymap,它的key是string类型,字符串;value是整形。
第8第9行展示的insert插入,因为我们这里要插入的是一个key/value pair(键值对),我们要用make_pair函数把一个字符串和一个整数打包成一个pair。
第10行是find查找,find返回的也是一个迭代器,iterator。这里我们懒得写很长的迭代器类型,直接用的auto。auto是c++11标准里的关键字,它会自动推断变量的类型。如果你的编译器不支持c++11,那你还是要老老实实写全:unordered_map<string, int>::iterator

迭代器指向的是一个pair,所以第11行我们可以用first和second去拿到对应的key和value,这里first是”c++”这个字符串,second是100这个整数。
第13第14行展示了一下erase删除。

值得一提的是,unordered_map重载了[]运算符,我们可以把key放在中括号里,像操作数组一样操作unordered_map:

img

上面这个程序的输出结果是101。大家可以看一下第8~第10行。我们把”c++”这个key放在中括号里就能直接操作”c++”对应的值。这种写法会让程序更直观。

6、基于范围的for循环(C++11)

对于一个有范围的集合如数组,本来就有范围,我们在使用循环而且确定循环范围就是多余的,C++11引入了范围for循环;语法是for循环后面括号内部被“:”分成两部分,前面一部分是auto& +迭代的变量,后一部分是迭代的范围;如对数组每个元素乘2并打印;

int arr[] = { 1, 2, 3, 4 };
	for (auto& a : arr)
		a *= 2;
	for (auto& a : arr)
		cout << a << " ";

7、c++ 栈 stack 用法

#include <stack> 
stack<int>s1;
stack<string>s2;

empty() 堆栈为空则返回真
pop() 移除栈顶元素 (删除)
push() 在栈顶增加元素 (增加)
size() 返回栈中元素数目
top() 返回栈顶元素,不删除(获取)

8、c++ map

使用map得包含map类所在的头文件

#include <map> //注意,STL头文件没有扩展名.h
8.1 map的构造函数

map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map:

map<int, string> mapStudent;

8.2 插入元素
// 定义一个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";
8.3 判断map中key值是否存在

1.find函数
iterator find ( const key_type& key );
如果key存在,则find返回key对应的迭代器,如果key不存在,则find返回尾后迭代器 .end()。可以参考下面的示例来判断key值是否存在

if (mymap.find(key) == mymap.end())
  cout << "没有这个key" << endl;

2.count函数
count函数用于统计key值在map中出现的次数,map的key不允许重复,因此如果key存在返回1,不存在返回0

if (mymap.count(key) == 0)
  cout << "no this key" << endl;
8.4 map元素的默认值

当map内元素值为int类型或常量时,默认值为0

当为String类型时,默认值不明,不显示。

9、c++优先队列(priority_queue)用法详解

头文件#include <queue>

优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的

和队列基本操作相同:

top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆

//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;

//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)

//对于基础类型 默认是大顶堆
priority_queue<int> a; 
//等同于 priority_queue<int, vector<int>, less<int> > a;
     
priority_queue<int, vector<int>, greater<int> > c;  //这样就是小顶堆
priority_queue<string> b;

10、C++ deque的用法与示例

deque 容器和 vector 容器最大的差异,一在于 deque 允许使用常数项时间对头端进行元素的插入和删除操作。二在于 deque 没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,换句话说,像 vector 那样,”旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在 deque 身上是不会发生的。也因此,deque 没有必须要提供所谓的空间保留(reserve)功能。
1.1deque构造函数

deque<T> deqT;//默认构造形式

deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。

deque(n, elem);//构造函数将n个elem拷贝给本身。

deque(const deque &deq);//拷贝构造函数。

1.2 deque赋值操作

assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。

assign(n, elem);//将n个elem拷贝赋值给本身。

deque& operator=(const deque &deq); //重载等号操作符

swap(deq);// 将deq与本身的元素互换

1.3 deque大小操作

deque.size();//返回容器中元素的个数

deque.empty();//判断容器是否为空

deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。

deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。

————————————————
版权声明:本文为CSDN博主「JT同学」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42462202/article/details/87537503

11、C++ pair的基本用法总结

1,pair的应用

pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。

其标准库类型–pair类型定义在#include 头文件中,定义如下:

类模板:template<class T1,class T2> struct pair

参数:T1是第一个值的数据类型,T2是第二个值的数据类型。

功能:pair将一对值(T1和T2)组合成一个值,

    这一对值可以具有不同的数据类型(T1和T2),

    两个值可以分别用pair的两个公有函数first和second访问。

定义(构造函数):

pair<T1, T2> p1; //创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。
pair<T1, T2> p1(v1, v2); //创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。
make_pair(v1, v2); // 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。
p1 < p2; // 两个pair对象间的小于运算,其定义遵循字典次序:如 p1.first < p2.first 或者 !(p2.first < p1.first) && (p1.second < p2.second) 则返回true。
p1 == p2; // 如果两个对象的first和second依次相等,则这两个对象相等;该运算使用元素的==操作符。
p1.first; // 返回对象p1中名为first的公有数据成员
p1.second; // 返回对象p1中名为second的公有数据成员
————————————————
版权声明:本文为CSDN博主「sevencheng798」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/sevenjoin/article/details/81937695

12、c++中memset的用法

头文件:

#include<string.h>//C语言
#include<cstring>//c++
memset()函数原型是extern void *memset(void *buffer, int c, int count)

buffer:为指针或是数组
c:是赋给buffer的值
count:赋值buffer中的位数

1. memset是以字节为单位,初始化内存块。

当初始化一个字节单位的数组时,可以用memset把每个数组单元初始化成任何你想要的值,比如,

char data[10];
memset(data, 1, sizeof(data)); // right
memset(data, 0, sizeof(data)); // right

而在初始化其他基础类型时,则需要注意,比如,

int data[10];
memset(data, 0, sizeof(data)); // right
memset(data, -1, sizeof(data)); // right
memset(data, 1, sizeof(data)); // wrong, data[x] would be 0x0101 instead of 1

例如:

int dp[50][50][15][15];
memset(dp, -1, sizeof(dp));
//将dp数组所有值赋为-1

13、位运算

常用技巧

位运算是算法题里比较特殊的一种类型,它们利用二进制位运算的特性进行一些奇妙的优化和计算。常用的位运算符号包括:“∧”按位异或、“&”按位与、“|”按位或、“∼”取反、“<<”算术左移和“>>”算术右移。以下是一些常见的位运算特性,其中 0s 和 1s 分别表示只由 0 或 1

构成的二进制数字。

x ^ 0s = x 		x & 0s = 0 		x | 0s = x

x ^ 1s = ~x	 	x & 1s = x 		x | 1s = 1s

x ^ x = 0 		x & x = x 		x | x = x

除此之外,n & (n - 1) 可以去除 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,减去 1 得到 11110011,这两个数按位与得到 11110000。n & (-n) 可以得到 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,取负得到 00001100,这两个数按位与得到 00000100。还有更多的并不常用的技巧,若读者感兴趣可以自行研究,这里不再赘述。

14、快速幂

快速幂算法的核心思想就是每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。

让我们先来看一个简单的例子:

3^10=333333333*3

3^10=(33)(33)(33)(33)(3*3)

310=(3*3)5

310=95

95=(94)*(9^1)

95=(65611)*(9^1)

非递归版:

ll fastM(ll a, ll x, ll n) {
	ll sum = 1;
	while (x) {
		if (x & 1)sum = sum * a % n;
		a = a * a % n;
		x >>= 1;
	}
	return sum;
}

三、常用技巧

1、上下左右

int direct[5] = {-1,0,1,0,-1};
for(int i = 0;i<4;++i){
    x = row + direct[i];
    y = col + direct[i+1];
}

2、log函数的一点小问题

为了避免log函数精度问题,在使用对数函数时,尽量使用*log10*

3、2 的(整数)次方

首先我们考虑一个数字是不是 2 的(整数)次方:如果一个数字 n 是 2 的整数次方,那么它

的二进制一定是 0…010…0 这样的形式;考虑到 n − 1 的二进制是 0…001…1,这两个数求按位与

的结果一定是 0。因此如果 n & (n - 1) 为 0,那么这个数是 2 的次方。

四、基本数学知识

1、 公倍数与公因数

1.1 欧几里得(辗转相除法)

利用辗转相除法,我们可以很方便地求得两个数的最大公因数(greatest common divisor,gcd);

将两个数相乘再除以最大公因数即可得到最小公倍数(least common multiple, lcm)。

int gcd(int a, int b) {

	return b == 0 ? a : gcd(b, a% b);

}

int lcm(int a, int b) {

	return a * b / gcd(a, b);

}
1.2 拓展欧几里得

进一步地,我们也可以通过扩展欧几里得算法(extended gcd)在求得 a 和 b 最大公因数的同

时,也得到它们的系数 x 和 y,从而使 ax + by = gcd(a, b)。

int xGCD(int a, int b, int &x, int &y) {
    if (!b) {
        x = 1, y = 0;
        return a;
    }
    int x1, y1, gcd = xGCD(b, a % b, x1, y1);
    x = y1, y = x1 - (a / b) * y1;
    return gcd;
}
1.3 扩展欧几里得中求最小非负x的方法的推导
int D=b/gcd(a,b)
x%=D;
if(x<0){
    x+=D;
}
//或者x=(x%D+D)%D

2、质数

质数又称素数,指的是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然

数。值得注意的是,每一个数都可以分解成质数的乘积。

2.1 分解质因数
for(int i=2; i<=m; i++)
    {
        while(m%i==0)
        {
            m=m/i;
            cout<<i<<"*";
        }
    }
2.2 判断素数
bool prime(int n){
    if(n < 2)return 0;
    for(int i = 2;i<=sqrt(n);++i){
        if(n % i == 0)return 0;
    }
    return 1;
}
  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值