文档更新与内容添加
- 更新mkdocs.yml,重新组织数学基础和数据结构部分的文档结构。- 删除进制转换和矩阵运算文档。 - 添加C++标准库各模块(iostream、fstream、string、ctime、chrono、vector、list、map、set)的文档。- 在数据结构部分添加二叉树和霍夫曼编码的文档。 - 电子学会考级内容调整,移除历史人物事件文档。
This commit is contained in:
parent
25b4324453
commit
b28c95cd8b
|
@ -0,0 +1,28 @@
|
||||||
|
1. `cout`: 用于标准输出,可以使用`<<`操作符将数据输出到屏幕上。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::cout << "Hello, world!" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `cin`: 用于标准输入,可以使用`>>`操作符从用户输入中读取数据。
|
||||||
|
```cpp
|
||||||
|
int num;
|
||||||
|
std::cin >> num;
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `endl`: 输出换行符并刷新输出缓冲区。
|
||||||
|
```cpp
|
||||||
|
std::cout << "Hello" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
4. `cerr`: 用于输出错误信息,通常用于标准错误流。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
std::cerr << "Error: something went wrong!" << std::endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
5. `fixed`和`setprecision`: 用于控制浮点数的输出精度。
|
||||||
|
```cpp
|
||||||
|
double num = 3.14159;
|
||||||
|
std::cout << std::fixed << std::setprecision(2) << num << std::endl;
|
||||||
|
```
|
|
@ -0,0 +1,89 @@
|
||||||
|
1. **查找和比较**:
|
||||||
|
- `find`:在范围内查找元素。
|
||||||
|
- `find_if`:在范围内查找满足指定条件的元素。
|
||||||
|
- `count`:统计范围内满足条件的元素个数。
|
||||||
|
- `count_if`:统计范围内满足指定条件的元素个数。
|
||||||
|
- `equal`:比较两个范围是否相等。
|
||||||
|
- `lexicographical_compare`:按字典序比较两个范围。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
vector<int> vec = {1, 2, 3, 4, 5};
|
||||||
|
|
||||||
|
// 查找元素
|
||||||
|
auto it = find(vec.begin(), vec.end(), 3);
|
||||||
|
if (it != vec.end()) {
|
||||||
|
cout << "Element found: " << *it << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查找满足条件的元素
|
||||||
|
auto it2 = find_if(vec.begin(), vec.end(), [](int x) { return x > 3; });
|
||||||
|
if (it2 != vec.end()) {
|
||||||
|
cout << "Element > 3 found: " << *it2 << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计元素个数
|
||||||
|
int num = count(vec.begin(), vec.end(), 2);
|
||||||
|
cout << "Number of 2s: " << num << endl;
|
||||||
|
|
||||||
|
// 比较两个范围
|
||||||
|
vector<int> vec2 = {1, 2, 3};
|
||||||
|
bool result = equal(vec.begin(), vec.end(), vec2.begin(), vec2.end());
|
||||||
|
if (result) {
|
||||||
|
cout << "Vectors are equal" << endl;
|
||||||
|
} else {
|
||||||
|
cout << "Vectors are not equal" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **排序和操作**:
|
||||||
|
- `sort`:对范围内的元素进行排序。
|
||||||
|
- `reverse`:反转范围内的元素顺序。
|
||||||
|
- `copy`:将范围内的元素复制到另一个位置。
|
||||||
|
- `remove`:移除范围内满足条件的元素(不会改变容器大小)。
|
||||||
|
- `remove_if`:移除范围内满足指定条件的元素(不会改变容器大小)。
|
||||||
|
- `transform`:对范围内的元素执行指定操作。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
vector<int> vec = {5, 3, 1, 4, 2};
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
sort(vec.begin(), vec.end());
|
||||||
|
|
||||||
|
// 反转元素顺序
|
||||||
|
reverse(vec.begin(), vec.end());
|
||||||
|
|
||||||
|
// 复制元素到另一个位置
|
||||||
|
vector<int> vec2(5);
|
||||||
|
copy(vec.begin(), vec.end(), vec2.begin());
|
||||||
|
|
||||||
|
// 移除元素
|
||||||
|
vec.erase(remove(vec.begin(), vec.end(), 3), vec.end());
|
||||||
|
|
||||||
|
// 对元素执行操作
|
||||||
|
transform(vec.begin(), vec.end(), vec.begin(), [](int x) { return x * 2; });
|
||||||
|
|
||||||
|
// 输出元素
|
||||||
|
for (auto& num : vec) {
|
||||||
|
cout << num << " ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,87 @@
|
||||||
|
1. `std::ifstream`: 用于从文件中读取数据的输入流对象。常用的成员函数包括:
|
||||||
|
|
||||||
|
- `open(const char* filename)`: 打开指定文件名的文件。
|
||||||
|
- `close()`: 关闭文件。
|
||||||
|
- `is_open()`: 判断文件是否已经打开。
|
||||||
|
- `good()`: 判断文件流状态是否良好。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::ifstream inputFile;
|
||||||
|
inputFile.open("input.txt");
|
||||||
|
|
||||||
|
if (inputFile.is_open()) {
|
||||||
|
std::cout << "File opened successfully." << std::endl;
|
||||||
|
// 读取文件内容
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(inputFile, line)) {
|
||||||
|
std::cout << line << std::endl;
|
||||||
|
}
|
||||||
|
inputFile.close();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unable to open file." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `std::ofstream`: 用于向文件中写入数据的输出流对象。常用的成员函数包括:
|
||||||
|
- `open(const char* filename)`: 创建或打开指定文件名的文件。
|
||||||
|
- `close()`: 关闭文件。
|
||||||
|
- `is_open()`: 判断文件是否已经打开。
|
||||||
|
- `good()`: 判断文件流状态是否良好。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::ofstream outputFile;
|
||||||
|
outputFile.open("output.txt");
|
||||||
|
|
||||||
|
if (outputFile.is_open()) {
|
||||||
|
std::cout << "File opened successfully." << std::endl;
|
||||||
|
// 写入数据到文件
|
||||||
|
outputFile << "Hello, world!" << std::endl;
|
||||||
|
outputFile << 42 << std::endl;
|
||||||
|
outputFile.close();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unable to open file." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. `std::fstream`: 同时支持读写操作的文件流对象。常用的成员函数包括:
|
||||||
|
- `open(const char* filename, std::ios_base::openmode mode)`: 打开指定文件名的文件,并指定打开模式(例如`std::ios::in`表示读取模式,`std::ios::out`表示写入模式)。
|
||||||
|
- `close()`: 关闭文件。
|
||||||
|
- `is_open()`: 判断文件是否已经打开。
|
||||||
|
- `good()`: 判断文件流状态是否良好。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::fstream file;
|
||||||
|
file.open("data.txt", std::ios::out | std::ios::app);
|
||||||
|
|
||||||
|
if (file.is_open()) {
|
||||||
|
std::cout << "File opened successfully." << std::endl;
|
||||||
|
// 写入数据到文件
|
||||||
|
file << "Appended line." << std::endl;
|
||||||
|
file.close();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Unable to open file." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
这些函数和对象使得在C++中进行文件输入输出操作变得简单和方便。
|
|
@ -0,0 +1,76 @@
|
||||||
|
1. `std::string`: C++中的字符串类,提供了丰富的成员函数用于字符串操作。常用的成员函数包括:
|
||||||
|
- `size()`: 返回字符串的长度。
|
||||||
|
- `length()`: 返回字符串的长度。
|
||||||
|
- `empty()`: 判断字符串是否为空。
|
||||||
|
- `clear()`: 清空字符串内容。
|
||||||
|
- `substr(pos, len)`: 返回从位置`pos`开始长度为`len`的子字符串。
|
||||||
|
- `find(str, pos)`: 在字符串中查找子字符串`str`,并返回第一次出现的位置。
|
||||||
|
- `replace(pos, len, str)`: 替换字符串中从位置`pos`开始长度为`len`的子串为字符串`str`。
|
||||||
|
- `append(str)`: 在字符串末尾追加字符串`str`。
|
||||||
|
- `insert(pos, str)`: 在指定位置插入字符串`str`。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string str = "Hello, world!";
|
||||||
|
|
||||||
|
// 使用成员函数进行字符串操作
|
||||||
|
std::cout << "Length: " << str.length() << std::endl;
|
||||||
|
std::cout << "Substring: " << str.substr(0, 5) << std::endl;
|
||||||
|
|
||||||
|
str.replace(7, 5, "C++");
|
||||||
|
std::cout << "Replaced: " << str << std::endl;
|
||||||
|
|
||||||
|
str.append(" Goodbye!");
|
||||||
|
std::cout << "Appended: " << str << std::endl;
|
||||||
|
|
||||||
|
str.insert(0, "Greetings, ");
|
||||||
|
std::cout << "Inserted: " << str << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. `std::getline()`: 从输入流中读取一行数据并存储到字符串中。
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string line;
|
||||||
|
std::cout << "Enter a line of text: ";
|
||||||
|
std::getline(std::cin, line);
|
||||||
|
std::cout << "You entered: " << line << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 字符串查找和比较函数:
|
||||||
|
- `std::string::find(str, pos)`: 在字符串中查找子字符串`str`,并返回第一次出现的位置。
|
||||||
|
- `std::string::rfind(str, pos)`: 在字符串中从后向前查找子字符串`str`,并返回第一次出现的位置。
|
||||||
|
- `std::string::find_first_of(str, pos)`: 在字符串中查找任意字符集合`str`中的字符,返回第一次出现的位置。
|
||||||
|
- `std::string::find_last_of(str, pos)`: 在字符串中从后向前查找任意字符集合`str`中的字符,返回第一次出现的位置。
|
||||||
|
- `std::string::compare(str)`: 比较字符串与`str`,返回大小关系(0表示相等,负数表示小于,正数表示大于)。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
std::string str = "Hello, world!";
|
||||||
|
|
||||||
|
if (str.find("world") != std::string::npos) {
|
||||||
|
std::cout << "Substring found!" << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str.compare("Hello, C++!") == 0) {
|
||||||
|
std::cout << "Strings are equal." << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
1. `time_t time(time_t* timer)`: 返回当前的日历时间作为一个 `time_t` 对象。如果 `timer` 不是 NULL,则结果也存储在 `timer` 指向的变量中。
|
||||||
|
2. `struct tm *localtime(const time_t* timer)`: 将日历时间 `timer` 转换为本地时间表示(`struct tm`),包括年、月、日、时、分和秒等字段。
|
||||||
|
3. `struct tm *gmtime(const time_t* timer)`: 类似于 `localtime`,但它将日历时间 `timer` 转换为协调世界时(UTC)。
|
||||||
|
4. `time_t mktime(struct tm* timeptr)`: 将表示日历时间的 `struct tm` 对象转换为 `time_t` 对象。
|
||||||
|
5. `char* asctime(const struct tm* timeptr)`: 将 `struct tm` 对象转换为人类可读的字符串,表示日期和时间的格式为 "Www Mmm dd hh:mm:ss yyyy\n",其中 Www 是星期几,Mmm 是月份,dd 是日期,hh:mm:ss 是时间,yyyy 是年份。
|
||||||
|
6. `char* ctime(const time_t* timer)`: 等同于 `asctime(localtime(timer))`。它将 `time_t` 对象转换为人类可读的字符串,表示本地时间。
|
||||||
|
7. `clock_t clock(void)`: 返回程序自执行开始以来或上一次调用 `clock()` 以来消耗的处理器时间。返回的值以时钟滴答数表示,可以使用 `CLOCKS_PER_SEC` 将其转换为秒。
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <iostream>
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 获取当前时间
|
||||||
|
time_t now = time(0);
|
||||||
|
std::cout << "当前时间为:" << ctime(&now);
|
||||||
|
|
||||||
|
// 将当前时间转换为本地时间
|
||||||
|
struct tm *localTime = localtime(&now);
|
||||||
|
std::cout << "本地时间为:" << asctime(localTime);
|
||||||
|
|
||||||
|
// 将当前时间转换为UTC时间
|
||||||
|
struct tm *utcTime = gmtime(&now);
|
||||||
|
std::cout << "UTC时间为:" << asctime(utcTime);
|
||||||
|
|
||||||
|
// 将本地时间结构体转换为时间戳
|
||||||
|
time_t localTimestamp = mktime(localTime);
|
||||||
|
std::cout << "本地时间的时间戳为:" << localTimestamp << std::endl;
|
||||||
|
|
||||||
|
// 测量程序执行时间
|
||||||
|
clock_t start = clock();
|
||||||
|
for (int i = 0; i < 1000000; ++i) {
|
||||||
|
// 一些计算任务
|
||||||
|
}
|
||||||
|
clock_t end = clock();
|
||||||
|
double elapsedSeconds = double(end - start) / CLOCKS_PER_SEC;
|
||||||
|
std::cout << "程序执行时间为:" << elapsedSeconds << " 秒" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
1. **时长类型**:
|
||||||
|
- `std::chrono::duration`:表示时间段。例如,`std::chrono::duration<int>` 表示基于整数的时间段,而 `std::chrono::duration<double>` 表示基于浮点数的时间段。
|
||||||
|
- `std::chrono::hours`、`std::chrono::minutes`、`std::chrono::seconds`、`std::chrono::milliseconds`、`std::chrono::microseconds`、`std::chrono::nanoseconds`:具有不同单位的特定时长类型。
|
||||||
|
|
||||||
|
2. **时间点类型**:
|
||||||
|
- `std::chrono::time_point`:表示时间点。它在时钟类型和时长类型上进行模板化。
|
||||||
|
- `std::chrono::system_clock`、`std::chrono::steady_clock`、`std::chrono::high_resolution_clock`:`<chrono>` 提供的时钟类型。
|
||||||
|
|
||||||
|
3. **时钟函数**:
|
||||||
|
- `std::chrono::duration_cast`:将一个时长转换为具有不同刻度的另一个时长。
|
||||||
|
- `std::chrono::time_point_cast`:将一个时间点转换为具有不同时钟的另一个时间点。
|
||||||
|
- `std::chrono::system_clock::now()`:根据系统时钟获取当前时间。
|
||||||
|
- `std::chrono::steady_clock::now()`:根据稳定时钟(单调时钟)获取当前时间。
|
||||||
|
- `std::chrono::high_resolution_clock::now()`:如果可用,根据高分辨率时钟获取当前时间。
|
||||||
|
|
||||||
|
4. **实用函数**:
|
||||||
|
- `std::chrono::duration_values`:提供时长类型的最小值和最大值。
|
||||||
|
- `std::chrono::time_point::min()`、`std::chrono::time_point::max()`:返回时间点类型的最小值和最大值。
|
||||||
|
|
||||||
|
5. **算术运算**:
|
||||||
|
- 时长类型支持算术运算,如加法(`operator+`)、减法(`operator-`)、乘法(`operator*`)、除法(`operator/`)以及比较运算(`operator==`、`operator!=`、`operator<` 等)。
|
||||||
|
|
||||||
|
6. **时长字面值**:
|
||||||
|
- 可以使用字面值后缀,如 `h`、`min`、`s`、`ms`、`us`、`ns` 来创建时长字面值。例如,`5s` 表示 5 秒。
|
||||||
|
|
||||||
|
这里是一个简单的示例,演示了这些功能:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
// 定义时长
|
||||||
|
auto 秒 = seconds(10);
|
||||||
|
auto 毫秒 = 5ms;
|
||||||
|
|
||||||
|
// 添加时长
|
||||||
|
auto 总时长 = 秒 + 毫秒;
|
||||||
|
|
||||||
|
// 获取当前时间点
|
||||||
|
auto 开始 = steady_clock::now();
|
||||||
|
|
||||||
|
// 等待 2 秒
|
||||||
|
std::this_thread::sleep_for(2s);
|
||||||
|
|
||||||
|
// 计算经过的时间
|
||||||
|
auto 结束 = steady_clock::now();
|
||||||
|
auto 经过时间 = duration_cast<seconds>(结束 - 开始);
|
||||||
|
|
||||||
|
std::cout << "总时长:" << 总时长.count() << " 毫秒\n";
|
||||||
|
std::cout << "经过时间:" << 经过时间.count() << " 秒\n";
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
1. **构造函数**:
|
||||||
|
- `vector<T>`:创建一个空的向量,元素类型为 `T`。
|
||||||
|
- `vector<T>(size_type count)`:创建包含 `count` 个默认构造的元素的向量。
|
||||||
|
- `vector<T>(size_type count, const T& value)`:创建包含 `count` 个值为 `value` 的元素的向量。
|
||||||
|
- `vector<T>(InputIterator first, InputIterator last)`:使用迭代器范围 `[first, last)` 中的元素创建向量。
|
||||||
|
|
||||||
|
2. **元素访问**:
|
||||||
|
- `vector::operator[]`:通过索引访问元素。
|
||||||
|
- `vector::at(size_type pos)`:通过位置访问元素,如果越界会抛出 `std::out_of_range` 异常。
|
||||||
|
- `vector::front()`:返回第一个元素的引用。
|
||||||
|
- `vector::back()`:返回最后一个元素的引用。
|
||||||
|
- `vector::data()`:返回指向底层数据的指针。
|
||||||
|
|
||||||
|
3. **迭代器**:
|
||||||
|
- `vector::begin()`:返回指向第一个元素的迭代器。
|
||||||
|
- `vector::end()`:返回指向最后一个元素后面位置的迭代器。
|
||||||
|
- `vector::rbegin()`:返回指向最后一个元素的逆向迭代器。
|
||||||
|
- `vector::rend()`:返回指向第一个元素前面位置的逆向迭代器。
|
||||||
|
|
||||||
|
4. **容量**:
|
||||||
|
- `vector::size()`:返回向量中元素的数量。
|
||||||
|
- `vector::max_size()`:返回向量能容纳的最大元素数量。
|
||||||
|
- `vector::empty()`:检查向量是否为空。
|
||||||
|
- `vector::reserve(size_type new_cap)`:为向量预留至少能容纳 `new_cap` 个元素的空间。
|
||||||
|
- `vector::capacity()`:返回向量当前能容纳的元素数量。
|
||||||
|
|
||||||
|
5. **修改容器**:
|
||||||
|
- `vector::push_back(const T& value)`:在向量末尾添加一个元素。
|
||||||
|
- `vector::pop_back()`:移除向量末尾的元素。
|
||||||
|
- `vector::insert(iterator pos, const T& value)`:在指定位置插入一个元素。
|
||||||
|
- `vector::erase(iterator pos)`:移除指定位置的元素。
|
||||||
|
- `vector::clear()`:清空向量中的所有元素。
|
||||||
|
|
||||||
|
6. **比较**:
|
||||||
|
- `vector::operator==`、`vector::operator!=`、`vector::operator<`、`vector::operator<=`、`vector::operator>`、`vector::operator>=`:用于比较两个向量的操作符。
|
||||||
|
|
||||||
|
7. **其他操作**:
|
||||||
|
- `vector::swap(vector& other)`:交换两个向量的内容。
|
||||||
|
- `vector::emplace_back(Args&&... args)`:在向量末尾构造一个元素。
|
||||||
|
- `vector::shrink_to_fit()`:将向量的容量调整为其当前元素数量。
|
||||||
|
|
||||||
|
```c++
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 创建一个空的整数向量
|
||||||
|
std::vector<int> numbers;
|
||||||
|
|
||||||
|
// 在向量末尾添加元素
|
||||||
|
numbers.push_back(10);
|
||||||
|
numbers.push_back(20);
|
||||||
|
numbers.push_back(30);
|
||||||
|
|
||||||
|
// 使用迭代器遍历向量并输出元素
|
||||||
|
std::cout << "Vector elements:";
|
||||||
|
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
|
||||||
|
std::cout << " " << *it;
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
|
||||||
|
// 访问向量的第一个和最后一个元素
|
||||||
|
std::cout << "First element: " << numbers.front() << std::endl;
|
||||||
|
std::cout << "Last element: " << numbers.back() << std::endl;
|
||||||
|
|
||||||
|
// 检查向量是否为空
|
||||||
|
std::cout << "Is the vector empty? " << (numbers.empty() ? "Yes" : "No") << std::endl;
|
||||||
|
|
||||||
|
// 获取向量的大小和容量
|
||||||
|
std::cout << "Vector size: " << numbers.size() << std::endl;
|
||||||
|
std::cout << "Vector capacity: " << numbers.capacity() << std::endl;
|
||||||
|
|
||||||
|
// 清空向量
|
||||||
|
numbers.clear();
|
||||||
|
|
||||||
|
std::cout << "Vector size after clear: " << numbers.size() << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
1. **构造函数**:
|
||||||
|
- `list()`:创建一个空链表。
|
||||||
|
- `list(const list& other)`:拷贝构造函数,用另一个链表初始化当前链表。
|
||||||
|
|
||||||
|
2. **赋值和交换**:
|
||||||
|
- `operator=`:将一个链表赋值给另一个链表。
|
||||||
|
- `assign`:用特定数量的元素或范围内的元素替换链表的内容。
|
||||||
|
- `swap`:交换两个链表的内容。
|
||||||
|
|
||||||
|
3. **迭代器相关**:
|
||||||
|
- `begin`:返回指向第一个元素的迭代器。
|
||||||
|
- `end`:返回指向最后一个元素之后的位置的迭代器。
|
||||||
|
- `rbegin`:返回指向最后一个元素的反向迭代器。
|
||||||
|
- `rend`:返回指向第一个元素之前的位置的反向迭代器。
|
||||||
|
|
||||||
|
4. **容量**:
|
||||||
|
- `empty`:判断链表是否为空。
|
||||||
|
- `size`:返回链表中元素的数量。
|
||||||
|
- `max_size`:返回链表最大可容纳的元素数量。
|
||||||
|
|
||||||
|
5. **访问元素**:
|
||||||
|
- `front`:返回第一个元素的引用。
|
||||||
|
- `back`:返回最后一个元素的引用。
|
||||||
|
|
||||||
|
6. **修改容器**:
|
||||||
|
- `push_front`:在链表的开头插入一个元素。
|
||||||
|
- `pop_front`:移除链表的第一个元素。
|
||||||
|
- `push_back`:在链表的末尾插入一个元素。
|
||||||
|
- `pop_back`:移除链表的最后一个元素。
|
||||||
|
- `insert`:在指定位置插入一个或多个元素。
|
||||||
|
- `erase`:移除指定位置或范围内的一个或多个元素。
|
||||||
|
- `clear`:移除链表的所有元素。
|
||||||
|
|
||||||
|
7. **其他操作**:
|
||||||
|
- `splice`:将另一个链表的元素移动到当前链表的指定位置。
|
||||||
|
- `merge`:将两个有序链表合并为一个有序链表。
|
||||||
|
- `sort`:对链表进行排序。
|
||||||
|
- `reverse`:反转链表中的元素顺序。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1. **构造函数**:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 创建空链表
|
||||||
|
list<int> mylist;
|
||||||
|
|
||||||
|
// 用另一个链表初始化当前链表
|
||||||
|
list<int> otherlist = {1, 2, 3};
|
||||||
|
list<int> mylist2(otherlist);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **赋值和交换**:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// 赋值
|
||||||
|
mylist = otherlist;
|
||||||
|
|
||||||
|
// 用特定数量的元素或范围内的元素替换链表的内容
|
||||||
|
mylist.assign(5, 10); // 用5个值为10的元素替换mylist的内容
|
||||||
|
|
||||||
|
// 交换两个链表的内容
|
||||||
|
mylist.swap(otherlist);
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **迭代器相关**:
|
||||||
|
```cpp
|
||||||
|
// 使用迭代器访问元素
|
||||||
|
list<int>::iterator it = mylist.begin();
|
||||||
|
for (; it != mylist.end(); ++it) {
|
||||||
|
cout << *it << " ";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **容量**:
|
||||||
|
```cpp
|
||||||
|
// 判断链表是否为空
|
||||||
|
if (mylist.empty()) {
|
||||||
|
cout << "链表为空" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回链表中元素的数量
|
||||||
|
cout << "链表中元素的数量:" << mylist.size() << endl;
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **访问元素**:
|
||||||
|
```cpp
|
||||||
|
// 返回第一个元素的引用
|
||||||
|
int firstElement = mylist.front();
|
||||||
|
|
||||||
|
// 返回最后一个元素的引用
|
||||||
|
int lastElement = mylist.back();
|
||||||
|
```
|
||||||
|
|
||||||
|
6. **修改容器**:
|
||||||
|
```cpp
|
||||||
|
// 在链表的开头插入一个元素
|
||||||
|
mylist.push_front(100);
|
||||||
|
|
||||||
|
// 移除链表的第一个元素
|
||||||
|
mylist.pop_front();
|
||||||
|
|
||||||
|
// 在链表的末尾插入一个元素
|
||||||
|
mylist.push_back(200);
|
||||||
|
|
||||||
|
// 移除链表的最后一个元素
|
||||||
|
mylist.pop_back();
|
||||||
|
|
||||||
|
// 在指定位置插入一个或多个元素
|
||||||
|
list<int>::iterator it = mylist.begin();
|
||||||
|
advance(it, 2); // 移动迭代器到第三个位置
|
||||||
|
mylist.insert(it, 777);
|
||||||
|
|
||||||
|
// 移除指定位置或范围内的一个或多个元素
|
||||||
|
it = mylist.begin();
|
||||||
|
advance(it, 1); // 移动迭代器到第二个位置
|
||||||
|
mylist.erase(it);
|
||||||
|
|
||||||
|
// 移除链表的所有元素
|
||||||
|
mylist.clear();
|
||||||
|
```
|
||||||
|
|
||||||
|
7. **其他操作**:
|
||||||
|
```cpp
|
||||||
|
// 将另一个链表的元素移动到当前链表的指定位置
|
||||||
|
list<int> anotherlist = {9, 8, 7};
|
||||||
|
list<int>::iterator pos = mylist.begin();
|
||||||
|
advance(pos, 2); // 移动到第三个位置
|
||||||
|
mylist.splice(pos, anotherlist);
|
||||||
|
|
||||||
|
// 将两个有序链表合并为一个有序链表
|
||||||
|
list<int> sortedlist1 = {1, 3, 5};
|
||||||
|
list<int> sortedlist2 = {2, 4, 6};
|
||||||
|
sortedlist1.merge(sortedlist2);
|
||||||
|
|
||||||
|
// 对链表进行排序
|
||||||
|
mylist.sort();
|
||||||
|
|
||||||
|
// 反转链表中的元素顺序
|
||||||
|
mylist.reverse();
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
1. **构造函数**:
|
||||||
|
- `map()`:创建一个空的映射容器。
|
||||||
|
- `map(const map& other)`:拷贝构造函数,用另一个映射容器初始化当前映射容器。
|
||||||
|
|
||||||
|
2. **赋值和交换**:
|
||||||
|
- `operator=`:将一个映射容器赋值给另一个映射容器。
|
||||||
|
- `assign`:用特定数量的键值对或范围内的键值对替换映射容器的内容。
|
||||||
|
- `swap`:交换两个映射容器的内容。
|
||||||
|
|
||||||
|
3. **迭代器相关**:
|
||||||
|
- `begin`:返回指向第一个键值对的迭代器。
|
||||||
|
- `end`:返回指向最后一个键值对之后的位置的迭代器。
|
||||||
|
|
||||||
|
4. **容量**:
|
||||||
|
- `empty`:判断映射容器是否为空。
|
||||||
|
- `size`:返回映射容器中键值对的数量。
|
||||||
|
- `max_size`:返回映射容器最大可容纳的键值对数量。
|
||||||
|
|
||||||
|
5. **插入和访问元素**:
|
||||||
|
- `insert`:插入一个键值对或多个键值对。
|
||||||
|
- `erase`:移除指定键或范围内的键值对。
|
||||||
|
- `clear`:移除映射容器的所有键值对。
|
||||||
|
- `find`:查找指定键的迭代器。
|
||||||
|
- `operator[]`:访问映射容器中指定键对应的值。
|
||||||
|
|
||||||
|
6. **其他操作**:
|
||||||
|
- `count`:返回指定键在映射容器中出现的次数。
|
||||||
|
- `lower_bound`:返回第一个不小于指定键的迭代器。
|
||||||
|
- `upper_bound`:返回第一个大于指定键的迭代器。
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 创建空映射容器
|
||||||
|
map<int, string> mymap;
|
||||||
|
|
||||||
|
// 插入键值对
|
||||||
|
mymap.insert(pair<int, string>(1, "One"));
|
||||||
|
mymap.insert(make_pair(2, "Two"));
|
||||||
|
mymap[3] = "Three";
|
||||||
|
|
||||||
|
// 访问元素
|
||||||
|
cout << "Value at key 2: " << mymap[2] << endl;
|
||||||
|
|
||||||
|
// 查找元素
|
||||||
|
map<int, string>::iterator it = mymap.find(3);
|
||||||
|
if (it != mymap.end()) {
|
||||||
|
cout << "Key 3 found, value: " << it->second << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移除键值对
|
||||||
|
mymap.erase(2);
|
||||||
|
|
||||||
|
// 输出键值对数量
|
||||||
|
cout << "Size of map: " << mymap.size() << endl;
|
||||||
|
|
||||||
|
// 遍历输出所有键值对
|
||||||
|
for (auto& pair : mymap) {
|
||||||
|
cout << "Key: " << pair.first << ", Value: " << pair.second << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
1. **构造函数**:
|
||||||
|
- `set()`:创建一个空集合。
|
||||||
|
- `set(const set& other)`:拷贝构造函数,用另一个集合初始化当前集合。
|
||||||
|
|
||||||
|
2. **赋值和交换**:
|
||||||
|
- `operator=`:将一个集合赋值给另一个集合。
|
||||||
|
- `assign`:用特定数量的元素或范围内的元素替换集合的内容。
|
||||||
|
- `swap`:交换两个集合的内容。
|
||||||
|
|
||||||
|
3. **迭代器相关**:
|
||||||
|
- `begin`:返回指向第一个元素的迭代器。
|
||||||
|
- `end`:返回指向最后一个元素之后的位置的迭代器。
|
||||||
|
- `rbegin`:返回指向最后一个元素的反向迭代器。
|
||||||
|
- `rend`:返回指向第一个元素之前的位置的反向迭代器。
|
||||||
|
|
||||||
|
4. **容量**:
|
||||||
|
- `empty`:判断集合是否为空。
|
||||||
|
- `size`:返回集合中元素的数量。
|
||||||
|
- `max_size`:返回集合最大可容纳的元素数量。
|
||||||
|
|
||||||
|
5. **插入和访问元素**:
|
||||||
|
- `insert`:插入一个元素或多个元素。
|
||||||
|
- `erase`:移除指定元素或范围内的元素。
|
||||||
|
- `clear`:移除集合的所有元素。
|
||||||
|
- `find`:查找指定元素的迭代器。
|
||||||
|
- `count`:统计指定元素在集合中出现的次数。
|
||||||
|
|
||||||
|
6. **其他操作**:
|
||||||
|
- `lower_bound`:返回第一个不小于指定元素的迭代器。
|
||||||
|
- `upper_bound`:返回第一个大于指定元素的迭代器。
|
||||||
|
- `equal_range`:返回范围内与指定元素相等的上下界迭代器对。
|
||||||
|
|
||||||
|
下面是这些函数的示例用法:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <set>
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
// 创建空集合
|
||||||
|
set<int> myset;
|
||||||
|
|
||||||
|
// 插入元素
|
||||||
|
myset.insert(5);
|
||||||
|
myset.insert(10);
|
||||||
|
myset.insert(3);
|
||||||
|
|
||||||
|
// 查找元素
|
||||||
|
auto it = myset.find(10);
|
||||||
|
if (it != myset.end()) {
|
||||||
|
cout << "Element found: " << *it << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统计元素个数
|
||||||
|
int num = myset.count(5);
|
||||||
|
cout << "Number of 5s: " << num << endl;
|
||||||
|
|
||||||
|
// 移除元素
|
||||||
|
myset.erase(3);
|
||||||
|
|
||||||
|
// 输出元素
|
||||||
|
for (auto& num : myset) {
|
||||||
|
cout << num << " ";
|
||||||
|
}
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
## 万能头
|
||||||
|
|
||||||
|
`#include <bits/stdc++.h>` 这个头文件实际上是一个非标准的头文件,在一些编译器中使用它可以简化包含标准库头文件的操作,但并不推荐在生产环境中使用,因为它不是标准的 C++ 头文件。通常情况下,应该直接包含需要的具体标准库头文件,而不是依赖于这个非标准的头文件。
|
||||||
|
|
||||||
|
这个头文件通常包含了 C++ 标准库的所有头文件,但具体的内容可能因编译器和系统环境而异。一般来说,它会包含以下头文件:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream> // 标准输入输出库
|
||||||
|
#include <fstream> // 文件输入输出库
|
||||||
|
#include <string> // 字符串处理库
|
||||||
|
#include <ctime> // 时间处理库
|
||||||
|
#include <chrono> // 时间处理库(C++11)
|
||||||
|
|
||||||
|
#include <vector> // 向量容器库
|
||||||
|
#include <list> // 链表容器库
|
||||||
|
#include <map> // 映射容器库
|
||||||
|
#include <set> // 集合容器库
|
||||||
|
#include <algorithm> // 算法库
|
||||||
|
|
||||||
|
#include <random> // 随机数库
|
||||||
|
#include <iomanip> // 控制输出格式库
|
||||||
|
#include <sstream> // 字符串流库
|
||||||
|
#include <stdexcept> // 异常处理库
|
||||||
|
#include <functional> // 函数库
|
||||||
|
#include <iterator> // 迭代器库
|
||||||
|
#include <numeric> // 数值算法库
|
||||||
|
#include <utility> // 实用工具库
|
||||||
|
#include <tuple> // 元组库
|
||||||
|
#include <bitset> // 位集合库
|
||||||
|
#include <array> // 数组库
|
||||||
|
#include <deque> // 双端队列库
|
||||||
|
#include <queue> // 队列库
|
||||||
|
#include <stack> // 栈库
|
||||||
|
#include <unordered_map> // 无序映射库
|
||||||
|
#include <unordered_set> // 无序集合库
|
||||||
|
```
|
||||||
|
|
||||||
|
需要注意的是,不同的编译器和环境可能会有不同的实现,因此不能保证所有编译器都支持这种方式,并且不同编译器可能包含的头文件也会有所不同。因此,建议还是根据需要直接包含具体的标准库头文件。
|
|
@ -1,89 +1,56 @@
|
||||||
进制转换是计算机科学中的一个基本概念。不同的进制表示系统使用不同的基数(例如,二进制的基数是2,十进制的基数是10)来表示数字。以下是一些常见的进制及其转换方法:
|
#### **十进制转换成二进制**
|
||||||
|
|
||||||
### 常见进制
|
将一个十进制数转换为二进制数的方法是通过**模运算**(求余数)进行的。具体步骤如下:
|
||||||
- **二进制(Binary)**:基数为2,只使用数字 `0` 和 `1`。
|
|
||||||
- **八进制(Octal)**:基数为8,使用数字 `0` 到 `7`。
|
|
||||||
- **十进制(Decimal)**:基数为10,使用数字 `0` 到 `9`。
|
|
||||||
- **十六进制(Hexadecimal)**:基数为16,使用数字 `0` 到 `9` 和字母 `A` 到 `F`(或 `a` 到 `f`,代表10到15)。
|
|
||||||
|
|
||||||
### 进制转换方法
|
##### **步骤:**
|
||||||
|
1. 用十进制数除以2,记录余数。
|
||||||
|
2. 将商继续除以2,继续记录余数。
|
||||||
|
3. 重复此过程,直到商为0为止。
|
||||||
|
4. 将所有余数倒序排列,即为该十进制数对应的二进制表示。
|
||||||
|
|
||||||
#### 1. 二进制转十进制
|
##### **示例:**
|
||||||
将二进制数按权展开,从右到左依次乘以 2 的幂,然后求和。
|
将十进制数 `153` 转换为二进制:
|
||||||
- 例如,二进制 `1011` 转换为十进制:
|
|
||||||
\[
|
|
||||||
1 \times 2^3 + 0 \times 2^2 + 1 \times 2^1 + 1 \times 2^0 = 8 + 0 + 2 + 1 = 11
|
|
||||||
\]
|
|
||||||
|
|
||||||
#### 2. 十进制转二进制
|
|
||||||
通过不断将十进制数除以2,记录每次的余数,然后将余数倒序排列得到二进制数。
|
|
||||||
- 例如,十进制 `13` 转换为二进制:
|
|
||||||
|
|
||||||
- 13 ÷ 2 = 6,余数 1
|
|
||||||
|
|
||||||
- 6 ÷ 2 = 3,余数 0
|
|
||||||
|
|
||||||
- 3 ÷ 2 = 1,余数 1
|
|
||||||
|
|
||||||
- 1 ÷ 2 = 0,余数 1
|
|
||||||
|
|
||||||
- 结果:`1101`
|
|
||||||
|
|
||||||
#### 3. 二进制转八进制
|
|
||||||
将二进制数每三位分组(从右到左),然后将每组转换为对应的八进制数。
|
|
||||||
- 例如,二进制 `101011` 转换为八进制:
|
|
||||||
- 将二进制数分组为 `101` 和 `011`
|
|
||||||
- `101` = 5,`011` = 3
|
|
||||||
- 结果:`53`
|
|
||||||
|
|
||||||
#### 4. 八进制转二进制
|
|
||||||
将每个八进制数转换为三位二进制数,拼接得到最终的二进制数。
|
|
||||||
- 例如,八进制 `53` 转换为二进制:
|
|
||||||
- `5` = `101`,`3` = `011`
|
|
||||||
- 结果:`101011`
|
|
||||||
|
|
||||||
#### 5. 十进制转十六进制
|
|
||||||
通过不断将十进制数除以16,记录每次的余数(如果余数大于9,转换为对应的字母),然后将余数倒序排列得到十六进制数。
|
|
||||||
- 例如,十进制 `255` 转换为十六进制:
|
|
||||||
- 255 ÷ 16 = 15,余数 15(对应 `F`)
|
|
||||||
- 15 ÷ 16 = 0,余数 15(对应 `F`)
|
|
||||||
- 结果:`FF`
|
|
||||||
|
|
||||||
#### 6. 十六进制转十进制
|
|
||||||
将十六进制数按权展开,从右到左依次乘以16的幂,然后求和。
|
|
||||||
- 例如,十六进制 `1A3` 转换为十进制:
|
|
||||||
\[
|
|
||||||
1 \times 16^2 + 10 \times 16^1 + 3 \times 16^0 = 256 + 160 + 3 = 419
|
|
||||||
\]
|
|
||||||
|
|
||||||
### Python 实现进制转换
|
|
||||||
|
|
||||||
Python 提供了一些内置函数来轻松进行进制转换。
|
|
||||||
|
|
||||||
- **十进制转其他进制:**
|
|
||||||
|
|
||||||
``` python
|
|
||||||
# 十进制转二进制
|
|
||||||
print(bin(13)) # 输出:0b1101
|
|
||||||
|
|
||||||
# 十进制转八进制
|
|
||||||
print(oct(13)) # 输出:0o15
|
|
||||||
|
|
||||||
# 十进制转十六进制
|
|
||||||
print(hex(255)) # 输出:0xff
|
|
||||||
```
|
```
|
||||||
|
153 ÷ 2 = 76 余 1
|
||||||
- **其他进制转十进制:**
|
76 ÷ 2 = 38 余 0
|
||||||
|
38 ÷ 2 = 19 余 0
|
||||||
``` python
|
19 ÷ 2 = 9 余 1
|
||||||
# 二进制转十进制
|
9 ÷ 2 = 4 余 1
|
||||||
print(int('1101', 2)) # 输出:13
|
4 ÷ 2 = 2 余 0
|
||||||
|
2 ÷ 2 = 1 余 0
|
||||||
# 八进制转十进制
|
1 ÷ 2 = 0 余 1
|
||||||
print(int('15', 8)) # 输出:13
|
|
||||||
|
|
||||||
# 十六进制转十进制
|
|
||||||
print(int('ff', 16)) # 输出:255
|
|
||||||
```
|
```
|
||||||
|
将余数倒序排列,得到 `153` 的二进制表示为:`10011001`
|
||||||
|
|
||||||
通过理解这些转换方法,可以更好地掌握不同进制之间的关系和计算方法。
|
---
|
||||||
|
|
||||||
|
#### **二进制转换成十进制**
|
||||||
|
|
||||||
|
将二进制数转换为十进制数的方法是通过将每一位的数值乘以2的幂次方,然后求和。具体步骤如下:
|
||||||
|
|
||||||
|
##### **步骤:**
|
||||||
|
1. 从二进制数的最低位开始,每一位数乘以2的对应幂次方(从0开始计数)。
|
||||||
|
2. 将所有结果相加,得到的即是对应的十进制数。
|
||||||
|
|
||||||
|
##### **示例:**
|
||||||
|
将二进制数 `1001101` 转换为十进制:
|
||||||
|
```
|
||||||
|
1 * 2^6 = 64
|
||||||
|
0 * 2^5 = 0
|
||||||
|
0 * 2^4 = 0
|
||||||
|
1 * 2^3 = 8
|
||||||
|
1 * 2^2 = 4
|
||||||
|
0 * 2^1 = 0
|
||||||
|
1 * 2^0 = 1
|
||||||
|
```
|
||||||
|
求和得到:`64 + 8 + 4 + 1 = 77`
|
||||||
|
|
||||||
|
因此,二进制数 `1001101` 对应的十进制数为:`77`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
#### **总结**
|
||||||
|
- **十进制转二进制**:通过不断除以2并记录余数,最终将余数倒序排列。
|
||||||
|
- **二进制转十进制**:通过每一位乘以对应的2的幂次方,并将结果相加。
|
||||||
|
|
||||||
|
在计算机科学中,理解进制转换是非常重要的,因为不同进制在不同场景下有着广泛的应用。通过这些转换方法,能够轻松在不同进制之间切换,理解数字在不同进制下的表示方式,虽然表示不同,但数学意义是相同的。
|
|
@ -0,0 +1,163 @@
|
||||||
|
霍夫曼编码(Huffman Coding)是一种广泛使用的数据压缩算法,特别适用于无损数据压缩。它由David A. Huffman在1952年提出,基于信息论中的熵编码思想。
|
||||||
|
|
||||||
|
## 霍夫曼编码的基本原理
|
||||||
|
|
||||||
|
霍夫曼编码是一种**前缀编码**,即没有任何一个编码是其他编码的前缀。它通过构建一个**最优二叉树**,使得频率越高的字符编码越短,从而减少整体编码的长度,实现数据压缩。
|
||||||
|
|
||||||
|
### 主要步骤
|
||||||
|
|
||||||
|
1. **统计字符频率**:计算待压缩数据中每个字符出现的频率。
|
||||||
|
2. **构建优先队列**:将每个字符和其对应的频率作为一个节点,放入优先队列(最小堆)。
|
||||||
|
3. **构建霍夫曼树**:
|
||||||
|
- 从优先队列中取出两个频率最小的节点,作为左右子节点,构造一个新的父节点,父节点的频率为左右子节点频率之和。
|
||||||
|
- 将这个新的父节点插回优先队列中。
|
||||||
|
- 重复上述过程,直到队列中只剩下一个节点,这个节点就是霍夫曼树的根节点。
|
||||||
|
4. **生成编码**:
|
||||||
|
- 从霍夫曼树的根节点出发,给左边的分支标记为`0`,右边的分支标记为`1`。
|
||||||
|
- 从根节点到每个叶子节点(即字符节点)路径上的`0`和`1`组合,构成该字符的霍夫曼编码。
|
||||||
|
|
||||||
|
### 示例
|
||||||
|
|
||||||
|
假设我们有如下字符及其出现频率:
|
||||||
|
|
||||||
|
```
|
||||||
|
字符 | 频率
|
||||||
|
---------------
|
||||||
|
A | 5
|
||||||
|
B | 9
|
||||||
|
C | 12
|
||||||
|
D | 13
|
||||||
|
E | 16
|
||||||
|
F | 45
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1. 统计频率
|
||||||
|
初始优先队列:
|
||||||
|
|
||||||
|
```
|
||||||
|
(A, 5), (B, 9), (C, 12), (D, 13), (E, 16), (F, 45)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 构建霍夫曼树
|
||||||
|
|
||||||
|
- 取出频率最小的两个节点 `(A, 5)` 和 `(B, 9)`,构造一个新节点 `(AB, 14)`,插回队列。
|
||||||
|
- 队列更新为:`(AB, 14), (C, 12), (D, 13), (E, 16), (F, 45)`
|
||||||
|
- 取出 `(C, 12)` 和 `(D, 13)`,构造 `(CD, 25)`,插回队列。
|
||||||
|
- 队列更新为:`(AB, 14), (CD, 25), (E, 16), (F, 45)`
|
||||||
|
- 取出 `(AB, 14)` 和 `(E, 16)`,构造 `(ABE, 30)`,插回队列。
|
||||||
|
- 队列更新为:`(CD, 25), (ABE, 30), (F, 45)`
|
||||||
|
- 取出 `(CD, 25)` 和 `(ABE, 30)`,构造 `(CDEAB, 55)`,插回队列。
|
||||||
|
- 队列更新为:`(CDEAB, 55), (F, 45)`
|
||||||
|
- 最后将 `(CDEAB, 55)` 和 `(F, 45)` 合并为根节点 `(Root, 100)`。
|
||||||
|
|
||||||
|
最终霍夫曼树:
|
||||||
|
|
||||||
|
```
|
||||||
|
[Root, 100]
|
||||||
|
/ \
|
||||||
|
[F, 45] [CDEAB, 55]
|
||||||
|
/ \
|
||||||
|
[CD, 25] [ABE, 30]
|
||||||
|
/ \ / \
|
||||||
|
[C,12][D,13][A,5] [B,9] [E,16]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. 生成编码
|
||||||
|
|
||||||
|
从根节点到每个字符的路径生成编码:
|
||||||
|
|
||||||
|
```
|
||||||
|
F: 0
|
||||||
|
C: 100
|
||||||
|
D: 101
|
||||||
|
A: 1100
|
||||||
|
B: 1101
|
||||||
|
E: 111
|
||||||
|
```
|
||||||
|
|
||||||
|
### 霍夫曼编码的优点
|
||||||
|
|
||||||
|
- **无损压缩**:霍夫曼编码不会丢失任何信息,解码后的数据与原始数据完全一致。
|
||||||
|
- **高效性**:对于频率分布差异较大的字符集,霍夫曼编码能显著减少编码后的数据量。
|
||||||
|
- **简单实现**:算法简单,适合软件实现。
|
||||||
|
|
||||||
|
### 霍夫曼编码的应用
|
||||||
|
|
||||||
|
- **文件压缩**:如ZIP和RAR等文件压缩格式。
|
||||||
|
- **图像压缩**:如JPEG图像格式的压缩。
|
||||||
|
- **数据传输**:在数据传输中减少带宽占用。
|
||||||
|
|
||||||
|
### 示例代码 (C++)
|
||||||
|
|
||||||
|
以下是一个简单的C++实现霍夫曼编码的示例:
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
#include <iostream>
|
||||||
|
#include <vector>
|
||||||
|
#include <queue>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
// 定义霍夫曼树节点
|
||||||
|
struct HuffmanNode {
|
||||||
|
char data;
|
||||||
|
int freq;
|
||||||
|
HuffmanNode *left, *right;
|
||||||
|
HuffmanNode(char data, int freq) : data(data), freq(freq), left(NULL), right(NULL) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 比较器,用于优先队列
|
||||||
|
struct compare {
|
||||||
|
bool operator()(HuffmanNode* l, HuffmanNode* r) {
|
||||||
|
return l->freq > r->freq;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 打印霍夫曼编码
|
||||||
|
void printCodes(HuffmanNode* root, string str) {
|
||||||
|
if (!root) return;
|
||||||
|
if (root->data != '$') cout << root->data << ": " << str << "\n";
|
||||||
|
printCodes(root->left, str + "0");
|
||||||
|
printCodes(root->right, str + "1");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建霍夫曼树并打印编码
|
||||||
|
void HuffmanCodes(char data[], int freq[], int size) {
|
||||||
|
HuffmanNode *left, *right, *top;
|
||||||
|
priority_queue<HuffmanNode*, vector<HuffmanNode*>, compare> minHeap;
|
||||||
|
|
||||||
|
for (int i = 0; i < size; ++i)
|
||||||
|
minHeap.push(new HuffmanNode(data[i], freq[i]));
|
||||||
|
|
||||||
|
while (minHeap.size() != 1) {
|
||||||
|
left = minHeap.top();
|
||||||
|
minHeap.pop();
|
||||||
|
|
||||||
|
right = minHeap.top();
|
||||||
|
minHeap.pop();
|
||||||
|
|
||||||
|
top = new HuffmanNode('$', left->freq + right->freq);
|
||||||
|
top->left = left;
|
||||||
|
top->right = right;
|
||||||
|
|
||||||
|
minHeap.push(top);
|
||||||
|
}
|
||||||
|
|
||||||
|
printCodes(minHeap.top(), "");
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
char arr[] = { 'A', 'B', 'C', 'D', 'E', 'F' };
|
||||||
|
int freq[] = { 5, 9, 12, 13, 16, 45 };
|
||||||
|
int size = sizeof(arr) / sizeof(arr[0]);
|
||||||
|
|
||||||
|
HuffmanCodes(arr, freq, size);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 总结
|
||||||
|
|
||||||
|
霍夫曼编码是非常有效的无损压缩算法,特别适用于字符频率分布不均的情况。它的思想简单但非常实用,在许多领域得到了广泛应用。
|
|
@ -16,8 +16,22 @@ nav:
|
||||||
- 数学基础:
|
- 数学基础:
|
||||||
- 进制转换: basic/math/进制转换.md
|
- 进制转换: basic/math/进制转换.md
|
||||||
- 矩阵运算: basic/math/矩阵运算.md
|
- 矩阵运算: basic/math/矩阵运算.md
|
||||||
|
- C++库:
|
||||||
|
- 总结: C++/总.md
|
||||||
|
- iostream: C++/1.iostream.md
|
||||||
|
- fstream: C++/2.fstream.md
|
||||||
|
- sring: C++/3.string.md
|
||||||
|
- ctime: C++/4.ctime.md
|
||||||
|
- chrono: C++/5.chrono.md
|
||||||
|
- vector: C++/6.vector.md
|
||||||
|
- list: C++/7.list.md
|
||||||
|
- map: C++/8.map.md
|
||||||
|
- set: C++/9.set.md
|
||||||
|
- algorithm: C++/10.algorithm.md
|
||||||
- 数据结构:
|
- 数据结构:
|
||||||
- 二叉树: data_structure/二叉树.md
|
- 二叉树:
|
||||||
|
- 二叉树: data_structure/二叉树.md
|
||||||
|
- 霍夫曼编码: data_structure/霍夫曼编码.md
|
||||||
- 电子学会考级(机器人):
|
- 电子学会考级(机器人):
|
||||||
- 历史人物: 电子学会考级/历史事件及人物.md
|
- 历史人物: 电子学会考级/历史事件及人物.md
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue