[温习C/C++]0x04 刷题基础编码技巧

系列文章目录

[温习C/C++]0x00-STL标准模板库概述
[温习C/C++]0x01-STL泛型算法-持续更新长文版
[温习C/C++]0x03-sort排序
[温习C/C++]0x04 刷题基础编码技巧

C/C++刷题基础编码技巧

更新日志

日期变更内容
2024-10-13完成C/C++刷题基础编码初稿
2024-10-14补充map相关编码技巧内容
2024-10-18补充16进制、10进制、2进制转换相关编码技巧内容
2024-10-21补充STL stack和queue章节内容
2024-11-09补充迭代器章节和array章节内容
2025-01-15更新priority_queue自定义排序

高频考点

  1. 双指针、滑动窗口
  2. 二分法
  3. 树、二叉树
  4. DFS、BFS
  5. 自定义排序、优先队列
  6. STL灵活运用(包括vector、set、map、queue、array等)

万能头文件

刷题只引入下面头文件就足够了,其他头文件不需要引入了。

#include <bits/stdc++.h>

字符串分割

【C++标准库】std::getline

头文件

#include <sstream>
#include <string>

函数原型

input - the stream to get data from
str - the string to put the data into
delim - the delimiter character

  • 官方例子
#include <iostream>
#include <sstream>
#include <string>
 
int main()
{
    // greet the user
    std::string name;
    std::cout << "What is your name? ";
    std::getline(std::cin, name);
    std::cout << "Hello " << name << ", nice to meet you.\n";
 
    // read file line by line
    std::istringstream input;
    input.str("1\n2\n3\n4\n5\n6\n7\n");      
    int sum = 0;
    for (std::string line; std::getline(input, line);)  //  geline两个参数,默认采用\n换行符分割字符串
        sum += std::stoi(line);
    std::cout << "\nThe sum is " << sum << ".\n\n";
 
    // use separator to read parts of the line
    std::istringstream input2;
    input2.str("a;b;c;d");
    for (std::string line; std::getline(input2, line, ';');)
        std::cout << line << '\n';
}

使用std::getline分割字符串

  • 本例按照' '空格作为字符串分隔符,并将分割结果赋值给temp
    std::string str("Hello World Let's Do Someting Fun");
    std::stringstream ss(str);
    std::string temp;
    const char cc = ' ';
    while (std::getline(ss, temp, cc)) {
        std::cout << " " << temp << std::endl;
    }

Output:
 Hello
 World
 Let's
 Do
 Someting
 Fun

【C标准库】strtok_s

头文件

#include <string.h>

函数原型

char *__cdecl strtok_s(char *_String, const char *_Delimiter, char **_Context)

使用strtok_s分割字符串

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
    char str[] = "A bird came down the walk";
    const char *delim = " ";
    char *next_token;
    char* token = NULL;

    printf("Parsing the input string '%s'\n", str);
    token = strtok_s(str, delim, &next_token);
    while(token) {
        printf("%s\n", token);
        token = strtok_s(NULL, delim, &next_token);
    }
    return 0;
}

Output:
Parsing the input string 'A bird came down the walk'
A
bird
came
down
the
walk

字符串截取子串

头文件

#include <string>

举个例子

std::string s = "134567";  
std::string str1 = s.substr(0, 1);  
std::cout << str1 << std::endl;  
  
std::string str2 = s.substr(0, 2);  
std::cout << str2 << std::endl;  
  
std::string str3 = s.substr(0, s.length());  
std::cout << str3 << std::endl;  
  
int val3 = stoi(str3, nullptr, 10);  // 子串转整数 [非标准库]
std::cout << val3 << std::endl;  
  
int val3_2 = atoi(str3.c_str());  // 子串转整数 [标准库]
std::cout << val3_2 << std::endl;

Output:
1
13
134567
134567
134567

字符串、二进制、十六进制、十进制之间的转换

头文件

#include <iostream>  
#include <bitset>  
#include <cstdio>  
#include <string>

进制格式化输出

#include <iostream>
#include <bitset>
#include <cstdio>

int main(int argc, char **argv) {
    int num = 10;
    printf("%d octal print: %05o\n", num, num);
    printf("%d decimal print: %03d\n", num, num);
    printf("%d hex print: %04x\n", num, num);

    std::cout <<" octal print: " << std::oct << num << std::endl;
    std::cout <<" decimal print: " << std::dec << num << std::endl;
    std::cout <<" hex print: " << std::hex << num << std::endl;
    std::cout <<" binary print: " << std::bitset<8>(num) << std::endl;
    return 0;
}

Output:
10 octal print: 00012
10 decimal print: 010
10 hex print: 000a
 octal print: 12
 decimal print: 10
 hex print: a
 binary print: 00001010

十进制和二进制互转

十进制转二进制

/* 10进制转2进制 */int a2 = 10;  
bitset<4> bit1(a2);  
cout << bit1.to_string() << endl;   // 1010  

bitset<10> bit2(a2);  
cout << bit2.to_string() << endl;   // 0000001010  

Output:
1010
0000001010 

二进制转十进制

  • 方法一
/* 2进制转10进制:第二种方法 */
string bit_str = "0101";  
int a4 = stoi(bit_str);   
cout << a4 << endl; // 101 

Output:
101
  • 方法二
/* 2进制转10进制:第二种方法 */
string bit_str = "0101";  
int a4 = stoi(bit4_str);   
cout << a4 << endl; // 101 

Output:
101

字符串和二进制相互转

二进制转字符串

bitset<10> bit("010101");
string str = bit.to_string();
cout << str << endl;
 
Output:
0000010101

字符串转二进制

string str = "010101";
bitset<10> bit(str);	
cout << bit << endl;
 
Output:
0000010101

字符串和十进制互转

字符串转十进制

/* 字符串转10进制 */
string str = "123456";  
int val = stoi(str); // 123456  
cout << val << endl; 

Output:
123456

十进制转字符串

/* 10进制转字符串 */
int num = 10;  
string val = std::to_string(num); // 10  
cout << val << endl;  

Output:
10

十六进制和十进制互转

十六进制转十进制

  • 方法一
#include <sstream>
 
int x;
stringstream ss;
ss << std::hex << "1A";  //std::oct(八进制)、std::dec(十进制)
ss >> x;
cout << x<<endl;
 
Output:
26
  • 方法二
/* 16进制字符串转10进制数 */
string str = "1A";  
int val = stoi(str, nullptr, 16);  // 26  
cout << val << endl;  
  
Output:
26

十进制转十六进制

/* 10进制转16进制 */
int num = 26;  
string str;  
stringstream ss;  
ss << std::hex << num;  // 将整数num以十六进制的形式写入到stringstream对象中
ss >> str;  
std::transform(str.begin(), str.end(), str.begin(), ::toupper);  // 1A  
cout << str << endl;  

二进制和十六进制互转

二进制转十六进制

// 2进制转16进制,先要把2进制转为10进制  
string binary = "1010";  
string hex_out;  
stringstream ss9;  
ss9 << std::hex << stoi(binary, nullptr, 2);  
ss9 >> hex_out;  
  
transform(hex_out.begin(), hex_out.end(), hex_out.begin(), ::toupper); // A  
cout << hex_out << endl;  

十六进制转二进制

/* 16进制转2进制 */
int val1 = stoi("c000", nullptr, 16);  
bitset<16> bit1(val1); // 1100000000000000  
cout << bit1.to_string() << endl;  
  
// 16进制转2进制  
string binary16_to_2 = "1B"; // 27用16进制表示为1B  
// 16进制先转10进制, 再通过bitset转2进制  
bitset<8> bit(stoi(binary16_to_2, nullptr, 16));  // 00011011  
cout << bit << endl;

字符串转整数

【C标准库】stoi

【C标准库】strtol, strtoll

Interprets an integer value in a byte string pointed to by str.

原型

long strtol( const char *str, char          **str_end, int base );
long strtol( const char *restrict str, char **restrict str_end, int base );
long long strtoll( const char *restrict str, char **restrict str_end, int base );	

base是要转化的数的进制,非法字符会赋值给str_end,str是要转化的字符。

【C标准库】atoi, atol, atoll

atoi (表示 alphanumeric to integer)是把字符串转换成整型数的一个函数。
interprets an integer value in a byte string pointed to by str. The implied radix is always 10.
采用十进制,将str字符串转换为整数。

头文件

#include <stdlib.h>
或者
#include <cstdlib>

函数原型

入参是指向以null字符结尾的字符串指针

int       atoi( const char *nptr ); //字符串转整数函数,nptr: 要转换的字符串
long      atol( const char *str );		
long long atoll( const char *str );

使用atoi完成整数进制转换

对应于成功转换进制的内容是str的整数值
如果无法执行转换,则返回0


    printf("%i\n", atoi(" -123junk"));
    printf("%i\n", atoi(" +321dust"));
    printf("%i\n", atoi("0"));
    printf("%i\n", atoi("0042")); // treated as a decimal number with leading zeros
    printf("%i\n", atoi("0x2A")); // only leading zero is converted discarding "x2A"
    printf("%i\n", atoi("junk")); // no conversion can be performed
    printf("%i\n", atoi("2147483648")); // UB: out of range of int
    printf("\"123456\" convert to interger num:%d\n",atoi("123456"));
    printf("\"-123456\" convert to interger num:%d\n",atoi("-123456"));

Output:
-123
321
0
42
0
0
-2147483648
"123456" convert to interger num:123456
"-123456" convert to interger num:-123456

字符串转整数(x进制数转化为十进制数)

x进制数转化为十进制数

头文件

#include <stdlib.h>
或者
#include <cstdlib>

函数原型

long      strtol( const char *str, char  **str_end, int base );(until C99)
long      strtol( const char *restrict str, char **restrict str_end, int base ); (since C99)
long long strtoll( const char *restrict str, char **restrict str_end, int base );

使用strtol

base是要转化的数的进制,非法字符会赋值给endptr,nptr是要转化的字符,例如:

#include<cstdio>
int main()  
{  
    char buffer[20]="20549stend#12";  
    char *stop;  
    int ans = strtol(buffer, &stop, 8);   //将八进制数2054转成十进制,后面均为非法字符
    printf("%d\n",ans);  
    printf("%s\n", stop);
    return 0;
}

Output:
1068
9stend#12

注意:

①如果base为0,且字符串不是以0x(或者0X)开头,则按十进制进行转化。
②如果base为0或者16,并且字符串以0x(或者0X)开头,那么,x(或者X)被忽略,字符串按16进制转化。
③如果base不等于0和16,并且字符串以0x(或者0X)开头,那么x被视为非法字符。
④对于nptr指向的字符串,其开头和结尾处的空格被忽视,字符串中间的空格被视为非法字符。

整数转字符串

十进制数转x进制数字符串
itoa为扩展库, 有些环境下该符号可能未定义, 可以使用 snprintf_s替代。

【非C标准库-扩展库】itoa,ltoa

【非C标准库】itoa,ltoa itoa (表示 integer to alphanumeric)是把整型数转换成字符串的一个函数

头文件

#include <stdlib.h>

函数原型

// These POSIX versions of the functions have deprecated names:
char * itoa( int value, char *buffer, int radix );
char * ltoa( long value, char *buffer, int radix );
char * ultoa( unsigned long value, char *buffer, int radix );

参数

  • value
    要转换的数字。

  • buffer
    保存转换结果的缓冲区。

  • radix
    基数,将用于转换 value,必须在2-36的范围内。

  • size
    缓冲区长度,以字符类型为单位。 此参数是从 C++ 中的 buffer 参数推断出来的。

返回值

这些函数均返回指向 buffer 的指针。 无错误返回。

参考: https://learn.microsoft.com/zh-cn/cpp/c-runtime-library/reference/itoa-itow

使用itoa转换十进制数为x进制数字符串

    int num1 = 123456;
    int num2 = 10;
    char str[16] = {0};
    itoa(num1, str, 8);
    printf("num : %d comnvert to string: %s \n", num1, str);
    itoa(num2, str,2);
    printf("num : %d comnvert to string: %s \n", num2, str);

Output:
num : 123456 comnvert to string: 361100 
num : 10 comnvert to string: 1010

【C标准库】snprintf_s格式化字符串

头文件

#include <stdio.h>

函数原型

int snprintf_s( char *restrict buffer, rsize_t bufsz,
                const char *restrict format, ... );

使用snprintf_s格式化字符串

    int num1 = 123456;
    char buffer[512] = {0};
    size_t buffer_size = sizeof(buffer);
    int str_len = _snprintf_s(buffer, buffer_size, buffer_size - 1,
                              "%5o", num1);
    if (str_len < 0) {
        return;
    }
    buffer_size -= str_len;
    printf("num : %d comnvert to string: %s \n", num1, buffer);

Output:
num : 123456 comnvert to string: 361100

处理char类型数据

判断一个字符是否是数字、大写字母、小写字母或者是特定字符,可以使用标准库中的#include <cctype>头文件提供的接口。

头文件

#include <cctype>

接口列表

isdigit(ch) : 判断字符ch是否是数字 (0-9)
isupper(ch): 判断字符ch是否是大写字母 (A-Z)
isslower(ch): 判断字符ch是否是小写字母 (a-z)
isspace(ch): 判断字符ch是否是空白字符 (空格'', 水平制表符'\t', 垂直制表符'\v', 换页符'\f', 回车符'\n',或者是字符'\n')
isalpha(ch): 判断字符ch是否是字母 (a-zA-Z, 大写或者小写)
isalnum(ch): 判断字符ch是否是字母或数字
ispunct(ch): 判断是否是标点符号 (不是空白字符,字母或数字的可打印字符)
tolower(ch): 如果字符ch是大写,则转换为对应的小写字母;否则返回 ch
toupper(ch): 如果字符ch是小写,则转换为对应的大写字母;否则发挥ch

举个例子

#include <iostream>  
#include <cctype>  
#include <string>  
  
std::string getDigit(std::string &s, int &cur)  
{  
    std::string res = "";  
    for (int i = 0; i < s.size(); ++i) {  
        if (isdigit(s[i])) {  
            res += s[i];  
        }  
    }    return res;  
}  
  
int main(int argc, char **argv) {  
    std::string s1 = "123s567";  
    std::string num_filter_str = "";  
    int cur = 0;  
    num_filter_str = getDigit(s1, cur);  
    std::cout << num_filter_str << std::endl;  
  
    char ch = 'c';  
    int ret = isalpha(ch);  
    if (ret){  
        std::cout << "isalpha ret = " << ret << "  --> " << ch << std::endl;  
    }  
  
    ch = '\v';  
    ret = isspace(ch);  
    if (ret) {  
        std::cout << "isspace ret = " << ret << "  --> " << ch << std::endl;  
    }  
  
    ch = 'A';  
    if (isupper(ch)) {  
        ch = tolower(ch);  
    }  
    std::cout << "tolower result = " << ch << std::endl;  
  
    ch = 'a';  
    if (islower(ch)) {  
        ch = toupper(ch);  
    }  
    std::cout << "toupper result = " << ch << std::endl;   
    
    return 0;  
}


Output:
123567
isalpha ret = 2  --> c
isspace ret = 8  --> 
tolower result = a
toupper result = A

Process finished with exit code 0

string常用操作

头文件

#include <string>

移除空格

string str:
str.erase(remove_if(str.begin(), str.end(), ::isspace), str.end());

字符串构造和拷贝

string s1("abc");   // 直接初始化和拷贝初始化都使用小括号
string s1(s2, pos2, [len2]);   // s1是s2从下标pos2开始len2个字符的拷贝
s1.substr(pos, len);

字符串插入

str.insert(pos, num, ch);   // pos之前插入num个ch字符
str.insert(pos, str1);      // pos之前插入字符串str1
  • 例子
std::string s = "ABC";  
s.insert(2, "77");  
std::cout << "insert:" << s << std::endl;

Output:
insert:AB77C

字符串删除部分字符

str.erase(pos, num);        // 从pos(含)位置删除num个元素
str.erase(pos);             // 从pos(含)位置删除后面所有元素
  • 例子
std::string s = "ABCDFG";  
s.erase(2, 2);  
std::cout << "erase:" << s << std::endl;

Output:
erase:ABFG

字符串查找

str1.find(args);   // 返回str1中第一次出现args的下标,未找到返回-1
str1.rfind(args);  // 返回str1中最后一次出现args的下标,未找到返回-1
str1.find_first_of(args);  // 在str1中查找args中任何一个字符第一次出现的下标
str1.find_last_of(args);
str1.find_first_not_of(args);
  • 例子
std::string s = "CDEFGAB";  
std::cout << "find:" << s.find("AB") << std::endl;  // find:5  
  
if (s.find("ZZ") == s.npos) {  
    std::cout << "not found" << std::endl;  // not found  
}

Output:
find:5
not found

字符串替换

std::string s = "ABCDE";  
// 将起始位置为1,数量为3个字符的位置,使用6677替换  
std::cout << "replace:" << s.replace(1, 3, "6677") << std::endl;

Output:
replace:A6677E

字符串反转

std::string s = "ABCDE";  
std::reverse(s.begin(), s.end());  
std::cout << "reverse:" << s << std::endl;

Output:
reverse:EDCBA

处理tuple

元组可以将不同类型的数据打包在一起,类似于容器,可以按照索引访问其中的元素。元组大小在编译时确定,不支持动态添加或移除元素。

头文件

#include <tuple>

创建元组

  • 声明初始化创建
tuple<int, float, string> data(1, 1.5, "helloword");
或者
tuple<int, float, string> data {1, 1.5, "helloword"};
  • make_tuple函数创建
tuple<int, float, string> data = make_tuple(1, 1,5, "helloword");

访问元组

元组不支持通过迭代器访问元素

  • 通过get方式
int value1 = get<0>(data);     // 获取data第一个元素
float value2 = get<1>(data);   // 获取data第二个元素
string value3 = get<2>(data);  // 获取data第三个元素

平方 & 开方 & 绝对值

头文件

#include <math.h>
或者
#include <cmath>

举个例子

#include <iostream>  
#include <math.h>  
  
int main(int argc, char **argv) {  
    // 平方  
    int a = pow(4, 2); // 4的2次方,即4*4=16  
    std::cout << a << std::endl;  
  
    // 开平方  
    int b = pow(4, 0.5); // 4的1/2次方, 也就是4开2次根等于2  
    std::cout << b << std::endl;  
    int c = sqrt(4);  
    std::cout << c << std::endl;  
      
    // 整数绝对值  
    int d = abs(-4); // -4的绝对值等于正4  
    std::cout << d << std::endl;  
  
    // 浮点数绝对值  
    double e = fabs(b - c);  
    std::cout << e << std::endl;  
    double f = fabs(b - c - 0.5);  
    std::cout << f << std::endl;  
    return 0;  
}

排序 std::sort

https://zh.cppreference.com/w/cpp/algorithm/sort

头文件

#include <algorith>

函数原型

// 默认
template< class RandomIt >
void sort( RandomIt first, RandomIt last );
// 自定义compare排序比较规则
template< class RandomIt, class Compare >  
void sort( RandomIt first, RandomIt last, Compare comp );

函数说明

std::sort 里面的Compare comp比较函数返回值为true,表示两个数位置需要交换;返回false, 表示不需要交换位置。
以非降序排序范围 [first, last) 中的元素。不保证维持相等元素的顺序。

  • 默认对迭代器[First, last)范围内的元素进行升序排序。
  • 默认版本: 元素默认使用operator< std::less进行比较。
  • 自定义compare排序比较规则版本:使用Compare compare对象进行比较。
  • std::sort是不稳定的排序,稳定排序参考 std::stable_sort

[std::stable_sort - cppreference.com](https://zh.cppreference.com/w/cpp/algorithm/stable_sort

参数说明

参数-说明
first, last-要排序的元素范围
comp-比较函数对象(即满足比较 (Compare) 概念的对象),在第一参数_小于_(即_先_ 序于)第二参数时返回 ​true。

比较函数的签名应等价于如下:

bool cmp(const Type1 &a, const Type2 &b);

虽然签名不必有 const&,函数也不能修改传递给它的对象,而且必须接受(可为 const 的)类型 Type1Type2 的值,无关乎值类别(从而不允许 Type1& ,也不允许 Type1,除非 Type1 的移动等价于复制 (C++11 起))。
类型 Type1 与 Type2 必须使得 RandomIt 类型的对象能在解引用后隐式转换到这两个类型。​

comp 可以是函数指针 或者 函数对象,什么是函数对象呢? 就是重载了operator()运算符的对象。

举个例子

#include <algorithm>
#include <array>
#include <functional>
#include <iostream>
#include <string_view>
 
int main()
{
    std::array<int, 10> s = {5, 7, 4, 2, 8, 6, 1, 9, 0, 3}; 
 
    auto print = [&s](std::string_view const rem)
    {
        for (auto a : s)
            std::cout << a << ' ';
        std::cout << ":" << rem << '\n';
    };
 
    std::sort(s.begin(), s.end());
    print("用默认的 operator< 排序");
 
    std::sort(s.begin(), s.end(), std::greater<int>());
    print("用标准库比较函数对象排序");
 
    struct
    {
        bool operator()(int a, int b) const { return a < b; }
    } customLess;
 
    std::sort(s.begin(), s.end(), customLess);
    print("用自定义函数对象排序");
 
    std::sort(s.begin(), s.end(), [](int a, int b)
                                  {
                                      return a > b; // 降序
                                  });
    print("用 lambda 表达式排序");
}



Output:
0 1 2 3 4 5 6 7 8 9 :用默认的 operator< 排序
9 8 7 6 5 4 3 2 1 0 :用标准库比较函数对象排序
0 1 2 3 4 5 6 7 8 9 :用自定义函数对象排序
9 8 7 6 5 4 3 2 1 0 :用 lambda 表达式排序

再举个例子

#include <algorithm>
#include <array>
#include <iostream>
#include <string>
#include <vector>
 
struct Employee
{
    int age;
    std::string name; // 不参与比较
};
 
bool operator<(const Employee& lhs, const Employee& rhs)
{
    return lhs.age < rhs.age;
}
 
int main()
{
    std::vector<Employee> v{{108, "Zaphod"}, {32, "Arthur"}, {108, "Ford"}};
 
    std::stable_sort(v.begin(), v.end());
 
    for (const Employee& e : v)
        std::cout << e.age << ", " << e.name << '\n';
}

Output:
32, Arthur
108, Zaphod
108, Ford

array

array容器大小是固定的,无法插入删除元素,只能进行访问和替换操作。

头文件

#include <array>

创建array容器

#include <iostream>  
#include <array>  
using namespace std;  
  
int main(int argc, char **argv) {  
    // 声明一个大小为100个的int类型数组,初始值全0  
    array<int, 100> a {};  
  
    // 声明一个大小为100个的int类型数组,初始化部分值,其余全0  
    array<int, 10> b {1, 2, 3};  
  
    return 0;  
}

array除了在定义时需要指定大小,不可以插入删除操作外,其余操作与vector一样。

vector

https://cplusplus.com/reference/vector/vector/

头文件

#include <vector>
  • 打印vector容器里元素值
void print_vector(std::vector<int>& vec)  
{  
//    for (int i = 0; i < vec.size(); ++i) {  
//        std::cout << vec[i] << " ";  
//    }  
//    std::cout << std::endl;  
  
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {  
        std::cout << *it << " ";  
    }  
    std::cout << std::endl;  
}

基本操作

vectorinserterase操作第一个参数只能接收迭代器。

vec.insert(iterator, val);

// 在迭代器之前插入num个值val,返回指向第一个插入元素的迭代器
vec.insert(iterator, num, val); 

// 删除迭代器位置元素,返回被删除元素之后位置的迭代器
vec.erase(iterator); 

// 删除所有元素
vec.clear(); 

// 改变容器大小,如果n大于原本size, 用val初始化,如果n小于原本size,删除容器后面的元素。
vec.resize(n, val); 

一维vector

  1. 创建
vector<int> a;          // 创建一个空的vector, 数据类型int, 数组名 avector<int> a1(100); // 创建一个vector, 数据类型int, 数组名a1, 元素个数100, 初值为0  
vector<int> a2(10, 666); // 创建一个vector, 数据类型Int,数组名a2, 元素个数10, 所有数初值为666  
vector<int>b(a2); // b 是 a2的副本  
vector<int>b1(a2.begin() + 3, a2.end() - 3); // 复制 [a2.begin(), a2.end() - 3)区间的元素到vector b1中
  1. 增加
    vector<int> a;          // 创建一个空的vector, 数据类型int, 数组名 a    a.push_back(5); // 尾部增加一个元素  
    print_vector(a);  
    a.insert(a.begin() + 1, 10); // 在a.begin() + 1 指向元素前插入一个10  
    print_vector(a);  
//    a.insert(a.end(), 10); // 本例中a中只有一个元素,所以 a.begin() + 1 就是a.end()  
    a.insert(a.begin() + 1, 5, 9); // 在 a.begin() + 1 指向元素前插入5个9  
    print_vector(a);  
  
    vector<int> b(a.begin(), a.begin() + 1); // 5  
    b.push_back(-1);  // 5 1  
    print_vector(b);  
  
    a.insert(a.begin() + 1, b.begin(), b.end());  
    print_vector(a);

Output:
5 
5 10 
5 9 9 9 9 9 10 
5 -1 
5 5 -1 9 9 9 9 9 10 
  1. 删除
vector<int> a;          // 创建一个空的vector, 数据类型int, 数组名 aa.push_back(5); // 尾部增加一个元素   ,会改变vector大小
print_vector(a);  
a.insert(a.begin() + 1, 7, 9); // 在a.begin() + 1 指向元素前插入一个10   ,会改变vector大小
print_vector(a);  
  
a.pop_back(); // 删除尾部最后一个元素   ,会改变vector大小
print_vector(a);  
  
a.erase(a.begin() + 1); // 删除指定位置元素 ,会改变vector大小
print_vector(a);  
  
a.erase(a.begin() + 3, a.end()); // 删除区间 [a.begin() + 3, a.end() -3)元素   ,会改变vector大小
print_vector(a);  

a.clear(); // 清空所有元素 ,会改变vector大小

Output:
5 
5 9 9 9 9 9 9 9 
5 9 9 9 9 9 9 
5 9 9 9 9 9 
5 9 9
  1. 遍历
//    for (int i = 0; i < vec.size(); ++i) {  
//        std::cout << vec[i] << " ";  
//    }  
//    std::cout << std::endl;  
  
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {  
        std::cout << *it << " ";  
    }  
    std::cout << std::endl;  
  1. 改变向量的大小
a.resize(5); // 改变向量大小,如果当前容器里元素数量比目标size大,则舍弃后面元素; 如果元素数量比size小,则填充默认值。
a[m]; // 查找a的第m+1个元素
a.size(); // 查询a的大小
a.empty(); // 查询a是否为空,空则返回true, 不空返回false
  1. 统计和
accumulate(a.begin(), a.end(), 0);  // 0 + a中所有数值的元素和

二维vector

  1. 创建
vector<int> a[5]; // 创建5个vector, 每个都是一个数组
vector<vector<int>> a; // 初始化
  1. 增加/修改
a.resize(n); // 将a大小调整为n个
a[n].resize(m); // 将a的第n+1个向量大小初始化为m
a[n].push_back(5); // 将a的第n+1个向量最后位置,插入一个元素5
a.swap(b);  // 将vector a和vector b交换
a[n].pop_back();  // 删除a的第n+1个向量的最后一个元素
a.clear(); // 清空所有元素
a[m][n]; // 查找a的第m+1个向量中的第n+1个元素
a[m].size(); // 查询a[m]的大小
  1. 遍历
    和遍历二维数组一样
vector<vector<int>> a;  
a.push_back(vector<int> (5,0));  
a.push_back(vector<int> (5, -1));  
a.push_back(vector<int> (5, 1));  
  
for (int i = 0; i < a.size(); ++i) {  
    for (int j = 0; j < a[0].size(); ++j) {  
        std::cout << a[i][j] << " ";  
    }  
    std::cout << std::endl;  
}  
  
for (vector<vector<int>>::iterator it = a.begin(); it < a.end(); it++) {  
    for (auto val : *it) {  
        std::cout << val << " ";  
    }  
    std::cout << std::endl;  
}

Output:
0 0 0 0 0 
-1 -1 -1 -1 -1 
1 1 1 1 1 
0 0 0 0 0 
-1 -1 -1 -1 -1 
1 1 1 1 1 

使用std::sort自定义排序

#include <iostream>  
#include <vector>  
  
void print_vector(std::vector<int>& vec)  
{  
//    for (int i = 0; i < vec.size(); ++i) {  
//        std::cout << vec[i] << " ";  
//    }  
//    std::cout << std::endl;  
  
    for (std::vector<int>::iterator it = vec.begin(); it != vec.end(); ++it) {  
        std::cout << *it << " ";  
    }  
    std::cout << std::endl;  
}  
  
bool comp(int x, int y)  
{  
    return (x < y);  
}  
  
struct obj_class {  
    bool operator() (int x, int y) {  
        return x < y;  
    }  
} my_object;  
  
int main(int argc, char **argv) {  
    int datas[] = {36, 71, 12, 46, 35, 45, 18, 20, 5};  
    std::vector<int> vec1(datas, datas + 9); // 36, 71, 12, 45, 35, 45, 18, 20, 5  
    print_vector(vec1);  
  
    // 使用默认比较 (operator <)  
    std::sort(vec1.begin(), vec1.begin() + 3); // (12 36 71) 45, 35, 45, 18, 20, 5  
    print_vector(vec1);  
  
    // comp 函数  
    std::sort(vec1.begin() + 3, vec1.end(), comp); // 36, 71, 12, (5 18 20 35 45 46)  
    print_vector(vec1);  
  
    // 仿函数  
    std::sort(vec1.begin(), vec1.end(), [](int x, int y) ->bool {   // 71 46 45 36 35 20 18 12 5  
        return x > y;  
    });  
    print_vector(vec1);  
  
    // using object  as comp  
    std::sort(vec1.begin(), vec1.end(), my_object); // 5 12 18 20 35 36 45 46 71  
    print_vector(vec1);  
  
    return 0;  
}

Output:
36 71 12 46 35 45 18 20 5
12 36 71 46 35 45 18 20 5
12 36 71 5 18 20 35 45 46
71 46 45 36 35 20 18 12 5
5 12 18 20 35 36 45 46 71

vector迭代器实效

  1. 什么是迭代器实效
    迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器, 程序可能会崩溃)。

优先队列技巧

【C++标准库】priority_queue

定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式。

头文件

#include <queue>

函数原型

在这里插入图片描述

class T  数据类型
class Container = std::vector<T>  底层所使用的容器
class Compare = std::less<typename Container::value_type>  比较方式(决定了优先级)

两种构造方法

优先级队列的构造方式有两种:直接构造一个空对象 和 通过迭代器区间进行构造。

构造空对象法
#include <iostream>
#include <queue>

int main(int argc, char **argv) {
    priority_queue<int> pq;   // 默认大顶堆
    std::cout << typeid(decltype(pq)).name() << std::endl;
    return 0;
}

Output:
St14priority_queueIiSt6vectorIiSaIiEESt4lessIiEE

分析:

std::priority_queue<int, std::vector<int>, std::less<int>>

默认比较方式为 less,最终为 优先级高的值排在上面, 即为大顶堆。

迭代器区间构造法
#include <iostream>
#include <queue>
#include <vector>

using namespace std;

int main(int argc, char **argv) {
    vector<char> vc = { 'a','b','c','d','e' };
    priority_queue<char, deque<char>, greater<char>> pq(vc.begin(), vc.end());	//小顶堆
    cout << typeid(pq).name() << endl;
    while (!pq.empty()) {
        cout << pq.top() << " ";
        pq2.pop();
    }
    return 0;
}

Output:
St14priority_queueIcSt5dequeIcSaIcEESt7greaterIcEE
a b c d e

基本用法

  • top 访问队头元素
  • empty 队列是否为空
  • size 返回队列内元素个数
  • push 插入元素到队尾 (并排序)
  • emplace 原地构造一个元素并插入队列
  • pop 弹出队头元素
  • swap 交换内容

使用priority_queue对结构体实现自定义排序

代码
#include <iostream>
#include <queue>
#include <vector>

// 自定义结构体
struct Node {
    int value;
    Node(int val) : value(val) {}
};

// 自定义比较结构体,实现 operator() 函数
struct CompareNode {
    bool operator()(const Node& a, const Node& b) const {
        // 按照 Node 的 value 从小到大排序
        return a.value > b.value;
    }
};

int main() {
    // 定义 priority_queue,使用自定义比较函数
    std::priority_queue<Node, std::vector<Node>, CompareNode> pq;

    // 向 priority_queue 中添加元素
    pq.push(Node(5));
    pq.push(Node(3));
    pq.push(Node(8));
    pq.push(Node(1));

    // 输出元素,将按从小到大的顺序输出
    while (!pq.empty()) {
        std::cout << pq.top().value << " ";
        pq.pop();
    }
    std::cout << std::endl;

    return 0;
}

Output:
1 3 5 8
解释
  1. 自定义结构体 Node

    • 首先定义了一个简单的结构体 Node,包含一个 int 类型的成员变量 value,并提供了一个构造函数用于初始化 value
  2. 自定义比较结构体 CompareNode

    • 定义了一个结构体 CompareNode,并在其中重载了 operator() 函数。
    • operator()(const Node& a, const Node& b) const 函数接受两个 Node 类型的引用 ab 作为参数。
    • 比较规则是 a.value > b.value,这意味着按照 value 从小到大排序。因为 priority_queue 总是将 “最大” 元素放在队首,通过定义 a.value > b.value,使得较小的元素在比较中被认为是 “更大” 的,从而实现从小到大排序。
  3. 使用 priority_queue

    • std::priority_queue<Node, std::vector<Node>, CompareNode> pq;
      • 声明了一个 priority_queue 容器 pq
      • 第一个模板参数 Node 是存储的元素类型。
      • 第二个模板参数 std::vector<Node> 是存储元素的底层容器,这里使用 vector
      • 第三个模板参数 CompareNode 是自定义的比较函数类型。
  4. 添加元素到 priority_queue

    • 使用 pq.push(Node(5)); 等语句将元素添加到 priority_queue 中。
  5. 输出元素

    • 使用 while (!pq.empty()) 循环,在循环中使用 pq.top() 获取队首元素,使用 pq.pop() 移除队首元素,从而输出元素。

如果你想要实现从大到小的排序,只需修改 CompareNode 的比较函数为 return a.value < b.value;

map

头文件

#include <map>

模板原型

template < class Key,                                   // map::key_type
           class T,                                     // map::mapped_type
		   class Compare = less<Key>,                   // map::key_compare
		   class Alloc = allocator<pair<const Key,T> >  // map::allocator_type
         > class map;

  • 参数解释
Key

Type of the _keys_. Each element in a map is uniquely identified by its key value.  
Aliased as member type map::key_type.

T

Type of the mapped value. Each element in a map stores some data as its mapped value.  
Aliased as member type map::mapped_type.

Compare

A binary predicate that takes two element keys as arguments and returns a bool. The expression comp(a,b), where _comp_ is an object of this type and _a_ and _b_ are key values, shall return true if _a_ is considered to go before _b_ in the _strict weak ordering_ the function defines.  
The map object uses this expression to determine both the order the elements follow in the container and whether two element keys are equivalent (by comparing them reflexively: they are equivalent if !comp(a,b) && !comp(b,a)). No two elements in a map container can have equivalent keys.  
This can be a function pointer or a function object (see [constructor](https://cplusplus.com/map::map) for an example). This defaults to [less](https://cplusplus.com/less)<T>, which returns the same as applying the _less-than operator_ (a<b).  
Aliased as member type map::key_compare.
																																	comp可以是两种类型:(1)函数指针,(2)函数对象。

Alloc

Type of the allocator object used to define the storage allocation model. By default, the [allocator](https://cplusplus.com/allocator) class template is used, which defines the simplest memory allocation model and is value-independent.  
Aliased as member type map::allocator_type.

常用函数

operator[]  
begin() end()  
empty()/size() 判空/元素个数  
clear()   清空
insert(x)  和 make_pair配合使用, 将元素x插入集合(x为二元组)
erase(x)  删除u所有等于x的元素(x为二元组)
erase(it) 删除it指向的元素(it为指向二元组的迭代器)
swap()  交换元素
count(k)  统计键值为k的元素数量
find(k) 查找键值为k的二元组位置,不存在返回尾指针 

map排序:默认key值排序

#include <iostream>  
#include <string>  
#include <map>  
  
using namespace std;  
  
int main() {  
    map<int, string> map1;  
    map1[1] = "abs";  
    map1[6] = "def";  
    map1[3] = "acv";  
    map1[2] = "bar";  
  
    for (auto it = map1.begin(); it!= map1.end(); it++) {  
        cout << it->first << " " << it->second << endl;  
    }  
    return 0;  
}

Output:
1 abs
2 bar
3 acv
6 def

map排序: 自定义key值排序

#include <iostream>  
#include <string>  
#include <map>  
  
using namespace std;  
  
struct com_str {  
    bool operator()(const string& x, const string& y) const {  
        return x > y;  
    }  
};  
  
int main() {  
    map<string, int, com_str> map2;  
    map2["abs"] = 1;  
    map2.insert(make_pair("def", 6));  
    map2.insert(make_pair("acv", 3));  
    map2.insert(make_pair("bar", 2));  
    for (auto it = map2.begin(); it != map2.end(); it++) {  
        cout << it->first << " " << it->second << endl;  
    }  
    return 0;  
}

Outpuit:
def 6
bar 2
acv 3
abs 1

map排序: 自定义value排序

目标: map<int, vector<int>> 将里面元素按照value的size大小排序,如果value size一样, 则按照key来排序。

#include <iostream>  
#include <algorithm>  
#include <vector>  
#include <string>  
#include <map>  
  
using namespace std;  
  
int main() {  
    map<int, vector<int>> map3;  
    map3[1] = vector<int> (5, 6);  
    map3.insert(make_pair(6, vector<int> {6, 5}));  
    map3.insert(make_pair(3, vector<int> {7, 4}));  
    map3[2] = vector<int> {0, 2, 10, 7};  
  
    vector<pair<int, vector<int>>> vec(map3.begin(), map3.end());  
    std::sort(vec.begin(), vec.end(), [](pair<int, vector<int>>& x, pair<int, vector<int>>& y) -> bool {  
        if (x.second.size() == y.second.size()) {  
            return x.first < y.first;  
        }  
  
        return x.second.size() < y.second.size();  
    });  

	// 打印排序结果
    for (auto it = vec.begin(); it != vec.end(); it++) {  
        cout << "KEY:" << it->first << endl;  
        for (auto vec_it = it->second.begin(); vec_it != it->second.end(); vec_it++) {  
            cout << *vec_it << " ";  
        }  
        cout << endl;  
    }  
  
    return 0;  
}


Output:
KEY:3
7 4
KEY:6
6 5
KEY:2
0 2 10 7
KEY:1
6 6 6 6 6

stack

栈( stack) 只允许在栈顶操作, 不允许在中间位置进行插入和删除操作, 不支持数组表示法和随机访问。 栈的基本操作很简单, 包括入栈、 出栈、 取栈顶、判断栈空、 求栈大小。

头文件

使用stack时需要引入头文件。

#include <stack>

常用接口

struct obj {  
    int val;  
    obj();  
    obj(int x): val(x) {
	}
};

stack<obj> s; // 创建一个空栈s, 数据类型为obj
push(x);   入栈
pop(); 出栈
top(); 取栈顶
empty(); 判断栈是否为空,如果空返回true, 非空返回false
size(); 获取栈元素个数, 栈大小

举个例子

stack<obj> s; // 创建一个空栈s, 数据类型为obj
if (s.empty()) {  
    cout << "current stack is empty, and size : " << s.size() << endl;  
}  
s.push(obj(1));  
cout << "top1 : " << s.top().val << endl;  
s.push(obj(2));  
cout << "top2 : " << s.top().val << endl;  
s.push(obj(3));  
cout << "top3 : " << s.top().val << endl;  
s.pop();  
cout << "top4 : " << s.top().val << endl;  
s.push(obj(4));  
cout << "top5 : " << s.top().val << endl;  
if (!s.empty()) {  
    cout << "current stack not empty, and size : " << s.size() << endl;  
}  
while (s.size() > 0) {  
    obj top = s.top();  
    cout << "pop value : " << top.val << endl;  
    s.pop();  
}


Output:
current stack is empty, and size : 0
top1 : 1
top2 : 2
top3 : 3
top4 : 2
top5 : 4
current stack not empty, and size : 3
pop value : 4
pop value : 2
pop value : 1

queue

队列( queue) 只允许从队尾入队、 从队头出队, 不允许在中间位置插入和删除, 不支持数组表示法和随机访问。 队列的基本操作很简单, 包括入队、 出队、取队头、 判断队空、 求队列大小。

头文件

使用队列(queue)时需要引入头文件。

#include <queue>

常用接口

struct obj {  
    int val;  
    obj();  
    obj(int x): val(x) {
	}
};

queue<obj> q; 创建一个空队列q, 数据类型为obj
push(x); x入队
pop(); 出队
front(); 取对头(未出队)
empty(); 判断队列是否为空, 如果空返回true, 非空返回false
size(); 获取队列元素个数。

举个例子

#include <iostream>  
#include <queue>  
  
using namespace std;  
  
struct obj {  
    int val;  
    obj();  
    obj(int x): val(x) {  
  
    }};  
  
int main(int argc, char **argv) {  
    queue<obj> q; // 创建一个队列q, 数据类型为obj  
    if (q.empty()) {  
        cout << "current queue is empty, and size : " << q.size() << endl;  
    }  
    q.push(obj(1));  
    cout << "front1 : " << q.front().val << endl;  
    q.push(obj(2));  
    cout << "front2 : " << q.front().val << endl;  
    q.push(obj(3));  
    cout << "front3 : " << q.front().val << endl;  
    q.pop();  
    cout << "front4 : " << q.front().val << endl;  
    q.push(obj(4));  
    cout << "front5 : " << q.front().val << endl;  
    if (!q.empty()) {  
        cout << "current queue not empty, and size : " << q.size() << endl;  
    }  
    while (q.size() > 0) {  
        obj front = q.front();  
        cout << "pop value : " << front.val << endl;  
        q.pop();  
    }  
    return 0;  
}

Output:
current queue is empty, and size : 0
front1 : 1
front2 : 1
front3 : 1
front4 : 2
front5 : 2
current queue not empty, and size : 3
pop value : 2
pop value : 3
pop value : 4

迭代器

迭代器不一定都支持算数加减

迭代器实质上是一个指针,但不是所有的容器的迭代器都支持算数加减操作。能进行算数运算的迭代器只有随机访问迭代器,要求容器元素存储在连续内存空间内。

  • vectorstringdeque的迭代器是有加减法的
  • mapsetmultimapmultisetlist的迭代器是没有加减法的。他们仅支持++it--it操作。

it++与++it的区别

在STL中的容器使用迭代器进行遍历时,it++++it的效果是相同的,遍历的次数也是相同的,但是在STL中的效率却不同:

  • ++it(或--it)返回的是引用
  • it++(或it--)返回的是临时对象
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值