文章目录
1.有关Linux指令
2.C++容器
类型 | 描述 | 特点 |
---|---|---|
1. vector | 动态数组,可以通过下表访问,可以快速访问,可以尾部插入删除 | 支持动态大小调整,适用于需要随机访问 和动态增删元素的场景 |
list | 双向链表,支持快速插入/删除 | 能够快速插入/删除,但是不支持随机访问元素 |
map | 键值对集合,基于红黑树的实现 | 存储键值对,按键自动排序,不允许重复键 |
2.1 vector
动态数组:
动态增加或减少,不用手动管理内存
#include <iostream>
#include <vector>
int main() {
// 创建一个空的 vector 容器
std::vector<int> myVector;
// 向 vector 容器尾部添加元素
myVector.push_back(3);
myVector.push_back(7);
myVector.push_back(12);
// 使用迭代器遍历 vector 容器并输出其中的元素
std::cout << "Vector elements: ";
for (auto it = myVector.begin(); it != myVector.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 获取 vector 容器的大小和访问特定位置的元素
std::cout << "Vector size: " << myVector.size() << std::endl;
std::cout << "Element at index 1: " << myVector[1] << std::endl;
// 修改特定位置的元素
myVector[2] = 20;
// 使用范围-based for 循环遍历 vector 并输出元素
std::cout << "Modified Vector elements: ";
for (int num : myVector) {
std::cout << num << " ";
}
std::cout << std::endl;
// 清空 vector 容器
myVector.clear();
// 检查 vector 是否为空
if (myVector.empty()) {
std::cout << "Vector is empty." << std::endl;
} else {
std::cout << "Vector is not empty." << std::endl;
}
return 0;
}
2.2 list
双向链表:
指向下一个节点和上一个节点。
#include <iostream>
#include <list>
int main() {
// 创建一个存储整数的 list 容器
std::list<int> myList;
// 在 list 尾部插入元素
myList.push_back(10);
myList.push_back(20);
myList.push_back(30);
// 在 list 头部插入元素
myList.push_front(5);
myList.push_front(15);
// 使用迭代器遍历 list 并输出元素
std::cout << "List elements: ";
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 删除 list 中特定的元素值
myList.remove(20);
// 使用范围-based for 循环遍历 list 并输出元素
std::cout << "List elements after removal: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
// 检查 list 是否为空
if (myList.empty()) {
std::cout << "List is empty." << std::endl;
} else {
std::cout << "List is not empty." << std::endl;
}
return 0;
}
2.3 map
自平衡红黑树:
用于储存键值对。
insert() : 向 map 中插入一个键值对。
erase() : 删除 map 中指定键的键值对。
find() : 查找指定键在 map 中的位置。
operator[] : 通过键访问对应的值。
size() : 返回 map 中键值对的数量。
empty() : 检查 map 是否为空。
clear() : 清空 map 中的所有键值对。
#include <iostream>
#include <map>
int main() {
// 创建一个存储 string 类型键和 int 类型值的 map 容器
std::map<std::string, int> myMap;
// 向 map 中插入键值对
myMap["Alice"] = 25;
myMap["Bob"] = 30;
myMap["Charlie"] = 20;
// 使用迭代器遍历 map 并输出键值对
std::cout << "Map elements: " << std::endl;
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
// 查找特定键在 map 中的位置
std::string searchKey = "Bob";
auto found = myMap.find(searchKey);
if (found != myMap.end()) {
std::cout << "Found " << searchKey << " with value: " << found->second <<
std::endl;
} else {
std::cout << searchKey << " not found in the map." << std::endl;
}
// 删除特定键的键值对
myMap.erase("Charlie");
// 检查 map 是否为空
if (myMap.empty()) {
std::cout << "Map is empty." << std::endl;
} else {
std::cout << "Map is not empty." << std::endl;
}
return 0;
}
3.红黑树
3.1 节点定义
enum Color{RED, BLACK};//节点颜色
//约定value唯一
template<class T>
struct RBTreeNode {//节点定义
RBTreeNode(const T& value = T(), Color color = RED)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(color)
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _value;
Color _color;
};
//节点一般为红色
3.2 结构
为了方便现关联式容器,在红黑树的实现中加入一个头结点。因为根节点必须是黑色,为了与根节点进行区分,将头节点给定为红色。让根节点的_parent指针域指向头节点,并且让头结点的_parent指针域指向红黑树根节点;_left指针域指向红黑树中最左侧节点,_right指针域指向红黑树中最右侧节点。
3.3 性质
红黑树保证最长路径不超过最短路径的二倍,因而近似平衡(最短路径就是全黑节点,最长路径就是一个红节点一个黑节点,当从根节点到叶子节点的路径上黑色节点相同时,最长路径刚好是最短路径的两倍)
1、节点是红色或黑色
2、根是黑色
3、叶子节点(外部节点,空节点)都是黑色,这里的叶子节点指的是最底层的空节点(外部节点),下图中的那些null节点才是叶子节点,null节点的父节点在红黑树里不将其看作叶子节点。
4、红色节点的子节点都是黑色
红色节点的父节点都是黑色
从根节点到叶子节点的所有路径上不能有 2 个连续的红色节点
5、从任一节点到叶子节点的所有路径都包含相同数目的黑色节点。
3.4 红黑树实现
enum Color{RED, BLACK};//节点颜色
template<class T>
struct RBTreeNode {//节点定义
RBTreeNode(const T& value = T(), Color color = RED)
: _left(nullptr)
, _right(nullptr)
, _parent(nullptr)
, _value(value)
, _color(color)
{}
RBTreeNode<T>* _left;
RBTreeNode<T>* _right;
RBTreeNode<T>* _parent;
T _value;
Color _color;
};
//约定value唯一
template<class T>
class RBTree {
typedef RBTreeNode<T> Node;
public:
RBTree() {
_head = new Node();
_head->_left = _head;
_head->_right = _head;
}
~RBTree() {
Destroy(_head->_parent);
delete _head;
_head = nullptr;
}
//
//插入
bool Insert(const T& value) {
Node*& root = GetRoot();
//1.按照二叉搜索树的方式插入
if (root == nullptr) {//空树
root = new Node(value, BLACK);
root->_parent = _head;
_head->_left = root;
_head->_right = root;
return true;
}
//查找插入位置
Node* cur = root;
Node* parent = cur->_parent;
while (cur) {
parent = cur;//保存其双亲
if (value < cur->_value) {
cur = cur->_left;
}
else if (value > cur->_value) {
cur = cur->_right;
}
else {//已存在
return false;
}
}
//插入新节点
cur = new Node(value);//默认插入节点为红色
if (value < parent->_value) {
parent->_left = cur;
}
else {
parent->_right = cur;
}
cur->_parent = parent;
//2.红黑树性质约束
while (parent != _head && parent->_color == RED) {//两个红色相连违法规则
Node* grandFather = parent->_parent;
if (parent == grandFather->_left) {
Node* uncle = grandFather->_right;
//情况一:叔叔节点存在且为红
if (uncle && uncle->_color == RED) {
parent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
//改变cur,继续向上调整
cur = grandFather;
parent = cur->_parent;
}
else {//情况二或三:叔叔节点为空,或叔叔节点存在且为黑
//因为情况三可以调整为情况二,所有先处理情况三
if (cur == parent->_right) {
//将情况三转换为情况二
RotateLeft(parent);
std::swap(parent, cur);
}
//情况二
parent->_color = BLACK;
grandFather->_color = RED;
RotateRight(grandFather);
}
}
else {//三种情况的对称情况-->解决方法相同
Node* uncle = grandFather->_left;
if (uncle && uncle->_color == RED) {//情况一
parent->_color = BLACK;
uncle->_color = BLACK;
grandFather->_color = RED;
//改变cur,继续向上调整
cur = grandFather;
parent = cur->_parent;
}
else {//情况二或三
if (cur == parent->_left) {//情况三
//调整为情况二
RotateRight(parent);
std::swap(cur, parent);
}
//情况二
parent->_color = BLACK;
grandFather->_color = RED;
RotateLeft(grandFather);
}
}
}
//最后根节点可能被调整为了红色,所以需要被改回黑色
root->_color = BLACK;
//3.更新头结点左右指针域--插入元素可能改变树的最值
_head->_left = MostLeft();
_head->_right = MostRight();
return true;
}
/
//查找
Node* Find(const T& value) {
Node*& root = GetRoot();
if (root == nullptr) return nullptr;
Node* cur = root;
while (cur) {
if (value < cur->_value) {
cur = cur->_left;
}
else if (value > cur->_value) {
cur = cur->_right;
}
else {
return cur;
}
}
return nullptr;
}
//检测是否是红黑树
bool IsRBTree() {
Node* root = GetRoot();
if (root == nullptr) return true;
if (root->_color == RED) {
//检测性质2
std::cout << "FALSE: 根节点为红色!" << std::endl;
return false;
}
int blackCount = 0;
Node* cur = root;
while (cur) {//统计一条路径黑色节点个数
if (cur->_color == BLACK) ++blackCount;
cur = cur->_left;
}
return _IsRBTree(root, blackCount, 0);
}
//中序遍历
void InOrder() {
_InOrder(GetRoot());
std::cout << std::endl;
}
//获取最值
int GetMax() {
return MostRight()->_value;
}
int GetMin() {
return MostLeft()->_value;
}
private:
//获取根节点
Node*& GetRoot() {
return _head->_parent;
}
//获取树的最值节点
Node* MostLeft() {//最左侧节点
Node*& root = GetRoot();
if (root == nullptr) return _head;
Node* cur = root;
while (cur->_left) {
cur = cur->_left;
}
return cur;
}
Node* MostRight() {//最右侧节点
Node*& root = GetRoot();
if (root == nullptr) return _head;
Node* cur = root;
while (cur->_right) {
cur = cur->_right;
}
return cur;
}
//左单旋
void RotateLeft(Node* parent) {
Node* subR = parent->_right;
Node* subRL = subR->_left;
parent->_right = subRL;
if (subRL) {//注意subrl可能为空
subRL->_parent = parent;
}
subR->_left = parent;
//跟新parent和subR的双亲节点
//先保存好parent原先的双亲节点
Node* pparent = parent->_parent;
parent->_parent = subR;
subR->_parent = pparent;
//处理上层节点
if (pparent == _head) {//已经更新到根节点
_head->_parent = subR;
}
else {
if (parent == pparent->_left) {
pparent->_left = subR;
}
else {
pparent->_right = subR;
}
}
}
//右单旋
void RotateRight(Node* parent) {
//其思想与左单旋相同
Node* subL = parent->_left;
Node* subLR = subL->_right;
parent->_left = subLR;
if (subLR) {
subLR->_parent = parent;
}
subL->_right = parent;
Node* pparent = parent->_parent;
parent->_parent = subL;
subL->_parent = pparent;
if (pparent == _head) {
_head->_parent = subL;
}
else {
if (parent == pparent->_left) {
pparent->_left = subL;
}
else {
pparent->_right = subL;
}
}
}
//检测是否满足红黑树特性
bool _IsRBTree(Node* root, const int blackNum, int count) {
if (root == nullptr) return true;
if (root->_color == BLACK) ++count;//统计路径内的黑色节点
//检测是否满足性质3
if (root->_parent != _head && root->_color == RED && root->_parent->_color == RED) {
std::cout << "FALSE: 存在两个相连的红色节点!" << std::endl;
return false;
}
if (root->_left == nullptr && root->_right == nullptr) {
//是叶子节点,此路径统计结束
return count == blackNum;//检测性质4
}
return _IsRBTree(root->_left, blackNum, count) && _IsRBTree(root->_right, blackNum, count);
}
//中序遍历
void _InOrder(Node*& root) {
if (root == nullptr) return;
_InOrder(root->_left);
std::cout << root->_value << " ";
_InOrder(root->_right);
}
//销毁
void Destroy(Node* root) {
if (root == nullptr) return;
Destroy(root->_left);
Destroy(root->_right);
delete root;
root = nullptr;
}
private:
Node* _head;
};
4.libev库定时器
4.1 定时器的初始化
//定时器初始化
struct ev_loop *loop = EV_DEFAULT;//通过 EV_DEFAULT 宏,获取到默认的事件循环。
ev_timer timer;
ev_timer_init(&timer, timerCallback, 0.0, 2.0); // 2秒定时器
timer.data = this->Mcfcb; // timer这个结构体中date的初始化
ev_timer_start(loop, &timer);//将定时器 timer 添加到事件循环 loop 中,并开始计时。如果定时器的延迟时间已过(在这个例子中是 0.0),它会立即触发;否则,它会在指定的延迟时间后触发。
ev_run(loop, 0);//启动事件循环。ev_run 函数会阻塞调用线程,直到没有更多活动的事件监听器或调用了 ev_break 函数。参数 0 表示使用默认的行为(即无限循环,直到没有更多事件或调用了 ev_break)。
ev_run(loop, int flags);
/*
libev 中常见的 flags 参数包括:
0 或 EVRUN_DEFAULT:这是默认值,表示事件循环将无限期地运行,直到没有活动的事件监听器为止。
EVRUN_ONCE:事件循环将只运行一次,处理当前所有待处理的事件,然后退出。
EVRUN_NOWAIT:事件循环将检查是否有待处理的事件,如果有则处理它们,并立即返回,而不会等待新事件的到来。*/
4.2 回调函数
void CUpReportPeriodData::timerCallback(struct ev_loop *loop, ev_timer *w, int revents)
{
struct C_FCB *self = static_cast<struct C_FCB*>(w->data);
std::cout<<"ZHW into CB"<<"FCB:"<<self->FCB<<","<<"flag:"<<self->flag<<","<<revFlag<<std::endl;
//如果FCB不为0,FCB--,再次更新控制域,发送报文
if (revFlag == 1) {
std::cout<<"zhw:"<<self->FCB<<std::endl;
ev_timer_stop(loop, w);
}
else if(revFlag==0)
{
if(self->FCB > 0)
{
self->FCB--;
std::cout<<"FCB:"<<self->FCB<<std::endl;
}
else if(self->FCB==0)
{
revFlag=1;
}
ev_timer_stop(loop, w);
}
}
void timerCallback(struct ev_loop *loop, ev_timer *w, int revents);
/*
一般revents这个函数不会被使用
struct ev_loop *loop:
这是一个指向当前事件循环的指针。libev 允许有多个事件循环实例,但这个函数通常只与触发它的那个事件循环相关。在这个回调函数中,可以使用这个指针来访问和操作该事件循环,比如添加新的watcher、修改定时器等。
ev_timer *w:
指向触发回调函数的定时器watcher的指针。通过这个指针,可以访问定时器watcher的所有属性,包括与定时器相关联的用户数据(通过 w->data 访问)。
revents:
一般这个函数不会被使用。
*/
5. CJSON
5.1 JSON数据封装
#include <stdio.h>
#include "cJSON.h"
int main(void)
{
cJSON* cjson_test = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_skill = NULL;
char* str = NULL;
/* 创建一个JSON数据对象(链表头结点) */
cjson_test = cJSON_CreateObject();
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cjson_test, "name", "mculover666");
/* 添加一条整数类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "age", 22);
/* 添加一条浮点类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cjson_test, "weight", 55.5);
/* 添加一个嵌套的JSON数据(添加一个链表节点) */
cjson_address = cJSON_CreateObject();
cJSON_AddStringToObject(cjson_address, "country", "China");
cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);
cJSON_AddItemToObject(cjson_test, "address", cjson_address);
/* 添加一个数组类型的JSON数据(添加一个链表节点) */
cjson_skill = cJSON_CreateArray();
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));
cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));
cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cjson_test, "student");
/* 打印JSON对象(整条链表)的所有数据 */
str = cJSON_Print(cjson_test);
printf("%s\n", str);
return 0;
}
5.2 JSON数据解析
/*
1.创建链表头指针
*/
cJSON* cjson_test = NULL;
/*
2.数据解析:
数据解析的API只有1个,返回的是链表头节点的地址
(cJSON *) cJSON_Parse(const char *value)
*/
cjson_test=(cJSON *) cJSON_Parse(str);
/*
3.根据键值对的名称从链表中去除对应的值,返回的是该键值对的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
*/
cJSON* cjson_get=(cJSON *) cJSON_GetObjectItem(cjson_test, "zhw");
/*
4.提取数组的数据:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
*/
char *message =
"{ \
\"name\":\"mculover666\", \
\"age\": 22, \
\"weight\": 55.5, \
\"address\": \
{ \
\"country\": \"China\",\
\"zip-code\": 111111\
}, \
\"skill\": [\"c\", \"Java\", \"Python\"],\
\"student\": false \
}";
cJSON* cjson_test = NULL;
cJSON* cjson_name = NULL;
cJSON* cjson_age = NULL;
cJSON* cjson_weight = NULL;
cJSON* cjson_address = NULL;
cJSON* cjson_address_country = NULL;
cJSON* cjson_address_zipcode = NULL;
cJSON* cjson_skill = NULL;
cJSON* cjson_student = NULL;
int skill_array_size = 0, i = 0;
cJSON* cjson_skill_item = NULL;
/* 解析整段JSO数据 */
cjson_test = cJSON_Parse(message);
if(cjson_test == NULL)
{
printf("parse fail.\n");
return -1;
}
/* 依次根据名称提取JSON数据(键值对) */
cjson_name = cJSON_GetObjectItem(cjson_test, "name");
cjson_age = cJSON_GetObjectItem(cjson_test, "age");
cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");
printf("name: %s\n", cjson_name->valuestring);
printf("age:%d\n", cjson_age->valueint);
printf("weight:%.1f\n", cjson_weight->valuedouble);
/* 解析嵌套json数据 */
cjson_address = cJSON_GetObjectItem(cjson_test, "address");
cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");
cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");
printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);
/* 解析数组 */
cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");
skill_array_size = cJSON_GetArraySize(cjson_skill);
printf("skill:[");
for(i = 0; i < skill_array_size; i++)
{
cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);
printf("%s,", cjson_skill_item->valuestring);
}
printf("\b]\n");
/* 解析布尔型数据 */
cjson_student = cJSON_GetObjectItem(cjson_test, "student");
if(cjson_student->valueint == 0)
{
printf("student: false\n");
}
else
{
printf("student:error\n");
}
return 0;
标签:cur,parent,程序员,cjson,菜鸟,编程,cJSON,root,节点
From: https://blog.csdn.net/zhwlit/article/details/141131402