0%

C++数据结构篇『一』C++风格数组与字符串类

起初是为了刷题而学习使用C++的,毕竟算法很重要。为什么不使用python作为刷题工具呢,第一性能很差劲,第二,上学期学算法我写python的第一句就是import numpy as np,这是个不太好的习惯。

刷题的过程中,因为对C++的一些形如STL的特性并不是很了解,与其说是C++倒不如说是C,所以不如学习一下C++的特性先。没想到两年后我来填坑了。

array

C语言风格的数组:

  • 可能退化为指针,sizeof(a)/sizeof(a[0])失效
  • 不知道自己的大小
  • 数组元素无法直接复制, a = b
  • 不能自动推导类型,auto a[] = {1, 2, 3}

C++风格数组:

  • 是一个容器类,有迭代器
  • 是模板类,容纳任何类型的数据,但数组不能扩大也不能缩小,数据类型相同
  • 对类模板的参数进行推导,推导出类型和数量
  • 可以直接赋值
  • 知道自己的大小 size()
  • 和另一个数组交换内容 swap()
  • 用指定内容填充自己 fill()
  • 取某个元素位置,做越界检查 at()

声明与初始化

使用C++风格数组时,先在头文件中引用,并使用using指令声明使用std空间中的对应对象。

1
2
3
4
5
6
7
8
9
10
11
#include <array>
using std::array;
...
// int 类型 10 个元素
array<int, 10> x, x1;
// char 类型 5 个元素
array<char, 5> a{'a', 'b', 'c', 'd', 'e'};

// 自动推导 C++17 支持
array a1 {1, 2, 3, 4};
array a2 {'1', '2', '3', '4'};

其他操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 访问指定元素 做越界检查
cout << a.at(2) << endl;
// 越界后,直接退出,不报错,后续语句无法执行
// cout << a.at(10) << endl;

// 访问第一个元素
cout << a.front() << endl;

// 访问最后一个元素
cout << a.back() << endl;

// &a 表示取出a这个数组对象的地址
// &a[0] 取出存储数组位置的首地址
// 返回指向数组第一个元素的指针
cout << *(a.data()) << endl;

// 容量
// 当前容量
cout << x.size() << endl;

// 返回容纳的最大元素数
// 因为 array 类必须指定数组的数量,且数量不可变
cout << x.max_size() << endl;

// 以指定值填充容器
x.fill(12);
x1.fill(13);

// 交换内容
x.swap(x1);

// 二维数组 4行3列
array<array<int , 3>, 4> a1;

C语言中的字符串

C语言中的字符串不能作为参数返回,因为子函数在执行完毕后所有变量的空间都会释放,(当然全局变量除外),处理起来比较麻烦。且C语言的字符串为字符数组,类型为const char*,以空字符结尾,遇到0会终止,无法记录自身长度,不能像对象那样用着方便,并不是严格意义的字符串。所以来用C++的String类吧,体验真正的字符串。

到C++中String类的世界

声明与初始化

  • C++11中生字符串语法:R”delimiter(raw_characters)delimiter”。
  • C++14生字符串,重载 “”s 运算符。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 字符串操作练习
#include <iostream>
using namespace std;

int main(){

// 生字符串 \n 被保留输出
const char* s1 = R"(hello\n world)";
cout << s1 << endl;

// 忽略分隔符
const char* s2 = R"Nouse(hello\a world)Nouse";
cout << s2 << endl;

// 自动推导为 std::string 字符串字面量 \0 会被转义
auto s3 = "he\0llo"s;
cout << s3 << endl;

// 否则为 \0 视为终止符
string s4 = "he\0llo";
cout << s4.size() << endl;

// \0 不转义
string s5 = R"(he\0llo)"s;
cout << s5 << endl;

// Character literals
auto c0 = 'A'; // char
auto c1 = u8'A'; // char
auto c2 = L'A'; // wchar_t
auto c3 = u'A'; // char16_t
auto c4 = U'A'; // char32_t

// Multicharacter literals
auto m0 = 'abcd'; // int, value 0x61626364

// String literals
auto s0 = "hello"; // const char*
auto s1 = u8"hello"; // const char*, encoded as UTF-8
auto s2 = L"hello"; // const wchar_t*
auto s3 = u"hello"; // const char16_t*, encoded as UTF-16
auto s4 = U"hello"; // const char32_t*, encoded as UTF-32

// Raw string literals containing unescaped \ and "
auto R0 = R"("Hello \ world")"; // const char*
auto R1 = u8R"("Hello \ world")"; // const char*, encoded as UTF-8
auto R2 = LR"("Hello \ world")"; // const wchar_t*
auto R3 = uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16
auto R4 = UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32

// Combining string literals with standard s-suffix
auto S0 = "hello"s; // std::string
auto S1 = u8"hello"s; // std::string
auto S2 = L"hello"s; // std::wstring
auto S3 = u"hello"s; // std::u16string
auto S4 = U"hello"s; // std::u32string

// Combining raw string literals with standard s-suffix
auto S5 = R"("Hello \ world")"s; // std::string from a raw const char*
auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char*, encoded as UTF-8
auto S7 = LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t*
auto S8 = uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16
auto S9 = UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32

return 0;
}

输出为:

1
2
3
4
5
hello\n world
hello\a world
he llo
he
he\0llo

查找方法

查找第一次出现的地方:

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>

using namespace std;

int main() {
string str = "cat";
int a;
// 在str中寻找c的位置,并返回,这里输出0
a = str.find("c");
printf("%d\n", a);
return 0;
}

更多的查找操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace std;

int main() {
string str = "0123456789";
int a;
a = str.find("01");
printf("%d\n", a);

// 从2开始查找,包含 2
// 范围内查找并返回第一个字符串 ab 在 str 的位置
a = str.find("23", 2);
printf("%d\n", a);

// 在 str[0]~str[2] 范围内查找并返回字符串 ab 在 str 的位置
a = str.rfind("1", 2);
printf("%d\n", a);

return 0;
}

first系列函数

1
2
3
4
5
6
7
8
9
// first 系列函数
// 返回 apple 中任何一个字符首次在 str 中出现的位置
str.find_first_of("apple");
// 返回 apple 中任何一个字符首次在 str[2]~str[n-1] 范围中出现的位置
str.find_first_of("apple", 2);
// 返回除 apple 以外的任何一个字符在 str 中首次出现的位置
str.find_first_not_of("apple");
// 返回除 apple 以外的任何一个字符在 str[2]~str[n-1] 范围中首次出现的位置
str.find_first_not_of("apple", 2);

last系列函数

1
2
3
4
5
6
7
8
9
// last 系列函数
// 返回 apple 中任何一个字符最后一次在 str 中出现的位置
str.find_last_of("apple");
// 返回 apple 中任何一个字符最后一次在 str[0]~str[2] 范围中出现的位置
str.find_last_of("apple", 2);
// 返回除 apple 以外的任何一个字符在 str 中最后一次出现的位置
str.find_last_not_of("apple");
// 返回除 apple 以外的任何一个字符在 str[0]~str[2] 范围中最后一次出现的位置
str.find_last_not_of("apple", 2);

没找到

没找到的话,返回值是无穷大。

子串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <string>
using namespace std;

int main() {
string str = "0123456789";

// 返回 [3] 及以后的子串
cout<<str.substr(3)<<endl;
// 返回 str[2]~str[2+(4-1)] 子串
// 即从[2]开始4个字符组成的字符串
cout<<str.substr(2, 4);
if (s.find("aa1", 0) == string::npos)
{
cout << "找不到该子串!" << endl;
}
return 0;
}

替换

1
2
3
4
// 返回把 [2]~[2+(4-1)] 的内容替换为 "sz" 后的新字符串
str.replace(2, 4, "sz");
// 返回把 [2]~[2+(4-1)] 的内容替换为 "abcd" 的前3个字符后,得到新字符串
str.replace(2, 4, "abcd", 3);

插入

1
2
3
4
5
6
7
8
9
// 从 [2] 位置之前,插入字符串 "sz",并返回形成的新字符串
str.insert(2, "sz");
// 从 [2] 位置之前,插入字符串 "abcd" 的前3个字符,并返回形成的新字符串
str.insert(2, "abcd", 3);
// 从 [2] 位置开始添加字符串 "abcd" 中从1开始,共插入3个,
// 即插入 bcd 并返回形成的新字符串
str.insert(2, "abcd", 1, 3);
// 从 [1] 插入四个相同的字符,不能插入子串
str.insert(1, 4, '+');

追加

1
2
3
4
5
str.push_back('a'); // 在 str 末尾添加字符 'a',只能插入一个
str.append("abc"); // 在 str 末尾添加字符串 "abc"
str.append(" to C and C++", 3, 2) // 从3开始追加,追加2个
str.append(" to C and C++", 2) // 从0开始追加,追加2个
str.append(4, '!') // 追加四次,同样只能追加字符,不能子串

删除

1
2
str.erase(3);    // 删除 [3] 及以后的字符,并返回新字符串
str.erase(3, 5); // 删除从 [3] 开始的 5 个字符,并返回新字符串

交换

1
str1.swap(str2); // 把 str1 与 str2 交换

比较

等于返回0,大于返回1,小于返回-1。

1
2
s.compare(pos1, n1, s2) // 让s中从pos下标开始的n1个字符跟s2做比较
s.compare(s1) // 直接比较

求长度

1
2
3
4
string str(0, 'a');
cout << str.size() << endl; // 大小
cout << str.empty() << endl; // 空为 1,不空为 0
cout << str.length() << endl; // 和size输出相同

拼接

两个String类的字符串直接相加即可

1
2
3
string str(10, 'a');
string str1(11, 'b');
cout << str + str1 << endl;

迭代器访问

1
2
3
4
5
string str("iUEgFDBIWEJFIDB");
for (string::iterator it = str.begin(); it != str.end(); it++)
{
cout << *it << endl;
}

其他操作

输入数据并求和

1
2
3
4
5
int a, b, len;
cin >> a >> b;
string s = to_string(a + b);
len = s.length();
cout << s;

字符倒序输出

1
2
3
4
5
6
7
8
9
10
11
#include<iostream>
// #include <string>
using namespace std;
int main()
{
string str("cvicses");
// 反向迭代器,指定初始位置和结束位置
string s(str.rbegin(),str.rend());
cout << s <<endl;
return 0;
}

参考

https://docs.microsoft.com/zh-cn/cpp/cpp/string-and-character-literals-cpp?view=msvc-160

感谢上学期间打赏我的朋友们。赛博乞讨:我,秦始皇,打钱。

欢迎订阅我的文章