关于c++:AR-正方形字符串

现请你输入指定大小的“ACM”字符串。特地地,咱们要求输入的字符串是正方形的(行数和列数相等)。输出输出的第一行是一个正整数N(N<=20),示意一共有N组数据,接着是N行数据,每行蕴含一个正整数M(M<=50),示意一行内有M个“ACM”相连。输入输入指定的正方形字符串。样例输出 Copy212样例输入 CopyACMACMACMACMACMACMACMACMACMACMACMACMACMACMACM 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//using namespace std;////int main(){// string a = "ACM";// int n = 0;// scanf("%d",&n);// while(n--){// int m = 0;// cin >> m;// for(int i = 0; i < (a.size() * m); i++){// for(int j = 0; j < m; j++){// cout << a ;// }// printf("\n");// }// }// return 0;//}

April 27, 2021 · 1 min · jiezi

关于c++:AP-奇偶位互换AQ-元音字母转换基础上机试题

给定一个长度为偶数位的0,1字符串,请编程实现串的奇偶位调换。输出输出蕴含多组测试数据。输出的第一行是一个整数C,示意有C测试数据。接下来是C组测试数据,每组数据输出均为0,1字符串,保障串长为偶数位(串长<=50)。输入请为每组测试数据输入奇偶位调换后的后果,每组输入占一行。样例输出 Copy201101100样例输入 Copy10011100 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//using namespace std;////int main(){// int n = 0;// scanf("%d",&n);// while(n--){// string a;// char q; // cin >> a;// for(int i = 0; i < (int)(a.size() - 1); i+=2){// q = a[i];// a[i] = a[i + 1];// a[i + 1] = q;// }// cout << a << endl;// }// return 0;//}给你一个字符串,现要求你对其进行解决,使得解决后的字符串满足如下要求:字符串外面的元音字母全副为大写;其余字母全副为小写。输出输出的第一行为一个正整数T(T<=20),示意测试数据的个数。每个输出的字符串只蕴含大写字母和小写字母。字符串长度不超过50。输入对于每一个测试数据,输入其对应的后果。每个后果占一行。样例输出 Copy4XYzapplicationqwcvbaeioOa样例输入 CopyxyzApplIcAtIOnqwcvbAEIOOA 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//using namespace std;////int main(){// int n = 0;// scanf("%d",&n);// while(n--){// string a;// cin >> a;// for(int i = 0; i < a.size(); i++){// if(a[i]=='A'||a[i]=='a'||a[i]=='E'||a[i]=='e'||a[i]=='I'||a[i]=='i'||a[i]=='O'||a[i]=='o'||a[i]=='U'||a[i]=='u'){// a[i] = toupper(a[i]);// }else {// a[i] = tolower(a[i]);// }// }// cout << a << endl;// }// return 0;//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AO-墓碑上的字符基础上机试题

考古学家发现了一座千年古墓,墓碑上有神秘的字符。通过认真钻研,发现原来这是开启古墓入口的办法。墓碑上有2行字符串,其中第一个串的长度为偶数,当初要求把第2个串插入到第一个串的正地方,如此便能开启墓碑进入墓中。输出输出数据首先给出一个整数n,示意测试数据的组数。而后是n组数据,每组数据2行,每行一个字符串,长度大于0,小于50,并且第一个串的长度必为偶数。输入请为每组数据输入一个能开启古墓的字符串,每组输入占一行。样例输出 Copy2CSJIBIABCMCLU样例输入 CopyCSBIJIACMCLUB 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//using namespace std;////int main(){// int n = 0;// scanf("%d",&n);// while(n--){// string a,b;// cin >> a >> b;w// int c = (int)a.size() / 2;// for(int i = 0; i < c; i++){// cout << a[i];// }// cout << b;// for(int i = c; i < (int)a.size(); i++){// cout << a[i];// } // cout << endl;// }// return 0;//}///*办法2-insert函数 * //int main(){// int n = 0;// scanf("%d",&n);// while(n--){// string a,b;// cin >> a >> b;// a.insert(a.size() / 2,b);// cout << a << endl; // }// return 0;//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AN-词组缩写基础上机试题

定义:一个词组中每个单词的首字母的大写组合称为该词组的缩写。比方,C语言里罕用的EOF就是end of file的缩写。 提醒:留神思考两个单词两头有多个空格的状况输出输出的第一行是一个整数T,示意一共有T组测试数据。接下来有T行,每组测试数据占一行,每行有一个词组,每个词组由一个或多个单词组成;每组的单词个数不超过10个,每个单词有一个或多个大写或小写字母组成;单词长度不超过10,由一个或多个空格分隔这些单词。输入请为每组测试数据输入规定的缩写,每组输入占一行。样例输出 Copy1end of file样例输入 CopyEOF 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//using namespace std;////int main(){// int n = 0;// scanf("%d",&n);// getchar(); // while(n--){// string str,s;// getline(cin,str);// if(str[0] >= 'a' && str[0] <= 'z'){// str[0] -= 32; // }// s = str.substr(0,1);//先把第一个单词小写字符变成大写// for(int i = 1; i < (int)str.size(); i++){//再把残余的单词首字母变成大写 // if(str[i - 1] == ' ' && (str[i] >= 'a' && str[i] <= 'z')){// str[i] -= 32;// s.append(str,i,1);//当第i-1个字符是空格,则取第i个字符加到字符s之后 // }else if(str[i - 1] == ' ' && (str[i] >= 'A' && str[i] <= 'Z')){// s.append(str,i,1);// }// }// cout << s << endl; // }// return 0;//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AM-过生日基础上机试题

小明出世在一个平年,他想晓得什么时候能够过生日,你能通知他吗?给你一个正整数Y,示意起始年份,和一个正整数N,你的工作是通知小明从Y年开始第N个平年是哪一年。留神:如果Y就是平年,那么第一个平年就是Y。输出输出的第一行为一个整数T,示意测试数据的组数。每一组输出蕴含两个正整数Y和N(1<=N<=10000)。输入对于每组输出,输入从Y年开始第N个平年是哪一年。样例输出 Copy32005 251855 122004 10000样例输入 Copy2108190443236 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//using namespace std;//bool Runyear(int y);//int main(){// int t = 0;// scanf("%d",&t);// while(t--){// int y = 0,n = 0;// scanf("%d %d",&y,&n);// int count = 0;// while(1){// if(Runyear(y)){// count++;// }// if(n == count){// break;// }// y++;// }// printf("%d\n",y);// } // return 0;//}//bool Runyear(int y){// if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0){// return true;// }else{// return false;// }//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AL-18岁生日基础上机试题

小明的18岁生日就要到了,他当然很开心,可是他忽然想到一个问题,是不是每个人从出世开始,达到18岁生日时所通过的天数都是一样的呢?仿佛并不全都是这样,所以他想请你帮忙计算一下他和他的几个敌人从出世达到18岁生日所通过的总天数,让他好来比拟一下。输出输出的第一行是一个数T,前面T行每行有一个日期,格局是YYYY-MM-DD。如我的生日是1988-03-07。输入T行,每行一个数,示意此人从出世到18岁生日所通过的天数。如果这个人没有18岁生日,就输入-1。样例输出 Copy11988-03-07样例输入 Copy6574 代码示例(本人写的,只能过平台,不完满) //#include<iostream>//using namespace std;//bool Runyear(int y);//int main(){// int t = 0;// char ch1,ch2;// scanf("%d",&t);// while(t--){// int year = 0,month = 0,day = 0,sum = 0;// scanf("%d%c%d%c%d",&year,&ch1,&month,&ch2,&day);// if(Runyear(year) && month == 2 && day == 29){// printf("-1\n");// }else{// for(int i = 1; i <= 18; i++){// if(Runyear(year) && mont h <= 2 || Runyear(year + 1) && month > 2){// sum += 366;// }else if(!Runyear(year) && month <= 2 || !Runyear(year + 1) && month > 2){// sum += 365;// }// year++;// }// printf("%d\n",sum);// } // }// return 0;//}////bool Runyear(int y){// if(y % 4 == 0 && y % 100 != 0 || y % 400 == 0){// return true;// }else{// return false;// }//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AK-镂空三角形基础上机试题

把一个字符三角形掏空,就能节俭资料老本,加重分量,但要害是为了谋求另一种视觉效果。在设计的过程中,须要给出各种花纹的资料和大小尺寸的三角形样板,通过电脑长期做进去,以便看看成果。输出每行蕴含一个字符和一个整数n(0<n<41),不同的字符示意不同的花纹,整数n示意等腰三角形的高。显然其底边长为2n-1。如果遇到@字符,则示意所做进去的样板三角形曾经够了。输入每个样板三角形之间应空上一行,三角形的两头为空。显然行末没有多余的空格。样例输出 CopyX 2A 7@样例输入 Copy(显示谬误,实为△型) XXXX A A AA AA A A A A AAAAAAAAAAAAAA代码示例(本人写的,只能过平台,不完满) //#include<iostream>//#include<string>//#include<string.h>//#include<cstring>//using namespace std;////int main(){// int n = 0;// char ch;ww// while((ch = getchar()) != '@'){// scanf("%d",&n); //此处能够换为 scanf("%d%*c",&n);%*c跳过一个字符// getchar();//scanf需循环输出,需应用gechar()吃掉上一次输出后暂存在buffer外面的(\n)空格 // for(int i = 0; i < n - 1; i++){//管制输入字符的行数(不含最初一行) // for(int j = i; j < n - 1; j++){ //管制左侧输入空格数 // printf(" ");// }// if(i == 0){//第一行输入一个ch并换行 // printf("%c\n",ch);// continue;// }else{//两头行输入 // printf("%c",ch);//输入一个ch; // for(int k = 2 * i - 1; k > 0; k--){//输入两头的空格数 // printf(" ");// }// printf("%c\n",ch);//输入左边的一个ch并换行 // }// }// for(int l = 2 * n - 1; l > 0; l--){//最初一行输入2*n - 1个ch // printf("%c",ch);// }// printf("\n\n");//图形之间空一行 // }// return 0;//} ...

April 27, 2021 · 1 min · jiezi

关于c++:AJ-放大的X基础上机试题

请你编程画一个放大的‘X’。如3*3的‘X’应如下所示:(图显示谬误,应为X型) X X XX X 5*5的‘X’如下所示:(图显示谬误,应为X型)X X X X X X XX X输出输出数据第一行是一个整数T,示意有T组测试数据。接下来有T行,每行有一个正奇数n(3<=n<=79),示意放大的规格。输入对于每一个n打印一个规格为 n*n 放大的‘X’,每组输入前面空一行。样例输出 Copy235样例输入 Copy(图显示谬误,应为X型)X X XX X X X X X X X XX X代码示例(本人写的,只能过平台,不完满) //#include<iostream>//using namespace std;////int main(){// int t = 0;// scanf("%d",&t);// while(t--){// int n = 0;// cin >> n; // for(int i = 1; i < (n + 1) / 2; i++){// for(int j = 1; j < i; j++){// printf(" ");// }// printf("X");// for(int k = 0; k < (n - 2 * i); k++){// printf(" ");// }// printf("X\n");// }// for(int i = (n + 1) / 2; i <= n; i++){// for(int j = n; j > i; j--){// printf(" ");// }// if(i == (n + 1) / 2){// printf("X\n");// continue;// } else{// printf("X");// }// for(int k = 0; k < 2 * i - n - 2; k++){// printf(" ");// }// printf("X\n");// }// printf("\n");// }// return 0;//} ...

April 27, 2021 · 1 min · jiezi

关于c++:C11-动态内存管理

C++内存动态内存、动态内存动态内存调配好后,程序运行过程中始终存在不会被开释,且一旦调配好,其内存大小就固定下来不能扭转,在编译和链接的阶段就会调配好。动态内存是程序运行过程中,依据程序的运行须要调配和开释,其大小可变。堆、栈堆和栈都是动态分配的,区别有两点: 栈是由编译器调配与开释;堆是程序通过调用malloc或new调配,调用free或delete开释。栈是线性构造;堆是链表构造。存储场景动态内存用来保留部分static对象、类static数据成员以及定义在任何函数之外的变量。栈内存用来保留定义在函数内的非static对象,存储在栈上,函数退出时,其占用内存被发出。调配在动态内存或栈内存中的对象由编译器主动创立和销毁。对于栈对象,仅在其定义的程序块运行时才存在;static对象在应用之前调配,在程序完结时销毁。 堆保留通过调用malloc或new失去的内存,不再须要时要显示地调用free或delete来开释堆内存容许程序员动静地申请所需空间,但也要求他们一旦不须要这些内存资源的时候就偿还他们。 内存相干谬误在程序运行的过程中,经常出现段谬误、内存继续增大等因为显式内存治理导致的问题,次要演绎为以下几点: 野指针:一些内存单元曾经开释,但之前指向它的指针还在应用。反复开释:程序试图开释曾经被开释过的内存单元。内存透露:没有开释不再应用的内存单元。缓冲区溢出:数组越界。不配对的new[]/delete[]针对上述问题中的1~3,C++规范中提供了智能指针来解决。 智能指针智能指针是基于RAII(Resource Acquisition Is Initialization)机制实现的类(模板),具备指针的行为(重载了operator*与operator->操作符)。当对象创立的时候,进行初始化;来到其作用域后,通过主动调用析构函数开释资源。 RAII (Resource Acquisition Is Initialization,资源获取就是初始化),是C++语言的一种治理资源、防止透露的习用法。C++规范保障任何状况下,已结构的对象最终会销毁,即它的析构函数最终会被调用。简略的说,RAII 的做法是应用一个对象,在其结构时获取资源,在对象生命期管制对资源的拜访使之始终保持无效,最初在对象析构的时候开释资源。 C++11新规范提供的两种智能指针区别在于治理底层指针的形式: shared_ptr 容许多个指针指向同一个对象;unique_ptr 独占所指向的对象;规范库还定义了一个名为weak_ptr的随同类,它是一种弱援用,指向shared_ptr所治理的对象。头文件:<memory>命名空间为:std unique_ptrunique_ptr”惟一“领有其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有挪动语义std::move()来实现)。 unique_ptr指针自身的生命周期:从unique_ptr指针创立时开始,直到来到作用域。来到作用域时,若其指向对象,则将其所指对象销毁(默认应用delete操作符,用户可指定其余操作)。unique_ptr指针与其所指对象的关系:在智能指针生命周期内,能够扭转智能指针所指对象,如创立智能指针时通过构造函数指定、通过reset办法从新指定、通过release办法开释所有权、通过挪动语义转移所有权。#include <iostream>#include <memory>#include <vector> using namespace std; struct Foo { Foo() {} ~Foo() {} void Print() { cout << "Foo" << endl; }}; int main() { Foo* p1 = new Foo(); unique_ptr<Foo> up1; // up1==nullptr// up1 = p1; // 编译谬误,不反对这样赋值 up1.reset(p1); // 替换治理对象,并开释之前治理的对象 p1 = nullptr; // unique_ptr<Foo> up2(up1); // 编译谬误,不反对这样结构 unique_ptr<Foo> up2(std::move(up1)); // up1所有权转移到up2。up1==nullptr up1.swap(up2); // up2与up1治理对象的指针替换。 up2==nullptr if (up1) { // up1 != nullptr up1->Print(); // unique_ptr重载了-> (*up1).Print(); // unique_ptr重载了* }// up2->Print(); // 谬误 up2 == nullptr, 必须先判断再调用 p1 = up1.get(); // get() 返回所治理对象的指针, up1持续持有其管理权 p1 = up1.release(); // release() 返回治理对象的指针,并开释管理权,up1==nullptr delete p1; unique_ptr<Foo> up3(new Foo()); up3.reset(); // 显示开释开释治理对象的内存,也能够这样做:up = nullptr; vector<unique_ptr<Foo>> v; unique_ptr<Foo> up4(new Foo());// v.push_back(up4); // 编译谬误,不反对这样拷贝 v.push_back(std::move(up4); // 只能up4放弃对其所有权,通过std::move()将所有权转移到容器中 return 0;}shared_ptrshared_ptr 基于“援用计数”模型实现, 多个shared_ptr对象能够领有同一个动静对象,并保护了一个共享的援用计数。当最初一个指向该对象的shared_ptr被销毁或者reset时,会主动开释其所指的对象,回收动静资源。销毁该对象时,应用默认的delete/delete[]表达式,或者是在结构 shared_ptr 时传入的自定义删除器(deleter),以实现个性化的资源开释动作。 ...

April 26, 2021 · 2 min · jiezi

关于c++:40组合总和II排序树层去重

40.组合总和II题目:给定一个数组 candidates和一个指标数target ,找出candidates 中所有能够使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使⽤⼀次。解集不能蕴含反复的组合。与39题比拟相似,39题题目:给定⼀个⽆反复元素的数组 candidates 和⼀个⽬标数 target ,找出 candidates 中所有能够使数字和为target 的组合。candidates 中的数字能够⽆限度反复被选取。解集不能蕴含反复的组合。 与39题不同之处在于:(1)本题每个组合中每个数字只能用一次;(2)本题candidates蕴含反复元素,并且解集中不能有反复组合 思路:去重,即应用过的元素不能反复选取。应用used数组进行去重,树层去重前须要先进行排序应用援用传递 used去重,工夫复杂度O(size)留神:援用传递和全局变量在回溯中的应用办法截然不同,区别:如果须要应用题目给的nums数组的大小信息进行初始化的话,就在入口函数对used数组进行初始化,并传递援用;如果used不须要进行初始化,间接应用全局变量即可class Solution {private: vector<vector<int>> result; vector<int> path; void backtracking(vector<int> &candidates, int target, int sum, int startIndex, vector<bool> &used) { if (sum == target) { result.push_back(path); return; } //应用used辨别是树枝还是树层 for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) { // used[i - 1] == true,阐明同⼀树⽀candidates[i - 1]使⽤过 // used[i - 1] == false,阐明同⼀树层candidates[i - 1]使⽤过 // 要对同⼀树层使⽤过的元素进⾏跳过 if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) { continue; } sum += candidates[i]; path.push_back(candidates[i]); used[i] = true; backtracking(candidates, target, sum, i + 1, used); // 和39.组合总和的区别1,这⾥是i + 1,每个数字在每个组合中只能使⽤⼀次 used[i] = false; sum -= candidates[i]; path.pop_back(); } }public: vector<vector<int>> combinationSum2(vector<int> &candidates, int target) { vector<bool> used(candidates.size(), false); // ⾸先把给candidates排序,让其雷同的元素都挨在⼀起。 sort(candidates.begin(), candidates.end()); backtracking(candidates, target, 0, 0, used); return result; }};每个递归层都创立一个used数组去重,空间复杂度较高O(size*size)class Solution {private: vector<vector<int>> result; vector<int> path; void backtracking(vector<int> &candidates, int target, int sum, int startIndex) { if (sum == target) { result.push_back(path); return; } vector<bool> used(candidates.size(), false); for (int i = startIndex; i < candidates.size() && sum + candidates[i] <= target; i++) { // 要对同⼀树层使⽤过的元素进⾏跳过 if (i > 0 && candidates[i] == candidates[i - 1] && used[candidates[i - 1]]) { continue; } sum += candidates[i]; used[candidates[i]] = true; path.push_back(candidates[i]); backtracking(candidates, target, sum, i + 1); sum -= candidates[i]; // 每层都领有本人的used数组,不必置false path.pop_back(); } }public: vector<vector<int>> combinationSum2(vector<int> &candidates, int target) { path.clear(); result.clear(); // ⾸先把给candidates排序,让其雷同的元素都挨在⼀起。 sort(candidates.begin(), candidates.end()); backtracking(candidates, target, 0, 0); return result; }};

April 26, 2021 · 2 min · jiezi

关于c++:c中istringstream及ostringstream超详细说明

前文说过,istringstream是继承于istream,ostringstream是继承于ostream,而他们应用的缓冲区类是stringbuf。 对于这些类之间的关系,有趣味能够去查看我之前的文章:c++规范输入输出流关系梳理 1. stringbuf类介绍stringbuf类缓冲区应用一个std::string类作为存储介质,而后依据结构时的读写模式来对string类进行读写。 1.1 stringbuf类构造函数小贴士:explicit用来避免由构造函数定义的隐式转换。//依据传入的读写标示结构一个领有空string的缓冲区,默认可读可写explicit basic_stringbuf(ios_base::openmode __mode = ios_base::in | ios_base::out): __streambuf_type(), _M_mode(__mode), _M_string(){ }//复制一个已有的string作为缓冲区内容,且依据__mode来指定可读、可写或者读写,默认可读可写explicit basic_stringbuf(const __string_type& __str, ios_base::openmode __mode = ios_base::in | ios_base::out): __streambuf_type(), _M_mode(), _M_string(__str.data(), __str.size()){ _M_stringbuf_init(__mode); }应用例子如下: #include <sstream>using namespace std;int main(){ stringbuf *buf = new stringbuf(ios_base::in);//结构一个可写的空stringbuf string str("my name is haha"); stringbuf *bufStr = new stringbuf(str, ios_base::out); if ( buf != nullptr ) { delete buf; } if ( bufStr != nullptr ) { delete bufStr; } return 0;}1.2 str函数str函数原型如下: ...

April 26, 2021 · 2 min · jiezi

关于c++:C内存管理4内存分配的重载示例

类内重载 operator new/operator delete 示例#include <iostream>#include <string>using namespace std;class Foo {public: int _id; int _data; int _num;public: // 如果没有重载的成员函数则调用全局版本 static void *operator new(size_t size); static void operator delete(void *pdead, size_t size); static void *operator new[](size_t size); static void operator delete[](void *pdead, size_t size); Foo() : _id(0) { cout << "default ctor.this=" << this << " id=" << _id << endl; } Foo(int i) : _id(i) { cout << "ctor. this=" << this << " id=" << _id << endl; } // virtual ~Foo() { cout << "dtor. this=" << this << " id=" << _id << endl; }};void *Foo::operator new(size_t size){ Foo *p = (Foo*)malloc(size); cout << "Foo::operator new(), size=" << size << "\t return: " << p << endl; return p;}void Foo::operator delete(void *pdead, size_t size){ cout << "Foo::operator delete(), pdead= " << pdead << " size= " << size << endl; free(pdead);}void *Foo::operator new[](size_t size){ Foo *p = (Foo*)malloc(size); cout << "Foo::operator new[](), size=" << size << "\t return: " << p << endl; return p;}void Foo::operator delete[](void *pdead, size_t size){ cout << "Foo::operator delete[](), pdead= " << pdead << " size= " << size << endl; free(pdead);}int main(){ cout << "sizeof(Foo)=" << sizeof(Foo) << endl; cout << "============" << endl; Foo *p = new Foo(7); delete p; cout << "============" << endl; Foo* pArray = new Foo[5]; delete [] pArray; return 0;}当 Foo 无虚析构函数时,输入: ...

April 24, 2021 · 5 min · jiezi

关于c++:C11的conditionvariable实现WaitForSingleObject功能

在多线程程序开发中,咱们有时会心愿期待其余线程完结后在继续执行某些工作。比方常见的,主线程须要期待其余所有子线程完结后再完结程序。在Windows下咱们个别应用WaitForSingleObject,或者WaitForMultipleObjects来实现,它们都会期待Object被设置为有信号时才返回的。这样咱们在主线程完结的中央wait,在子线程完结的中央设置信号,就能实现上述性能。然而这个API是Windows下的,无奈跨平台应用。还好C++11为咱们带来了多线程的反对。在C++11中咱们能够通过mutex和condition_variable来达到类似成果。上面是我写的小例子。 #include <iostream>#include <thread>#include <mutex>#include <condition_variable>#include <chrono>std::mutex mx;std::mutex g_mx;std::condition_variable g_cv;uint32_t g_thread_num = 0;void function_1(){ for (int i = 0; i < 100; i++) { std::lock_guard<std::mutex> loc(mx); std::cout << "from function 1:" << i << std::endl; } std::unique_lock<std::mutex> guard(g_mx); --g_thread_num; g_cv.notify_one(); guard.unlock(); std::cout << "function 1 end" << std::endl;}void function_2(){ for (int i = 0; i < 100; i++) { std::lock_guard<std::mutex> loc(mx); std::cout << "from function 2:" << i << std::endl; } std::unique_lock<std::mutex> guard(g_mx); --g_thread_num; g_cv.notify_one(); guard.unlock(); std::cout << "function 2 end" << std::endl;}void function_3(){ for (int i = 0; i < 100; i++) { std::lock_guard<std::mutex> loc(mx); std::cout << "from function 3:" << i << std::endl; } std::this_thread::sleep_for(std::chrono::seconds(3)); std::unique_lock<std::mutex> guard(g_mx); --g_thread_num; g_cv.notify_one(); guard.unlock(); std::cout << "function 3 end" << std::endl;}int main(){ std::thread t1(function_1); { std::unique_lock<std::mutex> guard(g_mx); ++g_thread_num; } t1.detach(); std::thread t2(function_2); { std::unique_lock<std::mutex> guard(g_mx); ++g_thread_num; } t2.detach(); std::thread t3(function_3); { std::unique_lock<std::mutex> guard(g_mx); ++g_thread_num; } t3.detach(); for (int i = 0; i < 100; i++) { std::lock_guard<std::mutex> loc(mx); std::cout << "from function main:" << i << std::endl; } { std::unique_lock<std::mutex> guard(g_mx); g_cv.wait(guard, []() { return g_thread_num == 0; }); } std::cout << "main end" << std::endl; return 0;}例子写的很简略,其实能够简略封装下会更好用。大家如果有什么更好的解决方案也欢送交换。

April 24, 2021 · 1 min · jiezi

关于c++:C内存管理4内存分配的重载

C++ 应用程序,内存调配的路径默认的内存调配形式 对于 ① 处的阐明(成员函数的自定义内存调配)可通过自定义内存池的形式进步应用效率,如更快的调配速度,更省的内存空间(省去 cookie)对于 ② 处的阐明 (重载全局空间的内存管理策略)全局的内存管理策略会被多处无意或无心的应用,代码会大范畴的收到影响,可重载但少见容器的内存调配形式 容器并未采纳默认的内存治理形式,而采纳内存分配器从新实现内存调配与结构被从新封装彼此拆散内存开释与析构被从新封装彼此拆散Foo *p = (Foo)operator new(sizeog(Foo)); // 内存申请new(p) Foo(x); // 结构...p->~Foo(); // 析构operator delete(p); // 内存开释全局重载 ::operator new / ::operator deletevoid *myAlloc(size_t size){ return malloc(size);}void myFree(void *ptr){ return free(ptr);}void *operator new(size_t size){ cout << "global new" << endl; return myAlloc(size);}void *operator new[] (size_t size){ cout << "global new[]" << endl; return myAlloc(size);}void operator delete(void *ptr) noexcept{ cout << "global delete" << endl; myFree(ptr);}void operator delete[] (void *ptr) noexcept{ cout << "global delete" << endl; myFree(ptr);}operator new 的一种实现 [...\vc98\src\newop.cpp]void *operator new(size_t size, const std::nothrow_t &_THROW0()){ // try to allocate size bytes void *p; while ((p == malloc(size)) == 0) { // buy more memory or return null pointer __TRY_BEGIN if (_callnewh(size) == 0) break; __CATCH(std::bad_alloc) return (0); __CATCH_END; } return (p);}operator delete 的一种实现 [...\vc98\src\delop.cpp]void __cdecl operator delete(void *p) _THROW0(){ // free an allocated object free(p);}类内重载 operator new/operator deleteclass Foo {public: void *operator new(size_t); void operator delete(void*, size_t[可选]);}------------------------------Foo *p = new Foo;==>try { void *mem = operator new(sizeof(Foo)); p = static_cast<Foo*>(sizeof(Foo)); p->Foo::Foo();}------------------------------delete p;==>p->~Foo();operator delete(p);类内重载 operator new, operator delete 的成员函数具备 static 属性非动态成员函数须要对象(暗藏的this指针)实现调用,而 operator new 被调用时示意对象正在被创立类内重载 operator new[]/operator delete[]class Foo {public: void *operator new[](size_t); void operator delete[](void*, size_t[可选]); // ...};------------------------------Foo *p = new Foo[N];==>try { void *mem = operator new(sizeof(Foo) * N + 4); p = static_cast<Foo*>(mem); p->Foo::Foo(); // N 次}------------------------------delete[] p;==>p->~Foo(); // N 次operator delete(p);类内重载 operator new[], operator delete[] 的成员函数具备 static 属性非动态成员函数须要对象(暗藏的this指针)实现调用,而 operator new[] 被调用时示意对象正在被创立

April 24, 2021 · 2 min · jiezi

关于c++:C内存管理3placement-new

placement new 容许咱们将 object 构建于 allocated memory 中;没有所谓 placement delete, 因为 placement new 基本没调配 memory;亦或称说与 placement new 对应的 operator delete 为 placement delete。 #include <new>char *buf = new char[sizeof(Complex) * 3];Complex *pc = new(buf) Complex (1, 2);// ...delete[] buf; Complex *pc = new(buf) Complex (1, 2);编译器转换为 ==>Complex *pc;try { void* mem = operator new (sizeof(Complex), buf); // allocate pc = static_cast<Complex*>(mem); // cast pc->Complex::Complex(1, 2); // construct}catch(std::bad_alloc) { // 若 allocation 失败就不执行 constructor }void* mem = operator new (sizeof(Complex), buf);源码实现 ==>void *operator new (size_t, viud *loc){ return loc;}

April 24, 2021 · 1 min · jiezi

关于c++:C内存管理2array-new-arraydelete

资源透露危险剖析示例一: // 1. 唤起 三次构造函数// 2. 无奈藉由参数进行初始化,因而 Complex 须要有默认构造函数Complex *pca = new Complex[3];......// 3. 唤起三次析构函数delete[] pca; 示例二: // 1. 唤起 三次构造函数// 2. 无奈藉由参数进行初始化,因而 string须要有默认构造函数string *psa = new string[3];......// 3. 唤起一次析构函数delete psa;问:没对数组外面的每个对象调用析构函数(示例二),会有什么影响呢? 对 class without pointer 可能没有影响(不会有资源泄露)对 class with pointer 通常有影响 (会有资源泄露)阐明: 示例一中,当 delete pca; pca 指向的全副内存空间将会被平安开释示例二中,当 delete psa; psa 指向的全副内存空间将会被平安开释,但因为 string 实现中蕴含指针指向一段堆空间中申请的内存空间以存储字符串,而数组元素数量的析构函数未被全副对应调用,导致字符串空间未被开释,因而会造成资源泄露总结 new, delete ; new [], delete [] 须要配对应用数组元素的结构与析构程序#include <iostream>using namespace std;class A{public: int id; A() : id(0) { cout << "default ctor.this=" << this << " id=" << id << endl; } A(int i) : id(i) { cout << "default ctor.this=" << this << " id=" << id << endl; } ~A() { cout << "dtor.this=" << this << " id=" << id << endl; }};constexpr size_t size = 3;int main(){ // A 必须有默认构造函数 // 默认结构函数调用三次,[0] 先于 [1] 先于 [2] A *buf = new A[size]; A* tmp = buf; cout << "buf=" << buf << " tmp=" << tmp << endl; for (size_t i=0; i < size; ++i) { // placement new: 在指定的地址结构对象 new (tmp++)A(i); } cout << "buf=" << buf << " tmp=" << tmp << endl; // 析构函数三次被调用(秩序逆反,[2] 先于 [1] 先于 [0]) delete[] buf; return 0;}总结 ...

April 24, 2021 · 2 min · jiezi

关于c++:百度C工程师的那些极限优化内存篇

导读:在百度看似简简单单的界面前面,是遍布全国的各个数据中心里,运行着的海量C++服务。如何晋升性能,升高延时和老本就成了百度C++工程师的必修功课。随同着优化的深刻攻坚,诞生并积攒下来一系列的性能优化实践和计划,其中不乏一些冷门但精美实用的教训和技巧。本文从内存拜访角度,收集总结了一些具备通用意义的典型案例,分享进去和大家学习交换。 1  背景在百度看似简简单单的界面前面,是遍布全国的各个数据中心里,运行着的海量C++服务。对C++的重度利用是百度的一把双刃剑,学习老本平缓,指针类谬误定位难、扩散性广另很多开发者望而生畏。然而在另一方面,语言层引入的额定开销低,对底层能力可操作性强,又可能为谋求极致性能提供优异的实际环境。 因而,对百度的C++工程师来说,把握底层个性并加以利用来领导利用的性能优化,就成了一门必要而且必须的技能。长此以往,百度工程师就将这种谋求极致的性能优化,逐步积淀成了习惯,甚至造成了对技术的信奉。上面咱们就来盘点和分享一些,在性能优化的征途上,百度C++工程师积攒下来的实践和实际,以及那些为了谋求极致,所挖掘的『奇技淫巧』。 2  重新认识性能优化作为程序员,大家或多或少都会和性能打交道,尤其是以C++为主的后端服务工程师,然而每个工程师对性能优化概念的了解在细节上又是千差万别的。上面先从几个优化案例动手,建设一个性能优化相干的感性认识,之后再从原理角度,形容一下本文所讲的性能优化的切入角度和办法根据。 2.1  从字符串解决开始2.1.1  string as a buffer为了调用底层接口和集成一些第三方库能力,在调用界面层,会存在对C++字符串和C格调字符串的交互场景,典型是这样的: size\_t some\_c\_style\_api(char\* buffer, size\_t size);void some\_cxx\_style\_function(std::string& result) { // 首先扩大到短缺大小 result.resize(estimate\_size); // 从c++17开始,string类型反对通过data获得十分量指针 auto acture\_size = some\_c\_style\_api(result.data(), result.size()); // 最终调整到理论大小 result.resize(acture\_size);}这个办法存在一个问题,就是在首次resize时,string对estimate\_size内的存储区域全副进行了0初始化。然而这个场景中,理论的无效数据其实是在some\_c\_style\_api外部被写入的,所以resize时的初始化动作其实是冗余的。在交互buffer的size较大的场景,例如典型的编码转换和压缩等操作,这次冗余的初始化引入的开销还是相当可观的。 为了解决这个问题,大概从3年前开始,曾经有人在继续尝试推动规范改良。 http://www.open-std.org/jtc1/...注:在这个问题上应用clang + libc++的同学有福,较新版本的libc++中曾经非标实现了resize\_default\_init性能,能够开始尝鲜应用。 在规范落地前,为了可能在百度外部(目前宽泛应用gcc8和gcc10编译器)提前应用起来,咱们专门制作了实用于gcc的resize\_uninitialized,相似于下面的性能,在百度,能够这样编码: size\_t some\_c\_style\_api(char\* buffer, size\_t size);void some\_cxx\_style\_function(std::string& result) { auto\* buffer = babylon::resize\_uninitialized(result, estimate\_size); auto acture\_size = some\_c\_style\_api(buffer, result.size()); result.resize(acture\_size);}2.1.2  split string理论业务中,有一个典型场景是一些轻schema数据的解析,比方一些规范分隔符,典型是'\_'或者'\t',简略宰割的分列数据(这在日志等信息的粗加工解决中分外常见)。因为场景极其单纯,可能的算法层面优化空间个别认为较小,而理论实现中,这样的代码是广为存在的: std::vector<std::string> tokens;// boost::splitboost::split(token, str, \[\] (char c) {return c == '\\t';});// absl::StrSplitfor (std::string\_view sv : absl::StrSplit(str, '\\t')) { tokens.emplace\_back(sv);}// absl::StrSplit no copyfor (std::string\_view sv : absl::StrSplit(str, '\\t')) { direct\_work\_on\_segment(sv);}boost版本宽泛呈现在新工程师的代码中,接口灵便,流传度高,然而理论业务中效率其实并不优良,例如和google优化过的absl相比,其实有倍数级的差距。尤其如果工程师没有留神进行单字符优化的时候(间接应用了官网例子中的is\_any\_of),甚至达到了数量级的差距。进一步地,如果联动思考业务状态,个别典型的宰割后处理是能够做到零拷贝的,这也能够进一步升高冗余拷贝和大量长期对象的创立开销。 ...

April 22, 2021 · 3 min · jiezi

关于c++:WonderTrader架构详解之四浅谈平台对策略的支持

前言 《WonderTrader架构详解》系列文章,上一篇介绍了WonderTrader的信号执行的解决机制。平台的数据和信号执行机制曾经实现当前,接下来就是要思考如何生成信号了,也就是说策略如何编写。在解决策略编写的问题之前,首先要解决的就是平台要反对哪些策略。本文作为系列文章的第四篇,将针对WonderTrader对不同策略的反对展开讨论。 往期文章列表: WonderTrader架构详解之一——整体架构WonderTrader架构详解之二——从数据说起WonderTrader架构详解之三——信号与执行策略的分类 如何对策略进行分类,这个问题自身就很简单。很多介绍策略的书都会对策略进行一个分类,而后再开展介绍,然而每位作者都有本人的分类规范,所以每本书里介绍的策略分类也不尽相同。笔者已经拜读过几本,比拟有代表性的就是丁鹏的《量化投资——策略与技术》。这本书里大抵分为以下几个大类策略: 量化选股策略 多因子选股格调轮动选股行业轮动选股资金流选股动量反转选股……量化择时策略 趋势追踪择时市场情绪择时无效资金择时牛熊线择时Hurst指数择时...股指期货套利 期现套跨期套商品期货套利 期现套跨期套跨种类套利跨市场套利统计套利 配对交易股指套利融券套利外汇套利…… 然而对于量化平台来说,以上的分类都没有理论的意义。为什么呢?因为平台须要思考的是从技术角度如何对策略进行分类,而下面展现的策略分类都是从策略实现的逻辑和指标交易种类来进行分类的。 那么从技术实现的角度角度对策略进行分类要思考哪些问题呢? 行情数据的需要 从技术实现的角度来说,策略对数据的需要会引起很大的架构变动。比如说有些策略须要K线数据进行信号的计算,有些策略须要tick数据进行信号的计算,而有些策略同时须要K线数据和tick数据进行计算。平台在对策略凋谢接口的时候,就必须要思考到不同策略的数据需要。以趋势追踪策略为例,趋势追踪策略个别应用K线数据进行信号计算,然而在理论经营的过程中可能也须要利用实时的tick数据进行一个止盈止损逻辑的触发。平台在思考策略对数据的需要的同时,还须要思考在实盘过程中对策略所须要的数据如何进行缓存、如何进行实时的更新、以及如何传递给策略。信号执行的需要 策略对信号执行的需要,次要是执行响应速度和执行办法两个方面。对于很多日频调仓的策略来说,如多因子选股,个别状况下须要执行较大数量的调仓,对于执行的要求绝对较低,次要依据执行当天的均匀成交价作为一个参考基准,在一天内执行实现即可。而对于高频策略来说,单次执行的数量个别较少,须要立刻响应信号,在微秒级的提早下收回下单指令。而对于日内调仓的策略来说,单次执行的数量介于多因子和高频之间,对执行的要求也介于二者之间:一方面不心愿太大的提早,造成太大的滑点;另一方面须要也不可能以一天的成交均价作为基准,反而以下一个周期的开盘价作为成交价的参考基准。重算调度的需要 不论是何种策略,都须要一个机制驱动策略进行外围逻辑的计算。个别策略采纳的驱动形式次要是事件驱动和工夫驱动。工夫驱动,就是到了某个规定确定的工夫节点,驱动策略的外围逻辑进行重算。而事件驱动,咱们这里次要指的是K线闭合、K线开始、tick进入等事件。对于基于K线的日内趋势策略,个别须要在K线闭合的时候进行重算;而基于日K线的跨日趋势策略,则能够在开盘后到第二天收盘前进行重算;对于高频策略,则须要在每一笔tick到来的时候进行重算。跟踪标的的需要 跟踪标的的需要,次要思考的是跟踪的标的数量。如果你是做国内商品期货的,个别状况下,即便沉闷种类的主力合约全副跟踪,也只有40多个。而如果要跟踪沪深300的成分股、或者中证1000的成分股,那么如何设计才可能满足这样的利用场景,就是一个十分重要的问题。 综合下面几点,WonderTrader最终从技术实现的角度将策略分类成三个大类: 日内趋势类策略 日内趋势类策略,不是说策略只做日内交易,而是指策略会在日内触发信号,即在交易工夫内的某个工夫点触发信号。这类策略个别策略逻辑都是基于分钟K线数据进行外围逻辑的重算,利用tick数据进行止盈止损逻辑的计算。这类策略信号强度个别较高,不须要在执行的过程中依据订单的成交状况动静调整执行策略,可能容忍肯定水平的滑点,属于笔者在上一篇《信号与执行》中提到的“重逻辑轻执行”的策略。高频类策略 高频类策略,次要利用tick数据触发外围逻辑重算,对于信号响应的提早特地敏感,而且呈现信号当前,还须要依据订单的执行状况实时调整信号。这类策略的盈利空间很小所以对滑点的容忍度非常低,因而须要平台解决信号要尽可能地快,属于笔者在上一篇《信号与执行》中提到的“轻逻辑重执行”的策略。选股类策略 选股类策略,次要指的是日频以上周期调仓的策略,以多因子选股策略为代表,所以称为选股类策略。选股类策略,计算量大、计算工夫长,资金容量大,信号的特点是数量大,对滑点也不敏感。因而这类策略对于信号执行的成果容忍度也比拟高。不过正是因为这些特点,选股类策略如果搭配更适合的执行算法的话,对绩效能够晋升好几个点。对于大规模的资金容量来说,几个点的晋升也是一个十分可观的数字了。因而选股类策略,对执行算法的需要,就体现须要在更长的时间跨度上,找到更适合的交易点。WonderTrader的策略引擎 针对下面的策略技术分类,WonderTrader提供了三种策略引擎,以满足不同类型策略的需要。 CTA引擎 CTA引擎(类名WtCtaEngine),是WonderTrader针对日内趋势类策略设计的策略引擎,因为最早次要是针对CTA策略的,所以引擎名字就依照CTAEngine沿用下来了。 CTA策略在初始化的时候,会向CTA引擎订阅一个主K线,也能够同时订阅其余周期或者其余种类的K线。CTATicker收到行情当前,会依据工夫戳判断是否有K线闭合,如果有K线闭合,则触发策略的on_bar回调;如果闭合的是主K线,则查看是否所有的K线都曾经闭合;如果所有K线都曾经闭合,则触发策略的on_schedule回调进行策略外围逻辑的重算。如果呈现新的信号,则由CTAEngine汇总当前失去一个指标组合,再丢给执行管理器。执行管理器则将指标组合散发到各个执行通道,并由执行通道转成交易指令下达交易所。如果没有K线闭合或者K线闭合事件曾经解决实现,再触发策略的on_tick回调进行风控运算。 下图展现了CTA引擎中策略信号产生和执行的根本流程: HFT引擎 HFT引擎(类名WtHftEngine),是WonderTrader针对高频类策略设计的策略引擎。 HFT策略在初始化的时候,会向HFT引擎订阅一些合约的tick数据(如果通道反对的话,还能够订阅股票Level2数据),也能够订阅K线数据(不分主次)。HFTTicker收到行情当前,和CTA引擎不同的是,不会查看是否有K线闭合,而是间接触发策略的on_tick回调进行外围逻辑的重算。如果呈现新的信号,则间接通过交易通道收回交易指令。交易通道收到订单回报当前,会触发策略的on_order回调,如果策略有调整,则再收回新的交易指令。交易通道收到成交回报当前,会触发策略的on_trade回调,如果有相应的调整,又会收回新的交易指令。 下图展现了HFT引擎中策略信号产生和执行的根本流程: SEL引擎 SEL引擎(类名WtSelEngine),是WonderTrader针对选股类策略设计的策略引擎。因为选股类策略具备计算工夫长和计算量大的特点,并且通常是非交易工夫重算,所以SEL引擎实质上是一个工夫驱动的引擎。SEL引擎在设计的时候,要兼顾盘后计算和盘中计算两种需要,所以整个策略的重算是异步的。 SEL策略在初始化的时候,会向SEL引擎注册一个工夫驱动的策略。例如每天、每周、每月、每年指定的工夫触发重算,或者在交易工夫内每N分钟触发重算。在非交易工夫,因为没有行情接入,所以SEL引擎会依据本地工夫进行比拟,如果满足工夫条件,则触发策略的on_schedule回调进行外围逻辑的重算。如果注册的是交易工夫内的分钟线驱动,则通过SELTicker依据最新接管到的tick数据的工夫戳进行行情工夫同步,再触发策略的on_schedule回调进行外围逻辑的重算。如果有新的信号产生,则将指标组合丢给执行管理器。执行管理器则将指标组合散发到各个执行通道,执行通道会依据预设的算法交易进行拆单,并依据算法交易的逻辑在适合的实机会由执行通道转成交易指令下达交易所。 策略引擎现状的思考 后面介绍的WonderTrader不同的策略引擎的外围逻辑,曾经基本上能够笼罩市面上绝大部分策略调度的需要了。不过对于WonderTrader的策略引擎,笔者目前也存在一些疑虑。 HFT引擎对做市策略的反对 WonderTrader为了简化策略的逻辑,将策略的信号全副简化为买和卖。这样的简化,省掉了策略研发人员很多事件,比方:到底是买开还是买平?可平今仓残余多少、可平昨仓残余多少?当初买进的信号要拆成几个单子下进来?单笔下单的最大数量是多少?WonderTrader会把这些问题全副主动解决掉。比方:主动依据持仓确定是开仓还是平仓;主动依据预设的开平计划管制是平仓还是锁仓;主动依据可平数量拆分信号为多个订单。笔者置信绝大部分策略研发都会喜爱这样的简化,然而有一种策略可能会例外:做市策略。 做市策略个别的交易逻辑是在高位挂一个卖单,同时在低位挂一个买单,通过赚取两个单子之间的价差获取收益。问题在于,如果依照后面提到的一些主动解决计划,开始的时候是没有持仓的,同时挂的两个订单成交当前,持仓呈一多一空的状态,净头寸依然为0。如果这个时候呈现第二轮信号,就可能会呈现一些问题:买入订单会平掉空头的头寸,卖出订单会平掉多头的头寸。持仓的状况会如此重复,对于有些策略来说可能就不大适应了。 SEL引擎回测的难点 SEL引擎回测的难点起源于实盘中的策略的外围逻辑是异步执行的。比如说,t0时刻触发了策略的重算,重算工夫较长,始终继续到t1时刻。如果在生产环境下,间接在t1时刻执行新的信号即可。然而对于回测环境下,如果采纳同步回测的形式,回测时执行的价格为t0时刻的p0;如果采纳异步形式回测的话,回测行情回放的工夫又比生产环境快很多,到了t1时刻行情早已回放超过t1了,这时的执行价格变成了t2时刻的p2了,而不是t1时刻的p1。综合来说,SEL引擎回测后果的参考价值要比CTA引擎小很多。笔者也已经思考过,依据重算工夫t,计算t0+t时刻,找到该时刻的pt作为执行价格。这样的解决形式看起来比拟可行,然而也会引入更多的复杂度。 WonderTrader的布局 后面大抵介绍了WonderTrader各个策略引擎的一些根本状况。通过多方调研,笔者认为从底层来说WonderTrader反对的利用场景曾经足够丰盛了。然而在WonderTrader使用和推广的过程中,笔者也发现了一些预计之外的需要,这些也是前面WonderTrader欠缺的方向。 欠缺交易接口 目前来说,WonderTrader只反对一般的交易接口,能够抽象地概括为现金业务的交易接口。实际上还有其余业务类型,例如ETF申赎、期权询价报价、期权行权、两融业务等等。笔者前面会依据理论的须要逐渐的欠缺这些接口,让WonderTrader反对更多的业务类型。 适配更多种类 从WonderTrader目前的推广的反馈来看,不少用户心愿利用WonderTrader进行数字货币的交易。而后因为数字货币7×24小时交易的特点,目前WonderTrader还不能很好的反对。抛开技术细节不谈,本源还是在于WonderTrader原本设计的指标就是针对惯例的交易市场的,即便是NYMEX这样的交易所,每天也有1个小时的休市工夫。当然,这不能成为WonderTrader固步自封的理由,所以将来WonderTrader也会逐笔欠缺对不同市场和不同种类的适配。 为应用层凋谢性能扩大接口 WonderTrader在设计的时候,为了保障底层的执行效率,所有的性能组件都是用C++开发的。功夫不负有心人,WonderTrader零碎外部提早,在个别台式机上也能达到10微秒以内。然而在推广的过程中,笔者发现局部用户实际上是基于wtpy做开发的,并没有C++的开发能力。然而这样的用户想要做一些二次开发的话,都须要C++底层做同步调整,于是这些用户只能望而生畏了。鉴于这样的状况,笔者也重复斟酌了一下,打算在将来思考将行情接入模块、交易模块向应用层子框架开发。这样的益处就是,一些二次开发的门槛也同步升高了,能够丰盛WonderTrader的生态,给不同需要的人群提供更丰盛的抉择。 结束语 本文对WonderTrader的策略引擎的介绍就到此结束了,心愿本文能给一些想要应用WonderTrader进行策略开发的敌人一些指引。笔者程度无限,不免有错漏之处,还请各位朋友多多包涵斧正。下一篇,笔者将针对不同的策略引擎,来具体介绍WonderTrader不同类型策略的回测的机制,望各位读者届时多多捧场。 最初再安利一下WonderTrader WonderTrader旨在给各位量化从业人员提供更好的轮子,将技术相干的货色都封装在平台中,打造更高效的底层框架,力求给策略研发带来更好的策略开发和交易体验。 WonderTrader的github地址:https://github.com/wondertrad... WonderTrader官网地址:https://wondertrader.github.io wtpy的github地址:https://github.com/wondertrad... 市场有危险,投资需谨慎。以上陈说仅作为对于历史事件的回顾,不代表对将来的观点,同时不作为任何投资倡议。

April 19, 2021 · 1 min · jiezi

关于c++:c中ostream类的超详细说明

依据前文,ostream类是c++规范输入流的一个基类,本篇具体介绍ostream类的次要成员函数用法。 1.ostream的构造函数从ostream头文件中截取一部分对于构造函数的申明和定义,如下: public://explicit用来避免由构造函数定义的隐式转换explicit basic_ostream(__streambuf_type* __sb) { this->init(__sb); }protected: basic_ostream() { this->init(0); }#if __cplusplus >= 201103L // Non-standard constructor that does not call init() basic_ostream(basic_iostream<_CharT, _Traits>&) { } basic_ostream(const basic_ostream&) = delete; basic_ostream(basic_ostream&& __rhs) : __ios_type() { __ios_type::move(__rhs); } // 27.7.3.3 Assign/swap basic_ostream& operator=(const basic_ostream&) = delete; basic_ostream& operator=(basic_ostream&& __rhs) { swap(__rhs); return *this; }能够看到ostream类的默认构造函数是爱护类型,而带参数的构造函数则是私有的,依据public和protected的性能,咱们要定义一个ostream对象,必须要在参数中传入streambuf类型的指针才能够,否则会报编译谬误。 一个可用的例子如下: #include <iostream>#include <fstream>using namespace std;int main(){ filebuf buf; if ( buf.open("/proc/self/fd/1", ios::out) == nullptr ) { cerr << "stdout open failed" << endl; return -1; } ostream out(&buf); return 0;}与istream一样,因为streambuf类型的构造函数是爱护类型,不能间接应用,所以须要应用它的继承者stringbuf或者filebuf,这里应用了filebuf,并且咱们输入错误信息没有应用cout,这里应用了ostream定义的另外一个实例cerr,会输入错误信息到规范谬误输入。 ...

April 19, 2021 · 3 min · jiezi

关于c++:C-变量声明与定义的各种规则

申明与定义拆散Tips:变量能且仅能被定义一次,然而能够被屡次申明。为了反对分离式编译,C++将定义和申明辨别开。其中申明规定了变量的类型和名字,定义除此性能外还会申请存储空间并可能为变量赋一个初始值。 extern如果想申明一个变量而非定义它,就应用关键字extern并且不要显式地初始化变量: extern int i; // 申明i而非定义iextern int i = 1; // 定义i, 这样做对消了extern的作用 static当咱们在C/C++用static润饰变量或函数时,次要有三种用处: 部分动态变量内部动态变量/函数类内静态数据成员/成员函数其中第三种只有C++中有,咱们后续在面向对象程序设计中再探讨,这里只探讨动态部分/全局变量。 1. 动态局部变量在局部变量后面加上static说明符就形成动态局部变量,例如: // 申明部分动态变量static int a;static int array[5] = {1, 2, 3, 4, 5};动态局部变量在函数内定义,但不像主动变量那样当函数被调用时就存在,调用完结就隐没,动态变量的生存期为整个源程序动态变量的生存期尽管为整个源程序,然而作用域与主动变量雷同,即只能在定义该变量的函数内应用该变量,退出函数后尽管变量还存在,但不可能应用它对根本类型的动态局部变量如果在申明时未赋初始值,则零碎主动赋0值;而对一般局部变量不赋初始值,那么它的值是不确定的依据动态局部变量的特点,它的生存期为整个源程序,在来到定义它的函数(作用域)但再次调用定义它的函数时,它又可持续应用,而且保留了前次被调用后留下的值。因而,当屡次调用一个函数且要求在调用之间保留某些变量的值时,可思考采纳动态局部变量,尽管用全局变量也能够达到上述目标,但全局变量有时会造成意外的副作用,因而最好采纳部分动态变量。例如: #include <iostream>void foo() { int j = 0; // 一般局部变量 static int k = 0; // 动态局部变量 ++j; ++k; printf("j:%d, k:%d\n", j, k);}int main(void){ for (int i = 1; i <= 5; i++) { foo(); }}// 输入:j:1, k:1j:1, k:2j:1, k:3j:1, k:4j:1, k:52. 动态全局变量(C++废除,用匿名命名空间代替)Tips:对于全局变量,不论是否被static润饰,它的存储区域都是在动态存储区,生存期为整个源程序。只不过加上static后限度这个全局变量的作用域只能在定义该变量的源文件内。全局变量(内部变量)的申明之前加上static就形成了动态的全局变量,全局变量自身就是动态存储变量,动态全局变量当然也是动态存储形式。这两者在存储形式上并无不同,这两者的区别在于非动态全局变量的作用域是整个源程序。当一个源程序由多个源程序组成时,非动态的全局变量在各个源文件中都是无效的,而动态全局变量则限度了其作用域,即只在定义该变量的源文件内无效,在同一源程序的其余源文件中不能应用它。 ...

April 18, 2021 · 3 min · jiezi

关于c++:C五花八门的C初始化规则

总结初始化的概念:创立变量时赋予它一个值(不同于赋值的概念)类的构造函数管制其对象的初始化过程,无论何时只有类的对象被创立就会执行构造函数如果对象未被用户指定初始值,那么这些变量会被执行默认初始化,默认值取决于变量类型和定义变量的地位无论何时只有类的对象被创立就会执行构造函数,通过显式调用构造函数进行初始化被称为显式初始化,否则叫做隐式初始化应用等号(=)初始化一个类变量执行的是拷贝初始化,编译器会把等号右侧的初始值拷贝到新创建的对象中去,不应用等号则执行的是间接初始化传统C++中列表初始化仅能用于一般数组和POD类型,C++11新规范将列表初始化利用于所有对象的初始化(然而内置类型习惯于用等号初始化,类类型习惯用构造函数圆括号显式初始化,vector、map和set等容器类习惯用列表初始化)初始化不等于赋值初始化的含意是创立变量时赋予其一个初始值,而赋值的含意是把对象的以后值擦去,并用一个新值代替它。 C++定义了初始化的好几种不同模式,例如咱们定义一个int变量并初始化为0,有如下4种形式: int i = 0;int i = {0};int i{0};int i(0);默认初始化与值初始化Tips:C不容许用户自定义默认值从而进步性能(减少函数调用的代价),C++默认也不做初始化从而进步性能,然而C++提供了构造函数让用户显式设置默认初始值。有个例外是把全局变量初始化为0仅仅在程序启动时会有老本,因而定义在任何函数之外的变量会被初始化为0。如果定义变量时没有指定初始值,则变量会被默认初始化或值初始化,此时变量被赋予了默认值,这个默认值取决于变量类型和定义地位。 #include <iostream>class Cat { public: std::string name; Cat() = default;};int main() { Cat cat1; // 默认初始化 Cat cat2 = Cat(); // 显式申请值初始化}1. 内置类型的默认初始化Tips:倡议初始化每一个内置类型的变量,起因在于定义在函数外部的内置类型变量的值是未定义的,如果试图拷贝或者以其余模式拜访此类值是一种谬误的编程行为且很难调试。如果内置类型的变量未被显式初始化,它的值由定义的地位决定。定义于任何函数体之外的变量会被初始化为0,定义在函数体外部的内置类型变量将不被初始化(uninitialized),一个未被初始化的内置类型变量的值时未定义的,如果试图拷贝或以其余模式拜访此类值将引发谬误。 #include <iostream>int global_value; // 默认初始化为0int main() { int local_value; // 应用了未初始化的局部变量 int* new_value = new int; std::cout << "new_value:" << *new_value << std::endl; // 未定义 std::cout << "global_value:" << global_value << std::endl; // 0 std::cout << "local_value:" << local_value << std::endl; // 未定义, 且会报warning return 0;}2. 类类型的默认初始化定义一个类变量然而没有指定初始值时,会应用默认构造函数来初始化,所以没有默认构造函数的类不能执行默认初始化。定义于任何函数体之外的类变量会先进行零初始化再执行默认初始化,定义在函数体外部的类变量会间接执行默认初始化。 ...

April 18, 2021 · 6 min · jiezi

关于算法-数据结构:PAT甲级2020年冬季考试-73-File-Path

7-3 File Path (25 分)The figure shows the tree view of directories in Windows File Explorer. When a file is selected, there is a file path shown in the above navigation bar. Now given a tree view of directories, your job is to print the file path for any selected file. Input Specification:Each input file contains one test case. For each case, the first line gives a positive integer N (≤10^3), which is the total number of directories and files. Then N lines follow, each gives the unique 4-digit ID of a file or a directory, starting from the unique root ID 0000. The format is that the files of depth d will have their IDs indented by d spaces. It is guaranteed that there is no conflict in this tree structure. ...

April 13, 2021 · 2 min · jiezi

关于算法-数据结构:PAT甲级2020年冬季考试-72-Subsequence-in-Substring

7-2 Subsequence in Substring (25 分)A substring is a continuous part of a string. A subsequence is the part of a string that might be continuous or not but the order of the elements is maintained. For example, given the string atpaaabpabtt, pabt is a substring, while pat is a subsequence. Now given a string S and a subsequence P, you are supposed to find the shortest substring of S that contains P. If such a solution is not unique, output the left most one. ...

April 13, 2021 · 2 min · jiezi

关于c++:PAT甲级2020年冬季考试-71-The-Closest-Fibonacci-Number

7-1 The Closest Fibonacci Number (20 分)The Fibonacci sequence Fn is defined by $$F_{n+2} =F_{n+1} +F_n \;\;for \;\;n≥0, with \;\;F_0 =0 \;\;and\;\; F_1 =1.$$ The closest Fibonacci number is defined as the Fibonacci number with the smallest absolute difference with the given integer N. Your job is to find the closest Fibonacci number for any given N. Input Specification:Each input file contains one test case, which gives a positive integer N (≤10^8). ...

April 13, 2021 · 2 min · jiezi

关于c++:PAT甲级2020年冬季考试-74-Chemical-Equation

7-4 Chemical Equation (30 分)A chemical equation is the symbolic representation of a chemical reaction in the form of symbols and formulae, wherein the reactant entities are given on the left-hand side and the product entities on the right-hand side. For example, $$CH_4+2O_2=CO_2+2H_2O$$ means that the reactants in this chemical reaction are methane and oxygen: CH4 and O2, and the products of this reaction are carbon dioxide and water: CO2 and H2O. ...

April 13, 2021 · 4 min · jiezi

关于c++:c标准输入输出流关系梳理

输入输出是每一种编程语言必不可少的局部,c++也不例外,上面咱们就来阐明c++的规范输入输出的前世今生。 1.首先说一下iostream和iostream.h的区别#include<iostream> // 这个就是1998年标准化当前的规范头文件,应用时须要应用申明命名空间std#include<iostream.h> // 这个就是标准化以前的头文件,外面的函数以及类都是全局的iostream是当初C++中规定的规范,目标在于使C++代码用于移植和混合嵌入时不受扩展名.h的限度,防止因为.h而造成的额定的解决和批改。 iostream蕴含的基本功能和对应的iostream.h雷同,iostream中定义的内容都在命名空间std中,而iostream.h是为了对c语言进行兼容,所以将规范输入输出性能都定义在全局空间中,他们的应用办法也是不一样的,另外举荐间接应用iostream,毕竟iostream.h是很多年前的老物件了,规范c++中曾经明确不实用了,当前有可能被淘汰。 留神:在标准化的过程中,库中有些局部的细节被批改了,所以旧头文件和新头文件中的实体不肯定齐全对应这里看一下他们应用上的不同: #include<iostream.h>或者是 #include<iostream>using namespace std;可见但凡要应用规范c++输入输出,都须要加上using namespace std。 2.输入输出流关系梳理要弄清楚c++的输入输出流,必须要从源头找起,从安装文件外面找出输入输出流相干的头文件,大略列一下,相干头文件有以下这些: istream,能够看到istream头文件是申明了basic_istream模板类ostream,ostream头文件是申明了basic_ostream模板类iostream,iostream只是申明了一个istream对象和三个ostream对象,这一点前面会阐明iosfwd,iosfwd头文件外面申明了所有输入输出类的模板类的一个实例fstream,fstream外面申明了basic_filebuf模板类、basic_ifstream模板类、basic_ofstream模板类iomainip,iomainip外面申明了一些带参数的操纵算子sstream,sstream外面申明了basic_stringbuf模板类、basic_istringstream模板类、basic_ostringstream模板类streambuf,streambuf外面申明了basic_streambuf模板类下面说到iosfwd对输入输出的类模板做了实例化,咱们截取一段代码,如下: /// Base class for @c char streams. typedef basic_ios<char> ios; //根底类 /// Base class for @c char buffers. typedef basic_streambuf<char> streambuf; /// Base class for @c char input streams. typedef basic_istream<char> istream; /// Base class for @c char output streams. typedef basic_ostream<char> ostream; /// Base class for @c char mixed input and output streams. typedef basic_iostream<char> iostream; /// Class for @c char memory buffers. typedef basic_stringbuf<char> stringbuf; /// Class for @c char input memory streams. typedef basic_istringstream<char> istringstream; /// Class for @c char output memory streams. typedef basic_ostringstream<char> ostringstream; /// Class for @c char mixed input and output memory streams. typedef basic_stringstream<char> stringstream; /// Class for @c char file buffers. typedef basic_filebuf<char> filebuf; /// Class for @c char input file streams. typedef basic_ifstream<char> ifstream; /// Class for @c char output file streams. typedef basic_ofstream<char> ofstream; /// Class for @c char mixed input and output file streams. typedef basic_fstream<char> fstream;为了叙述不便,后续咱们间接应用以上实例类来代指模板类,上面用一张图阐明这些类之间的关系: ...

April 13, 2021 · 1 min · jiezi

关于electron:开发Electron不小心接触到C经过一周多的时间终于摸索出

为啥要写二进制编译 exe程序,CMake minGW练习教程?前阵子,钻研了一下electron,想开发一款离线图片压缩桌面程序。写的过程中发现程序在,windows环境下运行没问题,然而在linux 和 macOS会有问题。常常排查发现是node依赖的第三方插件问题(只给了 exe程序)。所以开始着手钻研,如何通过 c++之类的源码,编译出别离对应 Wndows Linux MacOS的二进制应用程序。通过一周的工夫查阅大量的材料(google),最终得出上面的流程。 当初只是学会了,编译Linux 和windows的应用程序,然而我想要是 在一个零碎环境下别离编译出Windows Linux MacOS。目前只接触到了 穿插编译这个货色。正在着手学习,摸索出残缺无坑的流程再持续发相干文章。 本文存储于github 工具筹备OS: windows 10 CMake: https://cmake.org/ Visual Studio 2019: https://visualstudio.microsof... 下载 libpng-1.6.35 源码 Download the libpng-1.6.35 source codelibpng: https://github.com/glennrp/li... 解压 libpng-1.6.35.tar.gz directory|-libpng-1.6.35关上 zlib.props 文件,查看依赖zlib的版本libpng > projects -> vstudio -> zlib.props line 34 <ZLibSrcDir>..\..\..\..\zlib-1.2.8</ZLibSrcDir>下载 zlib-1.2.8 源码 Download the zlib-1.2.8 source codezlib : https://github.com/madler/zli... 解压 zlib-1.2.8.tar.gz directory|-libpng-1.6.35|-zlib-1.2.8编译 zlib-1.2.8关上 CMake gui点击 Browse Source 抉择 zlib-1.2.8 点击 Browse Build 抉择 zlib-1.2.8/build 目录点击 Configure(生成CMake Cahce) 抉择 Visual Studio和 x64点击 Finish 期待输入音讯 Configuring done ...

April 8, 2021 · 2 min · jiezi

关于node.js:node-本地二进制包装器无缝的用作本地依赖

本地二进制文件包装器次要是为了解决,不同环境下须要应用不同的本地二进制文件。 为什么不依据以后环境在线下载呢? 因为网络下载会有墙和编译的等不可抗力问题。所以自行下载好放起来,再通过包装器应用. const path = require('path');const fs = require('fs-extra');class Local_Bin_Wrapper { constructor(){ this.map = new Map(); } /** * 配置包门路 * @param url 本地二进制包的绝对路径 * @param platfrom 包的平台 darwin -> macOs win32-> windows * @returns this */ src(url: string, platfrom: "darwin" | "win32"): this { this.map.set(platfrom, path.resolve(url)); return this; } //返回合乎以后平台的二进制地址 path(): string { return this.map.get(process.platform); } //仅仅提供一个调用办法 async run(cmd = ["--version"]): Promise<execa.ExecaChildProcess<string>> { const path = this.path(); try { //验证是否存在 //如果不存在就会报错 try catch捕捉 fs.statSync(path); const result_1 = await execa(path, cmd); console.log("command:", result_1.command); if (result_1.stderr.length > 0) { console.log(result_1.stderr); } if (result_1.stdout.length > 0) { console.log(result_1.stdout); } return result_1; } catch (e) { console.log(`Failed to access file ${path}`, e); return Promise.reject(e); } }}调用例子import * as execa from "execa";const url = path.resolve(__dirname,'../bin');const bin = new Local_Bin_Wrapper() .src(`${url}/mac/pngquant/pngquant`, 'darwin') .src(`${url}/win/pngquant/pngquant.exe`, 'win32');//这样便于本人封装const pngquantBin = bin.path();execa(pngquantBin, ['--version']);

April 8, 2021 · 1 min · jiezi

关于c++:c类访问权限及友元

1.类的拜访权限class是c++的类申明关键字,它的成员类型有三种,是应用三个关键字来申明的,别离是public、private、protected,public申明的叫做私有成员,private申明的是公有成员,protected申明的则是爱护成员。 1)public-私有成员首先看一下public申明的私有成员: class A{public: int a; int b;public: int add();private: int sub();protected: int mul();};int A::add(){ return a+b;}int A::sub(){ return a-b;}int A::mul(){ return a*b;}int main(){ A a; a.a = 2; a.b = 3; return 0;}对于以上代码,编译通过,阐明对于私有成员a和b,私有成员函数、公有成员函数、爱护成员函数以及类对象都是能够间接进行拜访的。 2)private-公有成员接下来将成员变量a申明为公有,如下: class A{private: int a;public: int b;public: int add();private: int sub();protected: int mul();};int A::add(){ return a+b;}int A::sub(){ return a-b;}int A::mul(){ return a*b;}int main(){ A a; a.a = 2; a.b = 3; return 0;}编译时报错如下: test.cpp: In function ‘int main()’:test.cpp:33:4: error: ‘int A::a’ is private within this context编译报错的意思是第33行,a是一个公有成员,不能间接拜访,那么将33行正文掉,而后再编译,能够通过。综上,阐明对于公有成员a,私有成员函数、公有成员函数、爱护成员函数都能够间接进行拜访,但类对象不能间接拜访。 ...

April 8, 2021 · 2 min · jiezi

关于c++:HackingC-Learning笔记-Chapter10Generic-Programming泛型编程

@@@@@@ @@@

April 6, 2021 · 1 min · jiezi

关于c++:HackingC-Learning笔记-Chapter11Memory-Management

xxx xxxxxxx

April 6, 2021 · 1 min · jiezi

关于c++:c中运算符重载

加号运算符重载对于内置数据类型,编译器晓得如何运算 然而对于本人封装的类,编译器无奈进行运算 这时能够通过本人定义运算符重载进行运算 operator 通过成员函数重载+号#include<iostream>using namespace std;class Person{public: int m_a; int m_b; //通过成员函数实现重载 Person operator+ (Person &p) { //创立一个长期变量 Person temp; temp.m_a = this->m_a + p.m_a; temp.m_b = this->m_b + p.m_b; return temp; }};void test01(){ Person p1; p1.m_a = 66; p1.m_b = 44; Person p2; p2.m_a = 6; p2.m_b = 4; Person p3; //通过函数原型调用 p3 = p1.operator+(p2); //简便调用 //p3 = p1 + p2; cout << "p3.m_a:" << p3.m_a << endl; cout << "p3.m_b:" << p3.m_b << endl;}int main(){ test01(); system("pause"); return 0;}留神两种调用形式 ...

April 4, 2021 · 3 min · jiezi

关于c++:HackingC-Learning笔记-Chapter7Standard-Library-–-Part-2

规范库Range Copy算法留神: 必须要保障指标地位有足够的空间(resize 或者 reserve),规范算法在大多数状况下不能查看指标范畴是否足够大 copy(@beg, @end, @target_begin) -> @target_out copy_n(@beg, n, @target_begin) -> @target_out copy_backward(@beg, @end, @target_end) -> @target_begin (拷贝到end处)copy_if(@beg, @end, @target,f(O)->bool)->target_endsample(@beg, @end, @out, n, random_generator) ->out_end (实现采样-c++17)Special 迭代器感觉用途不大哦,除了插入这个interator规范库Sequence Reordering算法#shitf Elements 转换元素1.reserve/reserve_copy 反转2.rotate/rotate_copy 旋转 shift_left/shift_right(X感觉用途不大c++20)3.shuffle(@beg, @end, randim_engine) 随机打乱#sort 排序1.sort(@begin, @end, compare(o,o)->bool)2.stable_sort(@begin, @end, compare(o,o)->bool)//a.sort是疾速排序实现,因而是不稳固的;stable_sort是归并排序实现,因而是稳固的;//b.对于相等的元素sort可能扭转程序,stable_sort保障排序后相等的元素秩序不变//c.如果提供了比拟函数,sort不要求比拟函数的参数被限定为const,而stable_sort则要求参数被限定为const,否则编译不能通过3.partial_sort(@begin, n, @end, compare(o,o)->bool) /partial_sort_copy() nth_elements(没想到用处)4.is_sorted(@begin, @end, compare(o,o)->bool) -> true (是否有序)5.is_sorted_until(@begin, @end, compare(o,o)->bool) -> @sorted_end (返回到哪里之前是有序的)# partition 分区1.partition(@beg, @end, f(o,o)->bool) ->@ftrue_end 2.partition_copy(@beg, @end, @ft, @ff, f(O)->bool) -> {@ft_end, @ff_end}3.stable_partition(@beg, @end, f(o,o)->bool) ->@ftrue_end // stable_partition保障分区后原来的先后顺序不变,而partition无奈保障4.is_partition() -> true5.partition_point() -> @ftrue_end (分区后返回分界点)## Permutations 排列组合(不晓得利用场景??)1.next_permutation(@beg, @end) -> true 只有下一种排列能够是逻辑上大的,就返回true2.prev_permutataion(@beg, @end) -> true 只有上一种排列能够是逻辑上小的,就返回true3.is_permutation(@beg, @end, @beg2) -> true if range2 is a permutation of range1规范库Element-Wise Range Modifications(批改)# Filling / Overwriting Ranges 填充改写1.fill(@beg, @end, value) ->@filled_end (fill_n)2.generate(@begin ,@end generator()-> ●) (generator_n)//generator能够通过functors 写入不同的value,比fill性能强,不必循环这么捞的写了# Changing / Replacing Values 扭转代替1.transform(@beg, @end, @out, f(O)->■) -> @out_end2.transform(@beg, @end, @beg2, @out, f(O,△) -> ■) -> @out_end//该算法在其余编程语言中也称为"map"//target 必须可能承受和input range元素一样多的元素//f必须没有 side_effect / be stateful , 因为不能保障将functors利用于输出元素的程序3.replace(@beg, @end, old_value, new_value) / replace_if(@beg, @end, condition(O)->bool)4.replace_copy/replace_copy_if ...

April 2, 2021 · 1 min · jiezi

关于c++:leetcodeJZ22

题面输出一个链表,输入该链表中倒数第k个节点。为了合乎大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。 例如,一个链表有 6 个节点,从头节点开始,它们的值顺次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。 示例: 给定一个链表: 1->2->3->4->5, 和 k = 2.返回链表 4->5.原题链接 剖析区间滑动思维,看代码正文即可 源代码(正文局部即为思路)/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */class Solution {public: ListNode* getKthFromEnd(ListNode* head, int k) { int n=1; ListNode* tmp = head; ListNode* backup = head; //思考到k可能等于1 if(n==k){ tmp = backup; backup = head; } //当头节点head后退k位后,记录head之前第k位的节点head_before_k,而后head每前进一步,head_before_k也前进一步 while(head->next!=NULL){ head = head->next; n++; if(n==k){ tmp = backup; backup = head; } if(n>k){ tmp = tmp->next; } } return tmp; }};

April 1, 2021 · 1 min · jiezi

关于c++:leetcodeJZ21

题面输出一个整数数组,实现一个函数来调整该数组中数字的程序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半局部。 示例: 输出:nums = [1,2,3,4]输入:[1,3,2,4]注:[3,1,2,4] 也是正确的答案之一。 提醒: 0 <= nums.length <= 500001 <= nums[i] <= 10000原题链接 剖析刚开始想到思路1: 从前向后遍历数组,遇到奇数时,什么也不做;遇到偶数时,把该偶数删除,而后把该偶数放到数组前面然而这种思路会超时 上面剖析超时起因: 数组最大长度为50000,思路1绝不仅仅是简略的遍历数组,工夫复杂度为O(N),思路1还波及到数组的插入、删除,删除操作(erase)的复杂度就是O(N),最坏状况下,工夫复杂度为O(N2),太大。 接下来想到思路2: 采纳双指针,left指向数组首部,right指向数组尾部,left总体趋势向右挪动,right总体趋势向左挪动上面剖析这种思路不超时的起因: 只遍历数组一遍,工夫复杂度为O(N)。 源代码(正文局部即为思路)class Solution {public: vector<int> exchange(vector<int>& nums) { int len = nums.size(); int left = 0; int right = len-1; while(left<right){ //左偶、右奇 if(nums[left]%2==0 && nums[right]%2==1){ //替换数组元素的操作其实很简略,工夫复杂度O(1),空间复杂度O(1) int tmpL = nums[left]; int tmpR = nums[right]; nums[left] = tmpR; nums[right] = tmpL; left++; right--; } //左偶、右偶 if(nums[left]%2==0 && nums[right]%2==0){ right--; } //左奇、右偶 if(nums[left]%2==1 && nums[right]%2==0){ left++; right--; } //左奇、右奇 if(nums[left]%2==1 && nums[right]%2==1){ left++; } } return nums; }};提交后果: ...

April 1, 2021 · 1 min · jiezi

关于c++:leetcodeJZ21

题面请实现一个函数用来判断字符串是否示意数值(包含整数和小数)。例如,字符串"+100"、"5e2"、"-123"、"3.1416"、"-1E-16"、"0123"都示意数值,但"12e"、"1a3.14"、"1.2.3"、"+-5"及"12e+5.4"都不是。原题链接 剖析除非承受过专业训练,否则一开始很难想到用自动机(Automata)来做,我是一开始凭直觉列举出不同的规定,而后一直的用测试用例试错,最终通过所有测试用例。 上面的代码没有任何参考意义,正文局部能够看一看。 做完本题最大的意义在于: 了解官网自动机解法的优越性坚固了一些C++根底语法常识源代码(正文局部即为思路)class Solution {public: bool isNumber(string s) { char candidate[15] = {'0','1','2','3','4','5','6','7','8','9','.','+','-','E','e'}; set<char> cand; for(int i=0; i<15; i++){ cand.insert(candidate[i]); } set<char> cand2; for(int i=0; i<10; i++){ cand2.insert(candidate[i]); } //cand3没有存在的必要性 set<char> cand3; for(int i=0; i<13; i++){ cand3.insert(candidate[i]); } int rawLen = s.length(); for(int i=0; i<rawLen; i++){ if(s[i]==' '){ int tmpL = i; int tmpR = i; //避免空格有多个 while(s[tmpL]==' ' && tmpL>0){ tmpL--; } while(s[tmpR]==' ' && tmpR<rawLen-1){ tmpR++; } if(cand.find(s[tmpL])!=cand.end() && cand.find(s[tmpR])!=cand.end()){ return false; } } } //之前是判断和空格无关的状况 //删除掉所有的空格 s.erase(std::remove(s.begin(), s.end(), ' '), s.end()); //之后是去掉所有空格之后进行判断 int len = s.length(); //特判 if(len==0){ return false; } //特判 if(len==1){ if(s[0]=='+' || s[0]=='-' || s[0]=='.' || s[0]=='E' || s[0]=='e'){ return false; } else{ if(cand.find(s[0])!=cand.end()){ return true; } else{ return false; } } } //特判 if(s[1]=='+' || s[1]=='-' || s[len-1]=='e' || s[len-1] == 'E'){ return false; } /*后两个特判意义不大*/ int dotNumber = 0; int eNumber = 0; int dotPosition = -1; int ePosition = -1; for(int i=0; i<len; i++){ if(cand.find(s[i])==cand.end()){ return false; } else{ if(s[i]=='.'){ //.最多只能呈现一次 dotNumber++; dotPosition = i; if(dotNumber>=2){ return false; } if(dotPosition==len-1){ if(cand2.find(s[dotPosition-1])==cand2.end()){ return false; } } // cout<<dotPosition; } if(s[i] == 'e' || s[i]=='E'){ eNumber++; ePosition = i; if(eNumber>=2){ return false; } } if(s[i]=='+' || s[i]=='-'){ if(ePosition==-1){ if(i!=0){ return false; } } else{ if(i!=0){ if(i!=ePosition+1){ return false; } } } } } } //e之后不能再有小数点 if(ePosition !=-1 && dotPosition !=-1 && ePosition<dotPosition){ return false; } if(ePosition!=-1){ //e后面的那一个字符,必须是数字或者‘.' if((ePosition-1)<0 || s[ePosition-1]=='+' || s[ePosition-1]=='-'){ return false; } else{ //如果e后面那一个字符是. if(s[ePosition-1]=='.'){ if((ePosition-2)<0 || cand2.find(s[ePosition-2])==cand2.end()){ return false; } } } if(s[ePosition+1]=='+' || s[ePosition+1]=='-'){ if(ePosition+2>=len){ return false; } for(int k=ePosition+2; k<len; k++){ if(cand2.find(s[k])==cand2.end()){ return false; } } } //e前面第一位呈现的不是加号和减号 else{ if(ePosition+1>=len){ return false; } for(int k=ePosition+1; k<len; k++){ if(cand2.find(s[k])==cand2.end()){ return false; } } } } return true; }};提交后果: ...

April 1, 2021 · 2 min · jiezi

关于c++:HackingC-机翻阅读记录-Chapter6Function-Objects

集体笔记向+google机翻(机翻的确很多细节形容不清,看不懂记得参考原文)原文参见:https://hackingcpp.com/cpp/be... Function Objects至多提供一个operator() 重载像一个函数一样能够是有状态的(对象的data数据有记忆,相似函数里有一个static data) Example: Interval Query Lambdas (Basics)

March 31, 2021 · 1 min · jiezi

关于c++:C内存管理1内存分配的每个层面

C++ 应用程序,应用 memory 的路径 C++ memory primitives调配开释类属可否重载malloc()free()C 函数不可newdeleteC++ 表达式不可::operator new()::operator delete()C++ 函数可allocator<T>::allocate()allocator<T>::deallocateC++ 规范库可自在设计并予以搭配任何容器void *p1 = malloc(512); // 512 bytesfree(p1)complex<int>* p2 = new complex<int>; // one objectdelete p2;void *p3 = ::operator new(512); // 512 bytes::operator delete(p3);// 以下应用 C++ 规范库提供的 allocators// 其接口虽有规范规格,但实现厂商并未齐全恪守;上面三种模式略异#ifdef _MSV_VER // 以下两个函数都是 no-static, 要通过 object 调用。 // 调配 3 个 ints int *p4 = allocator<int>().allocate(3, (int*)0); // 对应规范库分配器的第二个参数 allocator<int>().deallocate(p4, 3);#endif#ifdef __BORLANDC__ // 以下两个函数都是 no-static, 要通过 object 调用。 // 调配 5 个 ints int *p4 = allocator<int>().allocate(5); // 同样蕴含第二个参数,但申明处有默认值,因而调用处可不写 allocator<int>().deallocate(p4, 5);#endif#ifdef __GNUC__ // 晚期版本的实现, 2.9 // 以下两个啊含混都是 static, 可通过全名调用。 // 调配 512 bytes void *p4 = alloc::allocate(512); alloc::deallocate(p4, 512);#endif#ifdef __GNUC__ // 古代版本的实现, 4.9 // 以下两个函数都是 no-static,要通过 object 调用。 // 调配 7 个 ints void *p4 = allocator<int>().allocate(7); allocator<int>().deallocate((int*)p4, 7); // 以下两个函数都是 no-static,要通过 object 调用。 // 调配 9 个 ints void *p4 = __gnu_cxx::pool_alloc<int>().allocate(9); __gnu_cxx::pool_alloc<int>.deallocate((int*)p4, 9);#endifnew expression (new 背地的行为)Complex *pc = new Complex(1, 2);编译器转换为 ==>>Complex *pc;try { /* 1 */ void mem = operator new (sizeof(Complex)); // allocate 申请内存空间 /* 2 */ pc = static_cast<Complex*>(mem); // cast 类型转换 /* 3 */ pc->Complex::Complex(1, 2); // construct 调用构造函数 // 留神:只有编译器才能够像下面那样间接呼叫 ctor}catch (std::bad_alloc) { // 若 allocation 失败,就不执行 constructor }注:申请内存可能会失败,因而引入 try...catch...new 做两个动作 ...

March 30, 2021 · 3 min · jiezi

关于c++:HackingC-机翻阅读记录-Chapter5Standard-Library-–-Part-1

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

March 30, 2021 · 1 min · jiezi

关于c++:HackingC-机翻阅读记录-Chapter2InputOutput

集体笔记向+google机翻(机翻的确很多细节形容不清,看不懂记得参考原文)原文参见:https://hackingcpp.com/cpp/be... Input&Output1.Command Line Arguments 命令行参数What &WhyHow to Access in C++int main (int const argc, char const* argv/*a pointer to a constant char*/) {Argument ConversionConversion to std::string, int, …String → Number Conversion Functions2.File Input&Output 文件输入输出3.Stream Input&Output 流输入输出4.Recover From Input Error 从输出谬误中复原

March 30, 2021 · 1 min · jiezi

关于c++:leetcodeJZ15剪绳子IIC实现

题面给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m - 1] 。请问 k[0]*k[1]*...*k[m - 1] 可能的最大乘积是多少?例如,当绳子的长度是8时,咱们把它剪成长度别离为2、3、3的三段,此时失去的最大乘积是18。 答案须要取模 1e9+7(1000000007),如计算初始后果为:1000000008,请返回 1。 示例 1: 输出: 2输入: 1解释: 2 = 1 + 1, 1 × 1 = 1示例 2: 输出: 10输入: 36解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36提醒: 2 <= n <= 1000原题链接 剖析该题在第14题“剪绳子”的根底上,减少了大数取余局部,上面探讨C++的4种解题思路: 动静布局、数学推导、数学推导优化、贪婪。 最初会再次印证一句古老的话:艺术与迷信终将在某一点相遇 1 动静布局(行不通)在第14题代码根底上,每次从第i-1个状态计算第i个状态时,退出取余运算,会得出谬误的后果。理由如下: 动静布局的特点是,第i个状态依赖于第i-1个状态,最初一个状态(指标状态)依赖于倒数第二个状态,如果在状态转移间进行取余,会呈现有的转移须要取余,有的转移不须要取余: 假如a和b是两个数,a>b,a须要取余,b不须要取余,a取余之后的值为a1,可能呈现a1<b的状况,这就会误导前面的状态转移,最终导致谬误的后果。 这是无奈得出正确后果的动静布局的代码,留神,为了不产生溢出,须要将dp数组和若干两头值改成long类型(详见代码正文): class Solution {public:int cuttingRope(int n){ int base = 1000000007; if(n==2) { return 1; } if(n==3) { return 2; } // 将dp数组改成long类型 long dp[n+1][n+1]; for(int i=0; i<n+1; i++) { for(int j=0; j<n+1; j++) { dp[i][j] = -1; } } for(int i=0; i<n+1; i++) { dp[i][i] = 1; dp[i][1] = i; } //dp[i][j],长度为i的绳子,切成j段,乘积的最大值 for(int tmp=3; tmp<=n; tmp++) { int now_len = tmp; for(int sp=2; sp<now_len; sp++) { int split = sp; // 将x1改成long类型 long x1 = 1; // 将dp数组候选值tmp_max改成long类型 long tmp_max = -1; bool overflow = false; while((x1<now_len) && (now_len-x1)>=(split-1)) { // 将乘积multi改成long类型 long multi = (x1*dp[now_len-x1][split-1]); if(!overflow){ if(multi>base){ multi = multi%base; tmp_max = multi; overflow = true; } else{ if(multi>tmp_max){ tmp_max = multi; } } } else{ if(multi>base){ multi = multi%base; if(multi>tmp_max){ tmp_max = multi; } } } x1++; } dp[now_len][split] = tmp_max; } } int final_max = -1; for(int p=1; p<=n; p++) { if(dp[n][p]>final_max) { final_max = dp[n][p]; } } return final_max;}};2 数学推导务必先看此处的数学推导过程。 ...

March 29, 2021 · 3 min · jiezi

关于c++:C如何通俗理解this指针解析和用法示例

要了解"this"指针, 重要的是要理解对象如何对待类的函数和数据成员。 每个对象都有本人的数据成员正本。全副拜访与代码段中雷同的性能定义。意味着每个对象都有本人的数据成员正本, 并且所有对象共享成员函数的单个正本。 当初的问题是, 如果每个成员函数只有一个正本并且被多个对象应用, 那么如何拜访和更新适当的数据成员? 编译器提供隐式指针以及函数名称" this"。" this"指针作为暗藏参数传递给所有非动态成员函数调用, 并且可用作所有非动态函数体内的局部变量。" this"指针在动态成员函数中不可用, 因为能够在没有任何对象(带有类名)的状况下调用动态成员函数。对于X类, this指针的类型为" X "。另外, 如果X的成员函数申明为const, 则this指针的类型为" const X "在晚期的C ++版本中, " this"指针将被更改;通过这样做, 程序员能够更改办法正在解决的对象。该性能最终被删除, 当初在C ++中为r值。C ++通过调用以下代码让对象销毁本身: delete this ;正如Stroustrup所说, "this"可能是指针的参考, 但在C ++的晚期版本中没有该参考。如果将" this"用作参考, 则能够防止上述问题, 并且比指针更平安。 以下是应用" this"指针的状况:1)当本地变量的名称与成员的名称雷同时 #include<iostream>using namespace std; /* local variable is same as a member's name */class Test{private : int x;public : void setX ( int x) { // The 'this' pointer is used to retrieve the object's x // hidden by the local variable 'x' this ->x = x; } void print() { cout << "x = " << x << endl; }}; int main(){ Test obj; int x = 20; obj.setX(x); obj.print(); return 0;}输入如下: ...

March 29, 2021 · 3 min · jiezi

关于c++:C中对文件的操作

文件操作写在后面 <u>ofstream写文件,也就是对文件的输入,所以out,以O结尾</u> <u>ofstream的成员函数中蕴含write写文件</u> <u>ifstream读文件,也就是文件对外输出,所以in,以I结尾</u> <u>ifstream的成员函数中蕴含read读文件</u> 操作文件蕴含头文件fstream 类型:文本文件:ASCLL存储 二进制文件:二进制存储 操作文件三大类1、ofstream:写操作 2、ifstream:读操作 3、fstream:读写操作 文本文件操作写文件1、蕴含头文件 fstream 2、创立流对象 ofstream ofs 3、关上文件 ofs.open(”文件门路“,"打开方式"); 4、写数据 ofs<<"写入的数据"; 5、敞开文件 ofs.close 打开方式解释ios::in为读文件而关上文件ios::out为写文件关上文件ios::ate初始地位:文件尾ios::app追加形式写文件ios::trunc如果文件存在先删除,再创立ios::binary二进制文件留神:文件打开方式能够配合应用,利用|操作符 例如:用二进制形式写文件 ios::binary|ios::out #include<iostream>#include<fstream>using namespace std;void test(){ //1、蕴含头文件 //2、创立流对象 ofstream ofs; //3、关上文件 ofs.open("材料.txt",ios::out); //4、写数据 ofs << "此情可待成追忆,只是过后已惘然"; //5、敞开文件 ofs.close();}int main(){ test(); system("pause");}总结: 文件操作必须蕴含头文件fstream 读文件能够利用ofstream,或者fstream类 关上文件时候须要操作文件的门路,以及打开方式 利用<<能够向文件中写数据 操作结束要敞开文件 读文件1、蕴含头文件 2、创立流对象 ifstream ifs 3、关上文件判断是否关上 ifs.open(“材料”,iOS::in); ifs.is_open返回布尔类型用if语句判断文件是否关上胜利 4、读数据(四种办法) 1/char buf[1024]={0};while(ifs>>buf){cout<<buf<<endl;}2/char buf[1024]={0};while(ifs.getline(buf,sizeof(buf))){cout<<buf<<endl;}3/string bufwhile(getline(ifs,buf)){cout<<buf<<endl;}4/不举荐char c;while((c=ifs.get())!=EOF)//end of file{cout<<c;}5、敞开文件 ifs.close() #include<iostream>#include<fstream>#include<string>using namespace std;void test(){ //1、蕴含头文件 //2、创立流对象 ifstream ifs; //3、关上文件 ifs.open("材料.txt", ios::in); if (ifs.is_open() == false) { cout << "文件关上失败!" << endl; } //4、读数据 string buf; while (getline(ifs, buf)) { cout << buf << endl; } //5、敞开文件 ifs.close();}int main(){ test(); system("pause");}二进制文件操作写文件ios::binary ...

March 27, 2021 · 1 min · jiezi

关于c++:动态规划数组等子集和问题总结

1.是否划分成2个相等和的子数组,转化为0-1背包问题,何为sum/2,二重循环从大到小遍历 #include <stdio.h>#include <iostream>#include<vector>using namespace std;bool canPartion(vector<int>nums) { int sum = 0; for (int i = 0; i < nums.size(); i++) { sum += nums[i]; } if (sum % 2 == 1) { return false; } int target = sum / 2; vector<int> dp(target + 1, 0); for (int i = 0; i < nums.size(); i++) { for (int j = target; j >= nums[i]; j--) { dp[j] = max(dp[j], dp[j-nums[i]] + nums[i]); } } std::cout << dp[target] << " " << target << endl; return dp[target] == target;}int main(){ vector<int> nums = {1, 5, 11, 5}; cout << canPartion(nums) << endl; return 0;}2。是否划分成k个和雷同数组,间接递归 ...

March 25, 2021 · 2 min · jiezi

关于c++:细数继承与派生纯干货推荐10

目录一、类与对象的根底1、简略概念2、类的类型①、私有②、公有③、爱护4、扩大:(类和构造体的区别)二、应用类和对象办法一:办法二:办法三:三、继承与派生1、名称解释2、繁多继承①、私有继承②、公有继承③、爱护继承④、总结3、多重继承4、注意事项①、互相赋值②、防止二义性最近在学习C++面向对象设计,看似很根底的内容实际上有多知识点值得斟酌。 学完类与对象,就很想写点货色来输入我的思考和学习的想法,也心愿各位朋友一起多多交换,如果文章里有谬误的中央请多多斧正。 一、类与对象的根底前言: 学习C,要有面向过程的设计思维。 学习C++,要有面向对象的设计思维。 1、简略概念对象: 任何一个事物都能够看成一个对象,比方一个班级,一个学校,一篇文章等等。 对象蕴含两个因素:属性 和 行为。 举个栗子:咱们把一个班级看做一个对象,那么班级里的人数,业余,均匀问题叫做属性(动态的)。班级上课,加入运动会等叫做行为(动静的)。一个对象个别是由一组属性和一组行为形成。 封装 :个别对象里的内容会波及到机密,在面对外人时。咱们能够把对象里的局部内容盖起来不让外人看到。 举个栗子:班级期末考试,家长想晓得班级里所有人的问题(家长对班级这个对象进行拜访/调用),但咱们只通知家长班级均匀问题,所有的同学的问题都不对外颁布(集体问题进行封装)。相同,平均分就是属于大家都能看到/接触/调用的内容。 2、类的类型①、私有听名字就晓得,这是个大家都能看失去或者接触失去的内容。 比方上个栗子中的班级平均分就属于私有局部,班级中的每位家长都能够看到。 `class Student{ public:}` ②、公有听名字就晓得属于定义对象所领有的局部,不能让外界看到或者解除(当然也会有例外,这里挖个坑)。把局部成员进行封装,避免外人看到。 上个栗子中对应的是班级里每位同学的问题。 `class Student{ private:}` ③、爱护首先一个问题,爱护类型和公有类型有什么区别? 这里咱们先做个简略的介绍: 公有和爱护的成员函数和数据只能被本类中的其余成员函数所调用,而不能被类外调用。 而爱护的性能在继承与派生中更能体现进去, 当然也有例外的状况产生。同公有类型一样,挖个坑。 写完例外情况(友元),我会在此处做个文章链接。 `class Student{ protected:}` 4、扩大:(类和构造体的区别)先上两种定义:(Student为例) `///classclass Student{ private: int num; char sex: string name; public: void display();};` `///struct(C)struct Student{ int num; char sex: string name; }stu;void display(int num,char sex,string name);` 补充一个小常识:C++在设计的时候就以兼容C,但又不是齐全简略的继承C,同时还要有本人的特点为准则进行开发。 最显著的就是struct中没有private和public,想给student增加什么属性间接写进去就完事了。 class不同,除了想把属性加进去外,还要思考这个属性要不要让他人看到。想让他人能看到,退出public中。不想让他人看到就退出private中。 举个栗子: 如果把struct比作一家新百伦鞋店,那么这家店就属于通透型的,穿鞋换鞋坐在沙发板凳上就能够实现,无需试衣间。所有的中央和盘托出。 class更像是含有试衣间的优衣库。筛选衣服的时候大家都能看到,然而试衣服的时候就必须要去试衣间里。我想没有多少人在换衣服的时候被他人看到吧? 以上内容援用于比拟C中的struct和C++中的class。 而在C++中对于struct的用法进行了降级: `///struct(C++)struct Student{ private: int num; char sex: string name; public: void display();};` 可见C++中应用struct也能够像class一样对成员进行public或private分类。 ...

March 25, 2021 · 3 min · jiezi

关于opencv:Learning-OpenCV3习题答案

不定时更新。。Codes

March 24, 2021 · 1 min · jiezi

关于qt:Qt4K高分屏自适应解决字体没有跟随组件增大的问题

解决背景在一次做我的项目的过程中,公司让我做高分屏适配,就是让咱们所开发的软件反对4K屏的显示,我在百度上开始搜寻材料,有很多博客给出的答案就是: #if(QT_VERSION >= QT_VERSION_CHECK(5,6,0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);#endif QApplication a(argc, argv);   解决过程我也这样做了,后果,没错,Qt的确是把窗口以及组件的大小进行了屏幕自适应,然而组件内的字体大小却没有扭转,所以显示进去的后果就是这个样子,如图:  解决办法看上去是不是很不协调,很丑有没有,然而没方法,你如果非得让我给你解释原理的话,就是:起因是qt的dpi计算错误,这样的状况会导致qt在不同的平台上大小不一,解决的办法就是设置好qt的dpi,qt是依据显示器的物理长度或者宽度于分辨率的关系来计算dpi的, 所以,咱们把Qt字体的PointSize从新设置,一次,就能解决这个问题。废话不说间接上代码: #if(QT_VERSION >= QT_VERSION_CHECK(5,6,0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);#endif QApplication a(argc, argv); QFont font = a.font(); font.setPointSize(10);//这个值依据本身状况自行调节,如果字体没变动,把setPointSize()内的值持续增大 a.setFont(font); 当你把setPointSize()内的值设置大了之后,你会发现,显示居然失常了,如图:   最终代码当初的话,字体大小就曾经失常了,然而如果在1080P下面字体可能会更大,因为你当初是在4K屏,所以看上去是失常的,那咱们接下来应该怎么做,很简略,让setPointSize()的值依据零碎自行调节,问题就解决了,废话不说,持续上代码: #if(QT_VERSION >= QT_VERSION_CHECK(5,6,0)) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);#endif QApplication a(argc, argv); const float DEFAULT_DPI = 96.0; HDC screen = GetDC(NULL); FLOAT dpiX = static_cast<FLOAT>(GetDeviceCaps(screen, LOGPIXELSX)); ReleaseDC(0, screen); float fontSize = dpiX / DEFAULT_DPI; QFont font = a.font(); font.setPointSize(font.pointSize()*fontSize); a.setFont(font);好了,这样的话,问题解决,这样自适应问题就解决掉了注:这些操作都需在main主函数外面操作

March 23, 2021 · 1 min · jiezi

关于c++:C-常见编译运行错误总结

最近开始写C++的代码了,小结一下谬误和起因。 Symbols[Symbols not found in architecture + linker command failed with exit code 1]起因: symbol没有找到,实际上就是应用某个变量或者某个函数名的时候,它的申明没有找到。很可能是函数头的变量类型没写对。应用MacOS的clang++作为调试器,提醒的谬误可能和最终用g++编译的不同,最好还是应用gdb调试Segmentfault个别是指针或者数组越界之类的谬误

March 22, 2021 · 1 min · jiezi

关于c++:WonderTrader架构详解之一整体架构

前言 时至今日,WonderTrader曾经受到不少敌人的关注,笔者也感到十分荣幸。这样的关注从某种角度来说,也是对笔者的认可,哪怕是批评的声音,也是对笔者的鞭策。之前有一些敌人在研读WonderTrader代码的时候,遇到了一些问题,最次要的就是不分明整个平台的架构设计,很多细节上了解不透。所以这些敌人曾经三番五次跟笔者说,心愿笔者写个文章整体介绍一下平台的架构设计。 其实笔者一开始不是很想写这样的文章。毕竟笔者自问还是下了很多功夫在代码里的,还是心愿识货的人多读读源码,而不是看一下架构文章就对WonderTrader缄口结舌地指点江山。起初天人交战了一段时间,笔者恍然大悟:既然开源了,何必悭吝多写几篇文章,授人以鱼不如授人以渔。于是乎,就有了这个《WonderTrader架构详解》的系列文章。 本文是该系列文章的第一篇,次要介绍WonderTrader的整体架构和策略实盘环境下的个别流程。 外围设计准则 笔者之前有一篇文章,大抵介绍了一下WonderTrader的演变过程。而WonderTrader通过这么屡次的迭代和重构,有一套外围的设计准则贯通始终,这也是WonderTrader经验屡次迭代和重构,最终走向成熟而不是土崩瓦解的外围起因。 1、组件化准则 在软件工程中,解耦是零碎设计的必修课程。耦合太重,各个模块之间的相互依赖太多,会导致代码逻辑简单难解,从而升高代码的可维护性。所以咱们在设计简单零碎的时候,肯定要思考如何正当的解耦。合了解耦当前,失去的就是一个组件化的零碎。 零碎组件化,有很多益处: 便于团队合作,不同组件的开发能够同步进行,晋升开发效率组件开发更灵便,只有合乎组件接口的范式,开发环境能够灵便应用组件扩大更便捷,对接新的第三方时只须要增量更新,不影响其余组件 然而零碎组件化,也有一些前提: 各组件之间通过接口进行拜访,没有额定的依赖。所以接口设计的好坏,间接关系到组件化的成功率和成果。组件化,须要在接口层面进行形象。而形象,则难以避免带来性能损失,如果在对性能有极致谋求的场景下,组件化肯定要审慎。对于同一个接口,接口层的形象,会将合乎该接口的不同组件的差异化的点暗藏起来。如果零碎肯定要用到这些差异化的局部,可能会导致接口设计的复杂化。 WonderTrader采纳的组件化设计,将独立的功能模块全副封装到独立的组件中,用接口连接起来。比拟典型的是不同的行情接入模块(Parsers)和不同的交易通道模块(Traders),因为每个模块都要对接各自对应的第三方API,所以采纳组件化设计,能无效的将各自不同的逻辑隔离开,让零碎只须要关注接口传递的数据即可。此外,执行单元(ExecuteUnit)、风控模块(RiskMonitor)、数据落地模块(DataWriter)和数据读入模块(DataReader)等,都遵循组件化设计的准则。 2、策略最简化准则 策略最简化准则,从某种角度来说,是一个哲学问题。之所以这么说,是源自于笔者和一位用户的探讨,探讨次要围绕策略的信号接口是怎么实现的来开展的。大抵的观点就是这位敌人感觉策略须要确定以后信号到底是要开多、开空、平多或者平空,而笔者却认为策略只须要设置指标部位即可,不须要关注到底是要怎么下达交易指令。 在上一次重构WonderTrader的时候,笔者花了很长时间来思考一些问题,诸如: 策略关注的最外围的点是什么?策略是不是肯定要晓得开平多空?策略是不是肯定要晓得不同的接口回调过去的数据结构?策略是不是肯定要自行治理订单?…… 在经验了重复的思考当前,笔者没有得出任何答案。然而笔者也不是一个拖沓的人,既然没有答案,那么笔者就决定从减法做起。 策略只须要关注信号逻辑,不须要关注如何执行,所有都丢给底层策略不须要晓得开平多空,只须要通知交易引擎本人的指标部位是什么,所有都丢给底层策略不须要和任何接口打交道,只和数据打交道,其余的所有都丢给底层策略不须要治理订单,所有都丢给底层…… 减法做完当前,笔者也不再纠结这个问题,正如后面提到的,这是一个哲学问题,而大多数哲学问题,都没有答案。然而笔者置信这样的减法,对于每个策略研发人员来说,都是十分敌对的。底层会帮忙策略把策略逻辑以外的任何事件都搞定,从数据管理到订单执行,包含多空开平,甚至平今对锁等等,底层都会全副搞定。 3、策略一致性准则 策略的一致性准则,蕴含多个方面: 策略代码的一致性,即回测和实盘用同样的代码策略回测和实盘信号的一致性,即实盘中触发的信号,在应用历史数据回测时该当是统一的同一套策略,不同的交易通道中收回的交易指令,该当是统一的 策略代码的一致性比拟好了解,也比拟好实现,只有保障回测框架和实盘框架向策略提供的API是统一的,就能够保障策略能够无缝在回测环境和实盘环境之间切换。 策略信号的一致性,其实是对平台数据处理的一致性的要求。只有实盘中实时处理的数据和历史数据完全一致,能力保障历史回测和实盘信号的一致性。因为实盘环境中,数据的达到有先后,如果解决数据保障策略响应的一致性,的确是比拟考验功夫的。笔者在WonderTrader中花了很多精力,来设计一种机制,能够尽可能的保障实盘数据的解决和历史回测数据的一致性,从而保障策略在实盘和回测中信号的一致性。 对于不同的交易通道中,信号和指令的一致性,这个就是WonderTrader的外围机制之一,即1+N的执行架构。1+N的执行架构,能够保障一个策略组合的信号在不同的交易通道中都可能统一,从而使得不同的交易通道的策略绩效相差无几。对于1+N执行架构的具体介绍,笔者会在后续的文章中做具体的论述,这里临时就不开展了。 外围架构介绍 后面提到的设计准则,绝对比拟形象。上面笔者将联合示意图,别离介绍WonderTrader的回测框架、实盘框架以及策略在实盘中的根本流程。读者能够从上面的架构设计中和后面提到的设计准则相互参考,应该能够失去一些印证。 1、回测框架 上图是WonderTrader回测框架的架构图。从上图咱们能够看进去,回测框架还是比拟简洁的。整个回测框架的外围在于4个仿真器和一个历史数据回放器。而4个仿真器,别离对应3种不同类型的策略,以及执行单元。 而策略局部,除了C++策略能够间接和回测引擎交互以外,多语言策略(当初只实现了python)则须要通过C接口的粘合层跟回测引擎进行交互,同时还须要提供一个多语言子框架提供多语言环境下的API和底层交互接口(python下为wtpy)。 执行单元,是实盘中执行器的外围逻辑模块,用于执行交易指令和治理订单的。因为执行单元只能C++开发,所以不再提供C接口粘合层。Exec仿真器,则通过定时设置指标仓位,触发执行单元的外围逻辑,并对执行单元收回的交易指令进行模仿撮合,从而达到回测执行单元,剖析执行逻辑的体现的目标。 历史数据回放器,通过从数据引擎加载历史数据,并依据历史数据按数据周期进行回放,触发策略的重算,从而驱动策略逻辑,并在仿真器Mocker中进行仿真撮合,进而达到回测的目标。从上图能够看出,历史数据回放器,能够从WT数据文件系统、CSV文件以及数据库(目前只对接了Mysql)加载历史数据。 2、实盘框架 WonderTrader实盘框架相比回测框架就要简单很多。除了策略实现和回测框架是统一的(前文提到的策略一致性准则),底层外围为了对接实盘中不同的功能模块,所以就有了很大的不同。 策略引擎,也是一个策略组合,接管到数据组件播送的实时数据当前,触发策略重算,从而生成信号,并经由策略引擎轧平汇总当前,丢给执行器执行,而执行器调用不同的执行单元的执行逻辑当前,最终通过Trades交易通道模块,向交易柜台下达交易指令,从而实现一轮信号的生产执行的循环。目前策略引擎针对不同类型的策略实现上有一些区别,所以分成了3种策略引擎。 须要留神的是,数据组件其实是作为一个独立的伺服在运行的。目标也比拟明确,就是要实现读写拆散,并且能够同时向多个组合盘实例提供数据服务。数据组件通过调用DataWriter模块接口进行数据的落地,数据存储反对WT文件系统以及数据库两种模式。而策略则通过策略引擎调用数据管理器,通过DataReader从数据存储中读取文件到内存中,失去数据管理器中返回的数据切片。 还有一个绝对独立的模块,就是风控模块RiskMonitor。风控模块次要针对组合盘(策略引擎)的虚构资金进行风控,目前内置的风控模块反对的指标次要包含最大日内回撤、最大多日回撤等危险指标进行管制。 3、策略根本流程 上图是WonderTrader中CTA策略在实盘环境中的根本流程。 首先Parser从行情源接入行情数据,并解析成WonderTrader本人的tick数据tick数据先传递给CTATicker(数据同步器)进行工夫戳的同步控制每一笔tick,都会触发策略的on_tick回调当tick数据的工夫戳确定了上一个分钟的完结,就会判断是否有K线刚好闭合如果K线曾经闭合,则触发策略的on_bar回调如果策略订阅的全副K线都闭合了,则触发策略的on_schedule回调进行重算策略重算过程中,调整了指标仓位,最终汇总到策略引擎中进行汇总解决头寸汇总当前,分发给各个执行器进行执行执行器调用底层执行单元的逻辑收回下单指令最初下单指令通过交易通道Traders最终下达交易柜台 WonderTrader一共有三种不同的策略引擎,以用于不同利用场景的策略。除了HFT策略,策略间接对接交易通道Trader以外,另外两种策略的根本流程都和下面介绍的CTA根本流程是统一的。 结束语 因为篇幅无限,本文的介绍就到此结束了。置信通过本文的介绍,各位读者对于WonderTrader的整体架构曾经有了一个初步的认知了。如果有读者违心更进一步的理解WonderTrader,心愿这篇文章能够帮忙到各位。下一周,笔者将围绕数据处理,来介绍WonderTrader的数据处理机制,望各位读者届时多多捧场。 WonderTrader旨在给各位量化从业人员提供更好的轮子,将技术相干的货色都封装在平台中,力求给策略研发带来更好的策略开发体验。 最初再安利一下WonderTrader WonderTrader的github地址:https://github.com/wondertrad... WonderTrader官网地址:https://wondertrader.github.io wtpy的github地址:https://github.com/wondertrad... 市场有危险,投资需谨慎。以上陈说仅作为对于历史事件的回顾,不代表对将来的观点,同时不作为任何投资倡议。

March 15, 2021 · 1 min · jiezi

关于stl:STL核心编程

title: STL外围编程categories: [C++]tags: [编程语言]date: 2021/03/13 作者:hackett 微信公众号:加班猿 容器序列式容器string容器类外部封装了char ,治理这个字符串,是一个char 型的容器 构造函数 string(); string(const char *s); string(const string& str); string(int n , char c); 赋值操作 string& operator=(const char* s); string& operator=(const string &s); string& operator=(char c); string& assign(const char* s); string& assign(const char *s , int n); string& assign(const string& s); string& assign(int n , char c); 字符串拼接 string& operator+=(const char* str); string& operator+=(const char c); string& operator+=(const string& str); string& append(const char *s); string& append(const char *s, int n); string& append(const string &s); string& append(const string &s, int pos, int n) ...

March 13, 2021 · 3 min · jiezi

关于c++:C核心编程

title: C++外围编程categories: [C++]tags: [编程语言]date: 2021/03/11 作者:hackett 微信公众号:加班猿 内存散布模型堆区:new/delete,由程序员调配和开释,若程序员不开释,程序完结由操作系统回收栈区:寄存函数的参数值,局部变量等;由编译器主动调配开释动态存储区:寄存全局动态变量、部分动态变量、全局变量以及虚函数表常量存储区:全局常量、函数指针、常量数组、对函数指针代码区:寄存函数体的二进制代码援用必须初始化,初始化后不可扭转常量援用润饰形参实质是一个指针常量函数默认参数(语法:返回值类型 函数名 (参数 = 默认值){})函数占位参数(语法:返回值类型 函数名 (数据类型))函数重载:同一作用域下函数名雷同函数参数类型不同/个数不同/程序不同构造函数能够有参数,因而能够产生重载 调用对象时主动调用结构,无需手动调用,而且只会调用一次 调用规定如果用户定义有参构造函数,c++不在提供默认无参结构,然而会提供默认拷贝结构 如果用户定义拷贝构造函数,c++不会再提供其余构造函数 虚析构和纯虚析构虚析构语法:virtual ~类名(){} 纯虚析构语法:virtual ~类名() = 0; 抽象类,无奈实例化对象 共性解决父类指针开释子类对象 都须要有具体的函数实现 深/浅拷贝浅拷贝:简略的复制拷贝操作 深拷贝:在堆区从新申请内存,进行拷贝操作;属性有在堆区开拓的,肯定要本人提供拷贝构造函数,避免浅拷贝带来的反复开释堆区问题 初始化列表语法:构造函数():属性1(值1),属性2(值2),属性3(值3)… 动态成员动态成员变量所有对象共享同一份数据 在编译阶段分配内存 类内申明,类外初始化 动态成员函数所有对象共享一个函数 动态成员函数只能拜访动态成员变量 this指针定义this指针指向被调用的成员函数所属的对象 this指针是隐含每一个非动态成员函数内的一种指针 用处形参和成员变量同名时,能够用this指针来辨别 在类的非动态成员函数中返回对象自身,可应用return *this const常函数const在成员函数后:void func() const{} 常函数内不能够批改成员属性 成员属性申明前加mutable关键字,在常函数中仍然可批改 常对象const在申明对象前 常对象只能调用常函数 友元friend作用:让一个类或者函数拜访另一个类中的公有成员全局函数 类 成员函数 运算符重载加号实现两个自定义数据类型相加 左移能够输入自定义数据类型(配合友元) 递增实现本人的整型数据(前置递增返回援用,后置递增返回值) 赋值类中有属性指向堆区,做赋值操作时也会呈现深浅拷贝问题 关系让两个自定义数据类型对象进行比照操作 函数调用应用形式十分像函数的调用,因而称为仿函数,没有固定写法,非常灵活 继承继承形式public 继承下来的成员属性放弃不变 protected 继承下来的成员属性变为protected private 继承下来的成员属性变为private 对象模型父类公有成员被子类继承下来,被编译器暗藏后拜访不到 结构和析构程序先调用父类的构造函数,再调用子类的构造函数,析构是先调用子类的析构函数,再调用父类的析构函数 同名成员/动态成员解决拜访子类:间接拜访 ...

March 13, 2021 · 1 min · jiezi

关于c++:C注释的常见用法

C++正文的常见用法本文参考:https://www.cnblogs.com/aspir... 1. 根本用法单行正文: // //多行正文: / ..... / /* */2. 几种常见的正文标准文件头的标注 /** * @file Example.h * * @brief 对文件的简述 * * Details. * * * * @author XX.xx * * @email example@126.com * * @version 1.2.3.1(版本号) * * @date example@126.com * * @license GNU General Public License (GPL) * */命名空间的标注 /** * @brief 命名空间的简略概述 \n(换行) * 命名空间的具体概述 */namespace example { }类的标注 /** * @brief 类的简略概述 \n(换行) * 类的具体概述 */class example { };函数的标注 ...

March 10, 2021 · 1 min · jiezi

关于c++:IassPassSasS三种云服务区别

咱们能够把云计算了解成一栋大楼,而这栋楼又能够分为顶楼、两头、低层三大块。那么咱们就能够把Iass(基础设施)、Pass(平台)、Sass(软件)了解成这栋楼的三局部。基础设施在最下端,平台在两头,软件在顶端。别的一些“软”的层能够在这些层下面增加。 接下来咱们再别离用实例给大家介绍下Iass、Sass、Pass。 IaaS:Infrastructure-as-a-Service(基础设施即服务) 第一层叫做IaaS 举例:几年前如果你想在办公室或者公司的网站上运行一些企业应用,你须要去买服务器,或者别的昂扬的硬件来管制本地利用,能力让你的业务失常运行。 但当初能够租用IaaS公司提供的场外服务器,存储和网络硬件。这样一来,便大大的节俭了保护老本和办公场地。 PaaS:Platform-as-a-Service(平台即服务) 第二层就是所谓的PaaS 举例: PaaS公司在网上提供各种开发和散发利用的解决方案,比方虚构服务器和操作系统。这节俭了你在硬件上的费用,也让扩散的工作室之间的单干变得更加容易。网页利用治理,利用设计,利用虚拟主机,存储,平安以及利用开发合作工具等。 SaaS:Software-as-a-Service(软件即服务) 第三层也就是所谓SaaS 举例:生存中,简直咱们每一天都在接触SaaS云服务,比方:咱们平时应用的苹果手机云服务,网页中的一些云服务等。如果你要买网站服务器、云服务器,能够搜寻微信小程序“云来米”,邀请码8KY4FQ4,助你轻松上云。 Iaas和Paas之间的比拟 PaaS的次要作用是将一个开发和运行平台作为服务提供给用户,而IaaS的次要作用是提供虚拟机或者其余资源作为服务提供给用户。接下来,将在五个方面对PaaS和IaaS进行比拟: 1) 开发环境:PaaS根本都会给开发者提供一整套包含IDE在内的开发和测试环境,而IaaS方面用户次要还是沿用之前比拟相熟那套开发环境,然而因为之前那套开发环境在和云的整合方面比拟欠缺,所以应用起来不是很不便。 2) 反对的利用:因为IaaS次要是提供虚拟机,而且一般的虚拟机能反对多种操作系统,所以IaaS反对的利用的范畴是十分宽泛的。但如果要让一个利用能跑在某个PaaS平台不是一件轻松的事,因为不仅须要确保这个利用是基于这个平台所反对的语言,而且也要确保这个利用只能调用这个平台所反对的API,如果这个利用调用了平台所不反对的API,那么就须要对这个利用进行批改。 3) 凋谢规范:尽管很多IaaS平台都存在肯定的公有性能,然而因为OVF等协定的存在,使得IaaS在跨平台和防止被供应商锁定这两面是稳步前进的。而PaaS平台的状况则不容乐观,因为不论是Google的App Engine,还是Salesforce的http://Force.com都存在肯定的公有API。 4) 整合率和经济性: PaaS平台整合率是十分高,比方PaaS的代表Google App Engine能在一台服务器上承载成千上万的利用,而一般的IaaS平台的整合率最多也不会超过100,而且广泛在10左右,使得IaaS的经济性不如PaaS。 5) 计费和监管:因为PaaS平台在计费和监管这两方面不仅达到了IaaS平台所能企及的操作系统层面,比方,CPU和内存的使用量等,而且还能做到利用层面,比方,利用的反应时间(Response Time)或者利用所耗费的事务多少等,这将进步计费和治理的精确性。

March 10, 2021 · 1 min · jiezi

关于c++:大括号之谜C的列表初始化语法解析

摘要:有敌人在应用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过。有敌人在应用std::array时发现一个奇怪的问题:当元素类型是复合类型时,编译通不过。 struct S { int x; int y;};int main(){ int a1[3]{1, 2, 3}; // 简略类型,原生数组 std::array<int, 3> a2{1, 2, 3}; // 简略类型,std::array S a3[3]{{1, 2}, {3, 4}, {5, 6}}; // 复合类型,原生数组 std::array<S, 3> a4{{1, 2}, {3, 4}, {5, 6}}; // 复合类型,std::array,编译失败! return 0;}按说std::array和原生数组的行为简直是一样的,可为什么当元素类型不同时,初始化语法还会有差异?更蹊跷的是,如果多加一层括号,或者去掉内层的括号,都能让代码编译通过: std::array<S, 3> a1{{1, 2}, {3, 4}, {5, 6}}; // 原生数组的初始化写法,编译失败!std::array<S, 3> a2{{{1, 2}, {3, 4}, {5, 6}}}; // 外层多一层括号,编译胜利std::array<S, 3> a3{1, 2, 3, 4, 5, 6}; // 内层不加括号,编译胜利这篇文章会介绍这个问题的原理,以及正确的解决形式。 ...

March 8, 2021 · 3 min · jiezi

关于c++:设计模式之单例工厂发布订阅者模式设计模式

设计模式之单例、工厂、公布订阅者模式设计模式单例模式 保障一个类仅有一个实例,并提供一个该实例的全局拜访点 在软件系统中,常常有这样一些非凡的类,必须保障他们 在零碎中只存在一个实例,能力确保它们的逻辑正确性, 以及良好的效率 利用场景:DBPool 、读取配置文件 单例模式分类:1、懒汉式 -- 须要应用单例的时候,才进行初始化2、饿汉式 -- 未调用单例的时候,曾经进行初始化写一个单例模式的demo#include <iostream>#include <mutex>#include <thread> using namespace std; //设计线程的个数#define PTHREAD_NUM 20//懒汉式 饿汉式 单例模式的选型#define SINGELTON_SELECTOR 0 //单例模式 #if SINGELTON_SELECTOR //懒汉式 -- 调用的时候才初始化class Singleton{ private: Singleton(){ cout<<"Singleton construct 1111\n"; } ~Singleton(){ cout<<"Singleton destruct 1111\n"; } //禁止拷贝结构 Singleton(const Singleton &si) = delete; //禁止等号赋值 Singleton & operator=(const Singleton &si) = delete; public: static Singleton * getInstance(){ static Singleton m_singleton; return &m_singleton; }}; #else //饿汉式 -- 调用之前就曾经初始化好,调用的时候间接返回地址class Singleton{ private: Singleton(){ cout<<"Singleton construct 2222\n"; } ~Singleton(){ cout<<"Singleton destruct 2222\n"; } //禁止拷贝结构 Singleton(const Singleton &si) = delete; //禁止等号赋值 Singleton & operator=(const Singleton &si) = delete; static Singleton m_singleton; public: static Singleton * getInstance(){ return &m_singleton; }}; Singleton Singleton::m_singleton; #endif //定义一个互斥锁,保障只有一个线程在打印 单例变量的地址static mutex m;void print_address(){ Singleton* singleton = Singleton::getInstance(); m.lock(); cout<<singleton<<endl; m.unlock();}//测试单例模式void test_singleton(){ thread threads[PTHREAD_NUM]; for(auto &t : threads) t = thread(print_address); for(auto &t : threads) t.join();} int main(int argc,char * argv[]){ cout<<"main\n"; test_singleton(); } 工厂模式定义一个用于创建对象的接口,让子类决定实例化哪一个类。Factory Method使得一个类的实例化提早(目标:解耦,伎俩:虚函数)到子类在软件系统中,常常面临着创建对象的工作;因为需要的 变动,须要创立的对象的具体类型常常变动应用工厂模式提供一种“封装机制”来防止客户程序和这种“具 体对象创立工作”的紧耦合 来解决这个问题 ...

March 7, 2021 · 3 min · jiezi

关于c++:关于DevC不支持c11标准的解决

记录一下,不便找回!1.关上工具2.抉择编译选项3.没批改前4.勾选第一个方框,并增加 -std=c++11 ,而后确定就行了。

March 3, 2021 · 1 min · jiezi

关于c++:C-static-const-volatile-总结

constconst 位于 *的左侧: 用来润饰指针所指向的变量,指针指向常量。// 指针不容许扭转数据int b = 500;const int *a = &b;*a = 600; // error // 能够通过变量自身去批改int b = 500;const int *a = &b;b = 600;cout << *a << endl; // 600const 位于 *的右侧: 用来润饰指针自身,指针是常量。// 指针自身是常量,定义时须要初始化int b = 500;//int * const a; // errorint * const c = &b; // error*c = 600; // 失常,容许改值cout << *c << endl;const 用在成员函数中, 位于 function_name () const {}咱们在定义类的成员函数中,经常有一些成员函数不扭转类的数据成员。 也就是说这些函数的 readonly function,而有一些函数要批改类的数据成员的值。 如果在readonly function都加上const进行标识,无疑会进步程序的可读性。 其实还能进步程序的可靠性,已定义成const的成员函数,一旦希图扭转数据成员的值,则编译器按错误处理。 ...

March 2, 2021 · 2 min · jiezi

关于c++:Qtopencv调用camera的简单应用

最近须要应用opencv,就简略写了一个测试示例并记录下来。其中 QCamera 等相干的类同样能够实现以后性能。 仓库 性能反对有线或IP摄像头连贯反对视频流播放反对多分辨率设置反对视频录制反对拍照视频抽帧、拍照、录制等由独单线程解决测试可用的视频流 CCTV1 高清 http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8CCTV3 高清 http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8CCTV5+ 高清 http://ivi.bupt.edu.cn/hls/cctv5phd.m3u8CCTV6 高清 http://ivi.bupt.edu.cn/hls/cctv6hd.m3u8camara.h#ifndef CAMARA_H#define CAMARA_H#include <QImage>#include <QObject>#include <QThread>#include <QTimer>#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>Q_DECLARE_METATYPE(QImage);/* *@brief Camara 在独自线程中实现视频抽帧、拍照及录像性能 *@brief open、close 等是耗时操作,请保障在 Camara 线程工夫中实现调用 (可思考异步槽函数、QMetaObject::invokeMethod) */class Camara : public QObject{ Q_OBJECTpublic: Camara(); ~Camara(); bool isOpened() const; bool isTakevideoed() const; QString getSavePath() const; QSize getResolutions() const; bool isTakeVideo() const;signals: void updateImage(QImage); void statusChanged(bool isOpen);public slots: void openCamara(const QString &url); void openCamara(int index); void closeCamara(); void takePicture(); void takeVideo(); void setSavePath(const QString &path); void setResolutions(const QSize &size);private slots: void tbegin(); void tend(); void captureCamara();private: QString m_savepath; QAtomicInteger<bool> m_isTakepicture = false; QAtomicInteger<bool> m_isTakevideo = false; QAtomicInteger<bool> m_isflip = false; QScopedPointer<cv::VideoCapture> m_capture; QScopedPointer<cv::VideoWriter> m_writer; QTimer *m_timer = nullptr; QThread m_thread;};#endif // CAMARA_H#include "camara.h"#include <QDateTime>Camara::Camara(){ moveToThread(&m_thread); connect(&m_thread, &QThread::started, this, &Camara::tbegin); connect(&m_thread, &QThread::finished, this, &Camara::tend); m_thread.start(QThread::HighPriority);}Camara::~Camara(){ m_thread.quit(); m_thread.wait();}void Camara::tbegin(){ m_capture.reset(new cv::VideoCapture); m_writer.reset(new cv::VideoWriter); m_timer = new QTimer(this); m_timer->setTimerType(Qt::PreciseTimer); connect(m_timer, &QTimer::timeout, this, &Camara::captureCamara);}void Camara::tend(){ closeCamara();}void Camara::openCamara(const QString &url){ if (!m_capture->isOpened() && m_capture->open(url.toLatin1().data())) { m_isflip = false; m_timer->start(33); emit statusChanged(true); } else { emit statusChanged(false); }}void Camara::openCamara(int index){ if (!m_capture->isOpened() && m_capture->open(index)) { m_isflip = true; m_timer->start(33); emit statusChanged(true); } else { emit statusChanged(false); }}void Camara::closeCamara(){ m_timer->stop(); if (m_writer->isOpened()) m_writer->release(); if (m_capture->isOpened()) m_capture->release();}void Camara::captureCamara(){ cv::Mat originalframe; cv::Mat flipframe; *m_capture >> originalframe; if (m_isflip) cv::flip(originalframe, flipframe, 1); else flipframe = originalframe; QImage img = QImage(flipframe.data, flipframe.cols, flipframe.rows, QImage::Format_RGB888).rgbSwapped(); if (!img.isNull()) { if (m_isTakepicture) { m_isTakepicture = !m_isTakepicture; QString name = m_savepath + QDateTime::currentDateTime().toString("yyyy-MM-hh hh_mm_ss") + ".jpeg"; img.save(name, "jpeg"); } if (m_isTakevideo) { *m_writer << flipframe; } updateImage(img); } originalframe.release(); flipframe.release();}void Camara::takePicture(){ m_isTakepicture = true;}void Camara::takeVideo(){ if (!m_isTakevideo) { QString name = m_savepath + QDateTime::currentDateTime().toString("yyyy-MM-hh hh_mm_ss") + ".avi"; if (m_writer->open(name.toLatin1().data(), cv::VideoWriter::fourcc('M', 'J', 'P', 'G'), 30.0, cv::Size(m_capture->get(cv::CAP_PROP_FRAME_WIDTH), m_capture->get(cv::CAP_PROP_FRAME_HEIGHT)), true)) { m_isTakevideo = true; } } else { m_isTakevideo = false; m_writer->release(); }}void Camara::setSavePath(const QString &path){ m_savepath = path + '/';}void Camara::setResolutions(const QSize &size){ if (m_capture->isOpened()) { m_capture->set(cv::CAP_PROP_FRAME_WIDTH, size.width()); m_capture->set(cv::CAP_PROP_FRAME_HEIGHT, size.height()); }}QString Camara::getSavePath() const{ return m_savepath;}bool Camara::isOpened() const{ return m_capture->isOpened();}bool Camara::isTakevideoed() const{ return m_isTakevideo;}QSize Camara::getResolutions() const{ QSize ret; ret.setWidth(m_capture->get(cv::CAP_PROP_FRAME_WIDTH)); ret.setHeight(m_capture->get(cv::CAP_PROP_FRAME_HEIGHT)); return ret;}bool Camara::isTakeVideo() const{ return m_writer->isOpened();}

March 1, 2021 · 2 min · jiezi

关于c++:c虚继承多继承

看这一篇文章之前强烈建议先看以下我之前公布的 虚指针,虚函数分析 例1: 以下代码输入什么? #include <iostream>using namespace std;class A {protected: int m_data;public: A(int data = 0) {m_data=data;} int GetData() { return doGetData(); } virtual int doGetData() { return m_data; }};class B : public A{protected: int m_data;public: B(int data = 1) { m_data = data; } int doGetData() { return m_data; }};class C: public B{protected: int m_data;public: C(int data=2) { m_data = data; }};int main(int argc, char const *argv[]){ C c(10); cout << c.GetData() << endl; cout << c.A::GetData() << endl; cout << c.B::GetData() << endl; cout << c.C::GetData() << endl; cout << c.doGetData() << endl; cout << c.A::doGetData() << endl; cout << c.B::doGetData() << endl; cout << c.C::doGetData() << endl; return 0;}构造函数从最初始的基类开始结构,各个类的同名变量没有造成笼罩,都是独自的变量。 ...

February 28, 2021 · 3 min · jiezi

关于c++:PAT甲级1105-Spiral-Matrix

题目粗心将给定的N个正整数按非递增的程序,填入“螺旋矩阵”,所谓“螺旋矩阵”,是指从左上角第1个格子开始,按顺时针螺旋方向填充,要求矩阵的规模为m行n列,满足条件:m*n等于N;m>=n;且m-n取所有可能值中的最小值. 算法思路此题的求解步骤分为求解矩阵规模和填充矩阵 1、求解矩阵规模因为须要行和列都尽可能的相近,所以初始取col为根号n,只有$n $%$ col != 0$,col就递加,从而获取列col和行row。2、填充矩阵这里采纳设置矩阵的4个边界(左右高低),别离为$left=0,right=col-1,up=0,bottom=row-1$,依照从左到右,从上到下,从右到左,从下到上的程序顺次填充,每一次填充结束就更新边界,比方依照从左往右填充了一行就更新上边界up(++up),只有每次填充结束后,index>=n,阐明填充完结。 提交后果 AC代码#include <cstdio>#include <cmath>#include <iostream>#include <algorithm>using namespace std;int main() { int n; scanf("%d", &n); int a[n]; for (int i = 0; i < n; ++i) { scanf("%d", &a[i]); } sort(a, a + n, [&](int a, int b) { return a > b; }); // 计算m和n int col = (int) sqrt(n * 1.0); while (n % col != 0) { --col; } int row = n / col; int result[row][col]; int index = 0;// 标记以后填充的元素 // 定义数组的左右高低边界 int left = 0, right = col - 1, up = 0, bottom = row - 1; while (index < n) { // 从左到右 for (int i = left; i <= right; ++i) { result[up][i] = a[index++]; } // 更新上界 ++up; if (index >= n) break; // 从上到下 for (int i = up; i <= bottom; ++i) { result[i][right] = a[index++]; } // 更新右边界 --right; if (index >= n) break; // 从右到左 for (int i = right; i >= left; --i) { result[bottom][i] = a[index++]; } // 更新下界 --bottom; if (index >= n) break; // 从下到上 for (int i = bottom; i >= up; --i) { result[i][left] = a[index++]; } // 更新左边界 ++left; } for (int i = 0; i < row; ++i) { for (int j = 0; j < col; ++j) { printf("%d", result[i][j]); if (j < col - 1) printf(" "); } printf("\n"); } return 0;}

February 28, 2021 · 2 min · jiezi

关于c++:PAT甲级1057-Stack

题目粗心现有一个栈,对其进行执行N次基本操作,该操作有三种类型,别离是Pop,Push和PeekMedian,代表了出栈,入栈和获取栈中元素的中位数,要求依照每一次输出的指令进行相应的输入 算法思路这里最为简单的就是实时的获取栈中的中位数,应用拷贝到数组排序或者set汇合都会超时,这里借助分块的思维来实现实时获取中位数,除了应用栈st来进行入栈和出栈的基本操作之外,还须要应用block数组和table数字别离保留每一块蕴含的数字个数和每一个数字呈现的次数,第i块(i>=0)保留的数字范畴是[iblockSize,(i+1)blockSize-1],那么求解栈的中位数的办法如下: 1、获取中位数的地位k=st.size()/2+st.size()%2;2、应用sum示意以后累计的数字的个数3、查找第k大的数字所在的块号i,第一个使得sum+block[i]>=k成立的就是第k大的数字所在的块号4、查找第k大的数字在第i号块中的对应数字num,第i号块的第一个数字为iblockSize,让num=iblockSize,而后遍历num数字,只有sum+table[num]==k,就阐明以后的num就是第k大的数字。提交后果 AC代码#include <iostream>#include <stack>#include <string>#include <cmath>#include <cstring>using namespace std;int table[100010];stack<int> st;void push(int block[],int x){ st.push(x); ++table[x]; int blockSize = (int)sqrt(100001*1.0); ++block[x/blockSize];}int pop(int block[]){ int x = st.top(); st.pop(); --table[x]; int blockSize = (int)sqrt(100001*1.0); --block[x/blockSize]; return x;}int getMedian(const int block[]){ // 获取中位数的地位 int k = st.size()/2+st.size()%2; // 累计以后曾经呈现过的数字个数 int sum = 0; int blockSize = (int)sqrt(100001*1.0); // 查找第一个使得sum+block[i]>=k的块号i int i; for(i=0;i<blockSize;++i){ if(sum+block[i]<k){ sum += block[i]; }else{ break; } } // 第k大的数字在第i块中 int num;// 保留以后遍历的数字 int start = i*blockSize; int end = (i+1)*blockSize; for(num=start;num<end;++num){ if(sum+table[num]<k){ sum += table[num]; }else{ // 以后num就是第k大的数字 break; } } return num;}int main() { int blockSize = (int)sqrt(100001*1.0); int block[blockSize]; memset(block,0,sizeof(block)); int n; scanf("%d",&n); string str; int num; for(int i=0;i<n;++i){ cin>>str; if(str=="Pop"){ if(st.empty()){ printf("Invalid\n"); }else{ printf("%d\n",pop(block)); } }else if(str=="Push"){ cin>>num; push(block,num); }else{ if(st.empty()){ printf("Invalid\n"); }else{ printf("%d\n",getMedian(block)); } } } return 0;}

February 28, 2021 · 1 min · jiezi

关于c++:PAT甲级1129-Recommendation-System

题目粗心依据用户每次点击的货色的编号,输入他在点以后编号之前应该给这个用户举荐的商品的编号,只举荐k个,也就是输入用户已经点击过的商品编号的最多的前k个,如果恰好两个商品有雷同的点击次数,就输入编号较小的那个。 算法思路这里依据题意很显著在每一次输出商品编号的时候须要依据排序后的后果输入举荐产品的编号,然而应用sort进行排序肯定会超时,所以应用set汇合的主动排序功能,排序根据的是商品的编号val和之前呈现的频率,所以将这两个属性封装为一个构造体Commodity,并且重载小于号,使得在每一次增加商品后就会依照自定义的排序规定进行排序。在每一次输出的时候,只有不是第一次点击商品,那么就输入set汇合中前k个商品的音讯,而后再将以后商品增加到set汇合中,因为该商品可能曾经呈现在了set汇合中,所以要先查找以后商品并删除,而后再将新的商品音讯增加其中。查找商品的办法就是应用num数组统计曾经增加进set汇合中的每一个商品编号对应的频率,而后将以后商品的编号和频率封装为Commodity,而后在set汇合中查找即可。 提交后果 AC代码#include <cstdio>#include <set>#include <cstring>using namespace std;struct Commodity{ int val; int fre; bool operator < (Commodity node) const{ return this->fre!=node.fre?this->fre>node.fre:this->val<node.val; } Commodity(int _val, int _fre){ val = _val; fre = _fre; }};int main() { int n,k; scanf("%d %d",&n,&k); set<Commodity> s; int num[n+1];// 统计每一个数字呈现的频率 memset(num,0,sizeof(num)); for(int i=1;i<=n;++i){ int query; scanf("%d",&query); if(i!=1){ printf("%d:",query); // 只有不是第一个数字就输入汇合s中前k个数字 int cnt = 0; for(auto it:s){ if(cnt<k){ printf(" %d",it); ++cnt; }else{ break; } } printf("\n"); } // 增加以后数字到汇合中 Commodity toBeFound = Commodity{query, num[query]}; auto it = s.find(toBeFound); if(s.find(toBeFound)!=s.end()){ // 以后数字在s汇合中呈现过 s.erase(it); } ++num[query]; s.insert(Commodity{query, num[query]}); } return 0;}

February 28, 2021 · 1 min · jiezi

关于c++:5分钟掌握C中的三种继承方式

public 形式继承基类成员对派生类的可见性对派生类来说,基类的私有成员和爱护成员可见,基类的私有成员和爱护成员作为派生类的成员时,它们都放弃原有的状态;基类的公有成员不可见,基类的公有成员依然是公有的,派生类不可拜访基类中的公有成员。 基类成员对派生类对象的可见性对派生类对象来说,基类的私有成员是可见的,其余成员是不可见的。 所以,在私有继承时,派生类的对象能够拜访基类中的私有成员,派生类的成员函数能够拜访基类中的私有成员和爱护成员。 简略来说,派生类能拜访基类的public, protected成员,继承过去权限不变,派生类对象只能拜访基类public成员。 测试代码如下: class A{private: int m_data1; void print1() { cout << "private print1" << endl; }protected: int m_data2; void print2() { cout << "protected print2" << endl; }public: A(int x = 1, int y = 2, int z = 3) : m_data1(x), m_data2(y), m_data3(z) {} int m_data3; void print3() { cout << "protected print3" << endl; }};class B : public A{public: void test_public() { cout << m_data3 << endl; print3(); } void test_protected() { cout << m_data2 << endl; print2(); } void test_private() { // 上面两行编译不过,B类内无法访问父类的公有成员 // cout << m_data1 << endl; // print1(); }};int main(int argc, char const* argv[]){ B b; b.test_public(); b.test_protected(); b.test_private(); cout << b.m_data3 << endl; // cout << b.m_data2 << endl; // 编译不过,子类对象无法访问父类protected的成员 // cout << b.m_data1 << endl; // 编译不过,子类对象无法访问父类private的成员 return 0;}private 形式继承基类成员对其对象的可见性与个别类及其对象的可见性雷同,私有成员可见,其余成员不可见 ...

February 28, 2021 · 3 min · jiezi

关于c++:PAT甲级1130-Infix-Expression

题目粗心给定一颗形象语法二叉树,要求输入其中序遍历序列 算法思路应用动态数组nodes存储每一个结点的信息,应用father数组记录所有结点的父亲结点,这样在遍历father的时候其值为0的就是根结点下标root。对于其中序遍历的输入,其特点是对于根结点没有左右括号,除此之外只有有孩子存在就肯定有左右括号,单目运算肯定在右边,而后就是双目运算和数字。 提交后果 AC代码#include <cstdio>#include <algorithm>#include <string>#include <iostream>#include <cstring>using namespace std;struct Node{ string data; int left{}; int right{};}nodes[25];int root = 0;void inOrder(int r){ if(r==-1){ return; } if(r!=root&&(nodes[r].left!=-1||nodes[r].right!=-1)){ // 根节点没有左右括号,只有有孩子,就有左括号 printf("("); } inOrder(nodes[r].left); cout<<nodes[r].data; inOrder(nodes[r].right); if(r!=root&&(nodes[r].left!=-1||nodes[r].right!=-1)){ // 根节点没有左右括号,只有有孩子,就有右括号 printf(")"); }}int main() { int n; scanf("%d",&n); int father[n+1]; memset(father,0,sizeof(father)); for(int i=1;i<=n;++i){ cin>>nodes[i].data>>nodes[i].left>>nodes[i].right; father[nodes[i].left] = i; father[nodes[i].right] = i; } for(int i=1;i<=n;++i){ if(father[i]==0){ root = i; break; } } inOrder(root); return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:PAT甲级1128-N-Queens-Puzzle

题目粗心N皇后问题的改版,输出的每一行数据为一种棋盘摆放地位,其下标为列,值为行,保障每一列不反复,判断是否是该N皇后问题的解 算法思路只需判断以后N个棋子是否在同一行或者对角线上,判断的办法就是在输出每一个棋子的地位的时候就去与后面的所有棋子比拟其行是否一样或者判断行标之差的绝对值是否等于列表之差的绝对值,如果是阐明不是N皇后的解,输入NO,否则输入YES 提交后果 AC代码#include <cstdio>#include <algorithm>using namespace std;int main() { int k; scanf("%d",&k); for(int i=0;i<k;++i){ int n; scanf("%d",&n); int solution[n+1]; bool isAns = true;// 是否是N皇后的解 for(int j=1;j<=n;++j){ scanf("%d",&solution[j]); for(int x=1;x<j;++x){ if(solution[j]==solution[x]||abs(j-x)==abs(solution[x]-solution[j])){ isAns = false; break; } } } if(isAns){ printf("YES\n"); }else{ printf("NO\n"); } } return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:PAT甲级1127-ZigZagging-on-a-Tree

题目粗心给定一颗二叉树的中序和后序序列,要求依照zigzagging的形式输入该二叉树的层序遍历 算法思路首先依据中序和后序遍历序列建设该二叉树,而后在层序遍历中应用level记录以后所在档次,每一次将以后层的所有结点都出队到temp数组中,更新子节点的level并入队,如果level是奇数就逆序temp,而后增加到ans数组中保留最终的层序遍历后果,++level。最初输入ans数组中的后果即可。 提交后果 AC代码#include <cstdio>#include <queue>#include <vector>#include <algorithm>using namespace std;// 中序,后序和中序序列在in数组中的下标int in[40],post[40],inIndex[40];int n;// 顶点数目vector<int> ans;// zigzagging层序遍历序列struct Node{ int v{}; int level{}; Node* left = nullptr; Node* right = nullptr;};Node* createTree(int postL,int postR,int inL){ if(postL>postR){ return nullptr; } Node* root = new Node; root->v = post[postR]; // 获取根节点在中序遍历中的地位 int rootIndex = inIndex[root->v]; // 左子树长度 int leftSize = rootIndex-inL; root->left = createTree(postL,postL+leftSize-1,inL); root->right = createTree(postL+leftSize,postR-1,rootIndex+1); return root;}// level为偶数从左往右,为奇数从右往左void layerOrder(Node* root){ queue<Node*> que; root->level = 1; que.push(root); int level = 1; while (!que.empty()){ int len = que.size(); vector<int> temp; // 一次性遍历每一层的结点,并保留在temp中 for(int i=0;i<len;++i){ Node* t = que.front(); que.pop(); if(t->left){ t->left->level = t->level + 1; que.push(t->left); } if(t->right){ t->right->level = t->level + 1; que.push(t->right); } temp.push_back(t->v); } // 档次为奇数得逆序 if(level%2!=0){ reverse(temp.begin(),temp.end()); } for(auto v:temp){ ans.push_back(v); } ++level; }}int main() { scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%d",&in[i]); inIndex[in[i]] = i; } for (int i = 0; i < n; ++i) { scanf("%d",&post[i]); } Node* root = createTree(0,n-1,0); layerOrder(root); for(int i=0;i<ans.size();++i){ printf("%d",ans[i]); if(i<ans.size()-1){ printf(" "); } } return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:PAT甲级1126-Eulerian-Path

题目粗心给定N个顶点M条边的无向图,判断该图是Eulerian,semi-Eulerian还是non-Eulerian,并输入每一个顶点的度。 算法思路首先得理清几个概念 Eulerian path:恰好拜访图中所有顶点的门路Eulerian circuit:Eulerian path的终点和起点雷同Eulerian: 在一个连通图中所有顶点的度为偶数semi-Eulerian:连通图含有Eulerian path但没有 Eulerian circuit,即连通图中只有两个顶点的度为奇数non-Eulerian:既不是Eulerian也不是semi-Eulerian对于顶点的度间接应用degree数组统计输入即可,而后咱们判断以后图是否是连通图,判断办法就是从任一终点应用深度优先搜寻,如果该连通重量的顶点数目和N雷同,就阐明该图连通,否则就不是连通图,输入Non-Eulerian,而后再统计每一个顶点的度是否是偶数,如果都是偶数,输入Eulerian,如果只有两个顶点的度为奇数,输入Semi-Eulerian,否则输入Non-Eulerian。 提交后果 AC代码#include <cstdio>using namespace std;int G[505][505];int degree[505];// 每一个顶点的度bool visited[505];// 拜访标记数组int cnt = 0;// 顶点为1的连通重量的顶点数目int n,m;void DFS(int start){ visited[start] = true; ++cnt; for(int i=1;i<=n;++i){ if(!visited[i]&&G[start][i]!=0){ DFS(i); } }}void printDegree(){ for(int i=1;i<=n;++i){ printf("%d",degree[i]); if(i<n) printf(" "); } printf("\n");}int main() { scanf("%d %d",&n,&m); for(int i=0;i<m;++i){ int a,b; scanf("%d %d",&a,&b); G[a][b] = G[b][a] = 1; ++degree[a]; ++degree[b]; } // 统计从顶点1登程的连通重量的顶点数 DFS(1); printDegree(); if(cnt!=n){ // 该图不连通 printf("Non-Eulerian"); }else{ int evenDegree = 0;// 度为偶数的顶点个数 for(int i=1;i<=n;++i){ if(degree[i]%2==0){ ++evenDegree; } } if(evenDegree==n){ printf("Eulerian"); }else if(evenDegree==n-2){ printf("Semi-Eulerian"); }else{ printf("Non-Eulerian"); } } return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:PAT甲级1125-Chain-the-Ropes

题目粗心给定N段绳子,将这N段绳子顺次对折,任意两段对折后绳子的长度为和的一半,要求给出对折这N段绳子后最长的长度。 算法思路因为对折后的绳子在之后又会持续对折,所以尽可能将长的绳子对折次数缩小能力取得最长的绳子,所以将所有的绳子进行升序排序,而后顺次对折就是最初的答案。 提交后果 AC代码#include <cstdio>#include <algorithm>using namespace std;int main() { int n; scanf("%d",&n); int a[n]; for(int i=0;i<n;++i){ scanf("%d",&a[i]); } sort(a,a+n); int b = a[0]; for(int i=1;i<n;++i){ b = (b+a[i])/2; } printf("%d",b); return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:PAT甲级1124-Raffle-for-Weibo-Followers

题目粗心给定M条微博转发条目,针对每一个用户的转发条目,从中抽取一些人作为winner并给予奖品,第一个获奖的人是第S次转发的人,下一个获奖的人为上一个获奖人的地位加N,同一个人不能获奖两次,当初要求你输入获奖名单,对于没有人获奖的状况,输入Keep going...。 算法思路应用一个map——winner记录所有曾经获奖的人,winnerIndex记录下一个获奖的人的地位,如果s>m,阐明没有人获奖,输入Keep going...。否则就依照程序输出每一条转发的微博条目,只有以后人的地位i==winnerIndex,并且winner[a] = false,就阐明此人为以后获奖的人,否则就++winnerIndex往下查找 提交后果 AC代码#include <unordered_map>#include <string>#include <iostream>#include <cstdio>using namespace std;int main() { int m,n,s;// 转发数目,跳跃数目,起始获奖下标 scanf("%d %d %d",&m,&n,&s); if(s>m){ cout<<"Keep going..."; return 0; } // 记录曾经获奖的人 unordered_map<string,bool> winner; // 下一个获奖人的下标 int winnerIndex = s; for(int i=1;i<=m;++i){ string a; cin>>a; if(i==winnerIndex){ if(!winner[a]){ // 以后人没有得过奖 cout<<a<<endl; winner[a] = true; winnerIndex += n; }else{ ++winnerIndex; } } } return 0;}

February 27, 2021 · 1 min · jiezi

关于c++:大厂动态规划面试汇总重量级干货彻夜整理

注:本文是BAT真题收录很值得大家花心思看完,看完会有播种。 前言算法是面试大公司必考的我的项目,所以面试前筹备好算法至关重要,明天整顿的常见的动静布局题目,心愿能够帮到大家。 要想学习其余绝世武功,要先打好根底。算法属于内功,则更为重要。 匪徒抢劫题目:匪徒抢劫一排房间,每个房间都有钱,不能抢劫两个相邻的房间,要求抢的钱最多。数组如:[2,7,9,3,1] 思路:当输出房间数为0,1,2时,这个很好判断,当输出房间数字大于3时,就要用到动静布局了,方程是: dp[i]是当抢到第i个数时,能抢到最大值,从部分最大值推到最终后果最大。 如果抢到第5个房间,那么第5个房间有二种状况,抢不和不被抢,因为只能隔房间。 如果抢到第4个房间,有个最大值;抢到第3个房间,有个最大值。如果加上第3房间最大值,加上第5房间的最大值,大于抢到第4个房间时的最大时。那就抢3,5而不抢4,反而,就按抢4的策略。 这样从前往后推,最初的后果肯定是最大的。代码如下: 跳台阶题目形容:有 N 阶楼梯,每次可上一阶或两阶,求有多少种上楼梯的办法 先来剖析下这个问题: 当N=1时,这个很好了解,只能跨1步这一种了当N=2时,你每次能够跨1步或2步,那就是走2步或走两个1步当N=3时,因为你能够跨1步或2步,那你在台阶1或2都能行。要计算到台阶1有多少种走法,到台阶2有多少种走法,而后2种相加,顺次逆推。 当N=4时,你在台阶2或3都能行,计算到台阶2有多少种走法,到台阶3有多少种走法,而后2者相加,顺次逆推。 总结如下:你会发现,这是斐波拉切数列,应用递归呈现反复计算问题,所以抉择动静布局算法。 层数公式种数1f(1)=112f(2)=223f(3)=f(1)+f(2)34f(4)=f(2)+f(3)5第三层:3种(在第一层走2步或在第二层走1步) 第四层:5种(在第二层走2步或在第三层走1步) 代码如下: i,j首先赋边界值,res保留i+j的值,每次后退,i,j,res的值都会被赋到后面后果。下面的算法是底向上,递归相当于自顶向下,防止了反复计算。 矩形最小门路和题目:给定一个,蕴含非负整数的 m x n 网格。请找出一条,从左上角到右下角的门路。使得门路上,所有数字总和为最小,每次只能向下,或者向右挪动一步。输出:[[1,3,1], [1,5,1], [4,2,1]]输入: 7解释: 因为门路 1→3→1→1→1 的总和最小。先看动静方程: i值j值dp方程i>0j=0dpi = dpi−1 + gridii=0j>0dp0 = dp0 + grid0i<0j>0dpi = min(dpi−1, dpi) + gridi阐明:因为 i=0 和 j=0 是临界条件,所以要先求进去。当 i>0 和 j>0 时,看如上数组,5 能够由上方3,或者左方 1 走过去。当走5的时候,要选取上方3对应的dp,与左方1 对应的dp进行比拟,抉择较小值累加,这样走进去的才是最小值。最初推出,到右下角的最小值。代码如下: sum用来存储,从0到sumi门路的最小和,看看每次sum的变动,sum1=7意思是,从0到1门路最小和是7。程序先把,第2行对应的sum都求进去,再把第2列对应的sum都求进去,最初求sum2就很容易了。最初,sumi-1就是推出的最小值,上述代码就是dp方程的实现。 划分数组为两个相等的子集题目:输出:[1, 5, 11, 5], 输入:[1, 5, 5]和[11] ...

February 27, 2021 · 2 min · jiezi

关于c++:为什么使用初始化列表会快一些

查看上面两段代码: // (1)class classA {...};class classB{public: classB(classA a) {mA = a;}private: classA mA;};// (2)class classA {...};class classB{public: classB(classA a): mA(a) {}private: classA mA;};为什么2的效率要高? 初始化列表的效率要高。 首先把数据成员按类型分类 内置数据类型,复合类型(指针,援用)用户定义类型(类类型)分状况阐明: 对于类型1,在成员初始化列表和构造函数体内进行,在性能和后果上都是一样的对于类型2,后果上雷同,然而性能上存在很大的差异初始化列表,顾名思义,是对成员数据进行初始化,而不是赋值,赋值操作在构造函数体内! 好比: classA a;classA b = a;和 classA a;classA b;b = a;的区别。 上述的代码1不够清晰,咱们写成上面这样: #include <iostream>using namespace std;class classA {public: classA() { cout << "classA()" << endl; } classA(const classA& a) { cout << "copy classA()" << endl; } ~classA() { cout << "~classA()" << endl; } classA& operator=(const classA& a) { cout << "operator=" << endl; return *this; }};class classB{public: classB(classA a) : mA(a) {}private: classA mA;};int main(){ classA a; classB b(a);}// 打印如下://1 classA()//2 copy classA()//3 copy classA()//4 ~classA()//5 ~classA()//6 ~classA()classA a; 调用默认构造函数结构a对象classB(classA a) : mA(a) {}, classB类的构造函数里的 classA a形参拷贝1申明的对象classB(classA a) : mA(a) {}, 初始化列表拷贝2里的形参a的对象2里的形参a析构1里的a析构对象b里的mA析构4,5,6的析构程序没有验证。 ...

February 27, 2021 · 1 min · jiezi

关于c++:虚函数虚表深度剖析

面向对象,从繁多的类开始说起。 class A{private:    int m_a;    int m_b;}; 这个类中有两个成员变量,都是int类型,所以这个类在内存中占用多大的内存空间呢? sizeof(A), 8个字节,一个int占用四个字节。下图验证: 这两个数据在内存中是怎么排列的呢? 原来是这样,咱们依据debug进去的地址画出a对象在内存的结构图 如果 class A 中蕴含成员函数呢? A 的大小又是多少? class A{public:    void func1() {}    private:    int m_a;    int m_b;}; 间接通知你答案,类的成员函数多大? 没人能答复你,并且不是本文的重点,类的成员函数是放在代码区的,不算在类的大小内。 类的对象共享这一段代码,试想,如果每一个对象都有一段代码,光是存储这些代码得占用多少空间?所以同一个类的对象共用一段代码。 共用同一段代码怎么辨别不同的对象呢? 实际上,你在调用成员函数时,a.func1() 会被编译器翻译为 A::func1(&a),也就是A* const this, this 就是 a 对象的地址。 所以依据this指针就能找到对应的数据,通过这同一段代码来解决不同的数据。 接下来咱们讨论一下继承,子类继承父类,将会继承父类的数据,以及父类函数的调用权。 以下的测试能够验证这个状况。 class A{public:    void func1() { cout << "A func1" << endl; }private:    int m_a;    int m_b;};class B : public A{public:    void func2() { cout << "B func2" << endl; }private:    int m_c;};int main(int argc, char const* argv[]){    B b;    b.func1();    b.func2();    return 0;} 输入: // A func1// B func2 那么对象b在内存中的构造是什么样的呢? 继承关系,先把a中的数据继承过去,再有一份本人的数据。 每个蕴含虚函数的类都有一个虚表,虚表是属于类的,而不是属于某个具体的对象,一个类只须要一个虚表即可。同一个类的所有对象都应用同一个虚表。 为了指定对象的虚表,对象外部蕴含指向一个虚表的指针,来指向本人所应用的虚表。为了让每个蕴含虚表的类的对象都领有一个虚表指针,编译器在类中增加了一个指针,*__vptr,用来指向虚表。这样,当类的对象在创立时便领有了这个指针,且这个指针的值会主动被设置为指向类的虚表。 class A{public:    void func1() { cout << "A func1" << endl; }    virtual void vfunc1() { cout << "A vfunc1" << endl; }private:    int m_a;    int m_b;}; cout << sizeof(A);, 输入12,A中包含两个int型的成员变量,一个虚指针,指针占4个字节。 a的内存构造如下: 虚表是一个函数指针数组,数组里寄存的都是函数指针,指向虚函数所在的地位。 对象调用虚函数时,会依据虚指针找到虚表的地位,再依据虚函数申明的程序找到虚函数在数组的哪个地位,找到虚函数的地址,从而调用虚函数。 调用一般函数则不像这样,一般函数在编译阶段就指定好了函数地位,间接调用即可。 class A{public:    void func1() { cout << "A func1" << endl; }    virtual void vfunc1() { cout << "A vfunc1" << endl; }private:    int m_a;    int m_b;};class B : public A{public:    void func1() { cout << "B func1" << endl; }    virtual void vfunc2() { cout << "B vfunc2" << endl; }private:    int m_a;}; 像这样,B类继承自A类,B中又定义了一个虚函数vfunc2, 它的虚表又是怎么样的呢? 给出论断,虚表如下图所示: 咱们来验证一下: A a;B b;void(*avfunc1)() = (void(*)()) *(int*) (*(int*)&a);void (*bvfunc1)() = (void(*)()) *(int*) *((int*)&b);void (*bvfunc2)() = (void(*)()) * (int*)(*((int*)&b) + 4);avfunc1();bvfunc1();bvfunc2();来解释一下代码: void(*avfunc1)() 申明一个返回值为void, 无参数的函数指针 avfunc1, 变量名代表咱们想要取A类的vfunc1这个虚函数。 右半局部的第一局部,(void(*)()) 代表咱们最初要转换成对应上述类型的指针,左边须要给一个地址。 咱们看 (*int(*)&a), 把a的地址强转成int*, 再解援用失去 虚指针的地址。 ...

February 27, 2021 · 1 min · jiezi

关于c++:OpenCV中二维坐标顺序

OpenCV的二维图像中,通过rows、cols、x、y四个属性来示意大小或地位,容易混同。 rows代表的是行,cols代表的是列。 x在cols上,y在rows上。(容易混同) 应留神的构造函数矩阵Mat img(int rows,int cols,int type);//后行(宽)后列(高) 矩形Rect rect(int x,int y,int width, int height);//先横坐标后纵坐标,width对应cols,height对应rows 点Point p(int x,int y);//先横坐标后纵坐标 尺寸Size size(int width,int height);//先宽(行)后高(列) at<>()函数img.at<type>(y,x);//先纵坐标后横坐标 img.at<type>(Point(x,y));//参数为点则先横坐标后纵坐标·

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1123-Is-It-a-Complete-AVL-Tree

题目粗心给定一颗均衡二叉树的插入序列,要求输入它的层序序列并判断是否为齐全二叉树。 算法思路首先须要对该AVL树进行建树操作(具体不细说,看代码即可),而后对该树进行层序遍历,一边输入层序变量序列,一遍判断该树是否是齐全二叉树判断二叉树是否是齐全二叉树的办法为: 1、如果以后节点只有右孩子,肯定不是。2、如果以后节点有孩子,然而其前驱节点中存在孩子缺失的状况,那么肯定不是。提交后果 AC代码#include<cstdio>#include<queue>#include<set>using namespace std;struct Node { int v; Node *left; Node *right; int height;};int n;//生成新的结点Node *newNode(int v) { Node *w = new Node; w->v = v; w->height = 1; w->left = nullptr; w->right = nullptr; return w;}int getHeight(Node *root) { if (root == nullptr) { return 0; } else { return root->height; }}int getBalancedFactor(Node *root) { return getHeight(root->left) - getHeight(root->right);}void updateHeight(Node *&root) { root->height = max(getHeight(root->left), getHeight(root->right)) + 1;}void L(Node *&root) { Node *temp = root->right; root->right = temp->left; temp->left = root; updateHeight(root); updateHeight(temp); root = temp;}void R(Node *&root) { Node *temp = root->left; root->left = temp->right; temp->right = root; updateHeight(root); updateHeight(temp); root = temp;}void insert(Node *&root, int x) { if (root == nullptr) { root = newNode(x); return; } if (root->v < x) { insert(root->right, x); // 插入实现后得得更新root的高度 updateHeight(root); if (getBalancedFactor(root) == -2) { if (getBalancedFactor(root->right) == -1) { //RR型须要对root左旋一次 L(root); } else if (getBalancedFactor(root->right) == 1) { // RL型,先对root->right右旋,再对root左旋 R(root->right); L(root); } } } else { insert(root->left, x); // 插入实现后得得更新root的高度 updateHeight(root); if (getBalancedFactor(root) == 2) { if (getBalancedFactor(root->left) == 1) { // LL型须要对root右旋一次 R(root); } else if (getBalancedFactor(root->left) == -1) { // LR型,先对root->left左旋,再对root右旋 L(root->left); R(root); } } }}Node *createTree(int data[]) { Node *root = nullptr; for (int i = 0; i < n; ++i) { insert(root, data[i]); } return root;}bool isComplete = true;bool onlyLeft = false;// 记录前驱是否只有左孩子bool noChildren = false;// 记录前驱是否没有孩子int num = 0;// 打印管制void layerOrder(Node *root) { queue<Node *> q; q.push(root); bool flag1 = false, flag2 = false; while (!q.empty()) { Node *t = q.front(); q.pop(); printf("%d", t->v); if (num < n - 1) { printf(" "); } else { printf("\n"); } ++num; if (t->left) { q.push(t->left); } if (t->right) { q.push(t->right); } // 判断是否是齐全二叉树 if (t->left == nullptr && t->right != nullptr) { // 只有右孩子 isComplete = false; } else if(noChildren||onlyLeft){ if(t->left != nullptr||t->right != nullptr){ // 前驱呈现只有左孩子或者没有孩子的状况,以后节点还有孩子 isComplete = false; } } else if (t->left == nullptr && t->right == nullptr){ // 没有孩子 noChildren = true; } else if(t->left != nullptr && t->right == nullptr){ // 只有左孩子 onlyLeft = true; } }}int main() { scanf("%d", &n); int data[n]; for (int i = 0; i < n; ++i) { scanf("%d", &data[i]); } Node *root = createTree(data); layerOrder(root); if (isComplete) { printf("YES"); } else { printf("NO"); } return 0;}

February 26, 2021 · 2 min · jiezi

关于c++:PAT甲级1122-Hamiltonian-Cycle

题目粗心给定一个无向图,判断查问的门路是否是一个哈密顿圈。 算法思路判断一条门路是一个哈密顿圈的办法为 1、除了首尾节点其余节点没有反复,不反复的节点数目等于N2、首节点只能反复一次,所有节点数目为N+13、首尾节点得相等4、任意两点之间连通在输出每一条门路的时候,首先判断输出节点与上一个输出节点是否连通,如果不是设置flag为false.而后统计节点数目cnt,并将节点增加进set汇合s中,只有s.size()!=n||cnt!=n+1||!flag||start!=End,就阐明该门路不是哈密顿圈,输入NO,否则输入YES。 提交后果 AC代码#include<cstdio>#include<unordered_set>#include<set>#include<vector>using namespace std;int G[205][205];int main() { int n, m; scanf("%d %d", &n, &m); for (int i = 0; i < m; ++i) { int a, b; scanf("%d %d", &a, &b); G[a][b] = G[b][a] = 1; } int k; scanf("%d", &k); for (int i = 0; i < k; ++i) { int num; scanf("%d", &num); unordered_set<int> s; // 终点,节点个数,起点 int start, cnt = 1, End = 0; bool flag = true;// 门路是否连通 scanf("%d", &start); int pre = start; for (int j = 1; j < num; ++j) { int v; scanf("%d", &v); if (G[pre][v] == 0) { flag = false; } if (j == num - 1) { End = v; } pre = v; ++cnt; s.insert(v); } if (s.size() != n || cnt != n + 1 || !flag || start != End) { printf("NO\n"); } else { printf("YES\n"); } } return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1121-Damn-Single

题目粗心有一个长度为N的couple list,列表上每一行的人都互为情侣,当初有M集体来加入晚会,须要按程序给出没有伴侣的嘉宾 算法思路应用一个map记录每一个人的couple,对于输出的所有的客人,只有没有呈现在couple中,就阐明是独身,增加进singles数组中,如果呈现在了couple中然而其伴侣不在来访嘉宾中,阐明此人也是独身,增加进singles数组中,最初输入singles数组即可。 提交后果 AC代码#include<cstdio>#include<unordered_map>#include<set>#include<vector>using namespace std;unordered_map<int,int> couples;int main() { int n; scanf("%d",&n); for(int i=0;i<n;++i){ int a,b; scanf("%d %d",&a,&b); couples[a+1] = b+1; couples[b+1] = a+1; } int m; scanf("%d",&m); set<int> guests; for(int i=0;i<m;++i){ int a; scanf("%d",&a); guests.insert(a+1); } vector<int> singles; for(auto it:guests){ if(couples[it]==0){ // 没有呈现在couple list上 singles.push_back(it); }else if(guests.find(couples[it])==guests.end()){ // 呈现在了couple list上,然而另外一半没有来 singles.push_back(it); } } printf("%lu\n",singles.size()); for(int i=0;i<singles.size();++i){ printf("%05d",singles[i]-1); if(i<singles.size()-1){ printf(" "); } } return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1120-Friend-Numbers

题目粗心如果两个数字的位数和雷同,那么就阐明这是一个好友数,当初给定N个数字,要求依照程序输入不同的好友数 算法思路在输出每一个数字的时候计算该数字的位数和,而后增加到set汇合中,set汇合的大小就是不同的好友数目,最初顺次输入set汇合中的元素。 提交后果 AC代码#include<cstdio>#include<set>using namespace std;int calDigitSum(int n){ int r = 0; while(n!=0){ r += n%10; n /= 10; } return r;}int main() { int n; scanf("%d",&n); int num; set<int> s; for(int i=0;i<n;++i){ scanf("%d",&num); s.insert(calDigitSum(num)); } printf("%lu\n",s.size()); int index = 0; for(auto it:s){ printf("%d",it); if(index<s.size()-1){ printf(" "); } ++index; } return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:C怎么判断大小端模式

大小端模式: 大端模式: 先寄存最高无效字节,体现为最高无效字节存储在低地址小端模式: 先寄存最低无效字节,体现为最低无效字节存储在低地址小端模式便于机器解决, 大端模式不便人浏览。 测试平台的字节序模式: // 代码1union test { int a; char b;} c;int main(int argc, char const *argv[]){ c.a = 1; cout << (c.b & 1 ? "小端" : "大端") << endl; return 0;}为什么union能够测出以后平台的字节序到底是大端还是小端呢? union: 共用体,也叫联合体,在一个“联结”内能够定义多种不同的数据类型,一个被阐明为该“联结”类型的变量中,容许装入该“联结”所定义的任何一种数据,这些数据共享同一段内存,以达到节俭空间的目标。 这里所谓的共享不是指把多个成员同时装入一个联结变量内, 而是指该联结变量可被赋予任一成员值,但每次只能赋一种值, 赋入新值则冲去旧值。 union变量所占用的内存长度等于最长的成员的内存长度。 // 以下程序阐明联结只能对一个成员赋值,所有的变量共享这一段内存union test { int a; char b;} c;int main(int argc, char const *argv[]){ c.a = 65; cout << c.a << endl; // 输入整数65 cout << c.b << endl; // 输入65的ASCII码 c.b = 66; cout << c.a << endl; cout << c.b << endl; return 0;}对于代码1,所以咱们赋值 c.a = 1; 如果是小端模式,内存内 寄存的应该是0x00000001, 此时c.b的值为0x01, 如果是大端模式,寄存的应该是0x01000000, 此时c.b的值为0x00。 ...

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1119-Pre-and-Postorder-Traversals

题目粗心给定一棵二叉树的前序和后序序列,要求判断该树是否惟一并输入中序序列,不惟一的时候输出任意一棵树的中序序列即可 算法思路在给定先序和后序序列后,咱们只能通过先序第一个节点和后序最初一个节点相等来判断残余的左右子树的范畴,然而对于先序和后序中的左右子树的绝对程序是统一的,那么咱们能够设置左子树的长度为leftSize,从0开始进行遍历,只有先序的左子树节点汇合和后序的左子树节点汇合雷同(程序能够不一样),同时先序左子树的首节点和后序左子树的尾节点是雷同的,这样咱们就失去了一个胜利的左右子树的划分,又因为咱们须要判断是否有多种划分后果,在第一次获得成功划分的时候须要持续进行查找,直到取得第二次胜利划分或者没有第二次划分从而退出循环。这里对建树的操作作进一步细说, 1、咱们应用count记录以后子树划分胜利的次数,初始为02、初始设置左子树长度为0,判断是否能够胜利划分的根据就是先序的左子树首节点和后序左子树尾结点是否相等3、判断先序和后序左子树汇合是否雷同的办法,就是应用一个set汇合一次填入先序左子树和后序左子树的节点,其长度恰好为左子树长度就阐明先序和后序左子树汇合雷同4、在每一次获取胜利的划分后就应用leftSize进行记录,并应用全局变量multiChoice记录是否有多个划分可能。5、划分结束之后,先序的左子树范畴为[preL+1,preL+leftSize],右子树范畴为[preL+leftSize+1,preR]。后序的左子树范畴为[postL,postL+leftSize-1],右子树范畴为[postL+leftSize,postR]提交后果 AC代码#include<cstdio>#include<algorithm>#include<unordered_set>#include<vector>using namespace std;int n;int pre[35],post[35];bool multiChoice = false;struct Node{ int v{}; Node* left = nullptr; Node* right = nullptr;};Node* createTree(int preL,int preR,int postL,int postR){ if(preL>preR){ return nullptr; } Node* root = new Node; root->v = pre[preL]; // 统计以后子树划分胜利的次数 int count = 0; // 在左子树长度为0,判断是否能够造成右子树 if(pre[preL+1]==post[postR-1]){ ++count; } int leftSize = 0; unordered_set<int> s; // 遍历左子树长度 int size = preR-preL+1;// 以后子树的长度 for(int i=1;i<size;++i){ s.insert(pre[preL+i]); s.insert(post[postL+i-1]); if(s.size()==i&&pre[preL+1]==post[postL+i-1]){ // 以后划分的左子树元素满足左子树定义 ++count; leftSize = i;// 记录左子树长度 if(count>=2){ // 存在多种划分 multiChoice =true; break; } } } // 先序:左子树范畴为[preL+1,preL+leftSize],右子树范畴为[preL+leftSize+1,preR] root->left = createTree(preL+1,preL+leftSize,postL,postL+leftSize-1); // 后序: 左子树范畴为[postL,postL+leftSize-1],右子树范畴为[postL+leftSize,postR] root->right = createTree(preL+leftSize+1,preR,postL+leftSize,postR-1); return root;}int num = 0;void inOrder(Node* root){ if(root==nullptr) return; inOrder(root->left); printf("%d",root->v); if(num<n-1){ printf(" "); }else{ printf("\n");// 最初得换行 } ++num; inOrder(root->right);}int main() { scanf("%d",&n); for(int i=0;i<n;++i){ scanf("%d",&pre[i]); } for(int i=0;i<n;++i){ scanf("%d",&post[i]); } Node* root = createTree(0,n-1,0,n-1); if(!multiChoice){ printf("Yes\n"); }else{ printf("No\n"); } inOrder(root); return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1118-Birds-in-Forest

题目粗心当初有N张图片,每一张图片外面有K只鸟,在同一张图片中的鸟属于同一棵树,计算森林中树木的最大数目,并且对于任意一对鸟,判断是否在同一棵树上。 算法思路本题考查并查集的利用,咱们应用set汇合birds保留所有的输出的鸟,并在输出每一张图片的时候,将其中所有的鸟进行合并为一组,而后对于birds中所有的鸟类依据其先人归并为一棵树,并存放到set汇合trees中,最初对于任意两个鸟的编号,只须要比拟father数组的值是否雷同即可。 留神点1、查找father的办法得应用门路压缩,不然会有一个测试点超时。提交后果 AC代码#include<cstdio>#include<algorithm>#include<unordered_set>#include<vector>using namespace std;unordered_set<int> birds;unordered_set<int> trees;int father[10005];int findFather(int x){ int a = x; while(x!=father[x]){ x = father[x]; } while (a!=father[a]){ int z = father[a]; father[a] = x; a = z; } return x;}void Union(int a,int b){ int fa = findFather(a); int fb = findFather(b); if(fa!=fb){ father[fa] = fb; }}int main() { for(int i=0;i<=10000;++i){ father[i] = i; } int n; scanf("%d",&n); for(int i=0;i<n;++i){ int k; scanf("%d",&k); vector<int> t(k); for(int j=0;j<k;++j){ scanf("%d",&t[j]); birds.insert(t[j]); if(j!=0){ Union(t[j],t[j-1]); } } } for(auto it:birds){ int f=findFather(it); trees.insert(f); } printf("%lu %lu\n",trees.size(),birds.size()); int query; scanf("%d",&query); for(int i=0;i<query;++i){ int a,b; scanf("%d %d",&a,&b); if(father[a]==father[b]){ printf("Yes\n"); }else{ printf("No\n"); } } return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1117-Eddington-Number

题目粗心给定N天中每一天的骑行间隔,如果有E天的骑行间隔大于E,那么E就称之为艾丁顿数,请找出这个数字(尽可能大) 算法思路既然要找到E的最大值,那么在每一天的骑行间隔大于N的时候,E获得最大值N,这也是E的初始值。咱们能够想到,(数组的下标值+1)其实就是第几天,那么将骑行的间隔逆序排序,这样对于骑行间隔大于(以后下标值+1)的地位,就是E的一个可能取值(E取下标值+1),咱们为了获得最大值就是一直往右挪动,晓得第一次呈现骑行间隔小于等于以后下标值+1的地位,那么其右边就是E的最大值. 提交后果 AC代码#include<cstdio>#include<algorithm>#include<unordered_set>#include<vector>using namespace std;bool cmp(int a,int b){ return a>b;}int main() { int n; scanf("%d",&n); int num[n]; for(int i=0;i<n;++i){ scanf("%d",&num[i]); } sort(num,num+n,cmp); int E = n;// 没有解的时候,E为n for(int i=0;i<n;++i){ if(num[i]<=i+1){ E = i; break; } } printf("%d\n",E); return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:PAT甲级1116-Come-on-Lets-C

题目粗心给定一个长度为N的排名列表和长度为K的查问列表,须要你依照如下规定输入每一个查问的后果. 1、排名第一的取得Mystery Award奖品2、排名为素数的取得Minion奖品3、所有其余加入较量的人均取得Chocolate奖品4、对于非法查问输入Are you kidding?5、对于反复查问输入Checked算法思路依照规定模仿即可,应用valid数组记录非法的输出,Rank记录每一个人的排名,checked数组记录曾经查问的记录,对于每一次查问,首先判断是否非法,如果非法再判断是否曾经查问过,如果没有,就依照1,2,3规定进行查问即可,最初得将checked[query]置为true 提交后果 AC代码#include<cstdio>#include<algorithm>#include<cmath>#include<vector>using namespace std;int valid[10000];int checked[10000];int Rank[10000];bool isPrime(int x){ if(n<=1) return false; int n = (int)sqrt(x*1.0); for(int i=2;i<=n;++i){ if(x%i==0){ return false; } } return true;}int main() { int n; scanf("%d",&n); vector<int> v(n); for(int i=0;i<n;++i){ scanf("%d",&v[i]); valid[v[i]] = true; Rank[v[i]] = i+1; } int k; scanf("%d",&k); for(int i=0;i<k;++i){ int query; scanf("%d",&query); if(!valid[query]){ printf("%04d: Are you kidding?\n",query); }else{ if(checked[query]){ printf("%04d: Checked\n",query); }else if(query==v[0]){ printf("%04d: Mystery Award\n",query); }else if(isPrime(Rank[query])){ printf("%04d: Minion\n",query); }else{ printf("%04d: Chocolate\n",query); } checked[query] = true; } } return 0;}

February 26, 2021 · 1 min · jiezi

关于c++:关于面向对象的面试知识点整理一

例1: C++ 中的空类默认产生哪些类成员函数? 对于一个空类,编译器默认产生4个成员函数:默认构造函数、 析构函数、 拷贝构造函数和赋值函数。 例2: structure是否能够领有constructor / destructor及成员函数?如果能够, 那么structure和class还有区别么? 区别是class中变量默认是private, struct中的变量默认是public。struct能够有构造函数,析构函数,之间也能够继承,等等。 C++中的struct其实和class意义一样, 惟一不同的就是struct外面默认的访问控制是public, class中默认的访问控制是 private。 C++中存在struct关键字的惟一意义就是为了让C程序员们有个归属感, 是为了让C++编译器兼容以前用C开发的我的项目。 例3: 现有以下代码, 则编译时会产生谬误的是? struct Test{ Test(int) {} Test() {} ~Test() {} void fun() {}};int main(){ Test a(1); a.fun(); Test b(); b.fun(); return 0;}Test b() 这个语法等同于申明了一个函数,函数名为b, 返回值为Test, 传入参数为空。然而实际上,代码作者是心愿申明一个类型为Test,变量名为b的变量,应该写成Test b;, 但程序中这个谬误在编译时是检测不进去的。出错的是b.fun(),它是编译不过来的。 例4: 上面程序的打印出的后果是什么? #include <iostream>using namespace std; class Base{public: Base(int i) : m_j(i), m_i(m_j) {} Base() : m_j(0), m_i(m_j) {} ~Base() {} int get_i() const { return m_i; } int get_j() const { return m_j; } private: int m_i; int m_j;};int main(){ Base obj(98); cout << obj.get_i() << endl; cout << obj.get_j() << endl; return 0;}本题想要失去的后果是"98,98"。 然而成员变量的申明是先m_i, 而后是m_j;初始化列表的初始化变量程序是依据成员变量的申明程序来执行的, 因而m_i会被赋予一个随机值。更改一下成员变量的申明程序能够失去料想的后果。 如果要失去 "98,98"的输入后果,程序须要批改如下 ...

February 25, 2021 · 3 min · jiezi

关于c++:PAT甲级1115-Counting-Nodes-in-a-BST

题目粗心给定一颗二叉搜寻树的插入序列,计算最初两层的节点个数 算法思路首先咱们将这n个数字顺次插入到二叉搜寻树中,而后应用层序遍历获取每一层节点的数目和最大层数maxLevel,L[maxLevel],L[maxLevel-1]就是最初一层和倒数第二层的节点个数 提交后果 AC代码#include<cstdio>#include<algorithm>#include<queue>using namespace std;struct Node{ int val; Node* left; Node* right; int level;};/// 将x插入到root中void insert(Node* &root,int x){ if(root== nullptr){ root = new Node; root->val = x; root->left = nullptr; root->right = nullptr; return; } if(root->val<x){ insert(root->right,x); }else{ insert(root->left,x); }}// 依据数组arr建树Node* createTree(int arr[],int n){ Node* root = nullptr; for(int i=0;i<n;++i){ insert(root,arr[i]); } return root;}int L[1000];// 每一层的节点个数int maxLevel = -1;void layerOrder(Node* root){ queue<Node*> que; root->level = 1; que.push(root); while(!que.empty()){ Node* t = que.front(); que.pop(); maxLevel = max(maxLevel,t->level); ++L[t->level]; if(t->left){ t->left->level = t->level + 1; que.push(t->left); } if(t->right){ t->right->level = t->level + 1; que.push(t->right); } }}int main() { int n; scanf("%d",&n); int initial[n]; for(int i=0;i<n;++i){ scanf("%d",&initial[i]); } Node* root = createTree(initial,n); layerOrder(root); printf("%d + %d = %d",L[maxLevel],L[maxLevel-1],L[maxLevel-1]+L[maxLevel]); return 0;}

February 25, 2021 · 1 min · jiezi

关于c++:PAT甲级1114-Family-Property

题目粗心给定一个人的家庭成员及其房产信息,须要晓得每一个家庭的人数,均匀占地面积和房产数目。 算法思路这是一道惯例的并查集利用题目,咱们首先应用families数组保留所有的输出集,并在输出的时候记录哪些是输出的成员(应用visited记录),并且合并那些是一家人的成员,这样就将所有成员都进行了归类在各自的家庭,而后计算每一个家庭的占地总面积和总房产数目,并且应用flag标记以后家庭(在[0,10000]中有意义的家庭),而后在[0,10000]的所有人中统计所有的家庭成员数目(visited[i]=true的就是)和家庭数目(flag为true的人代表一个家庭),最初计算房子的均匀面积和数目。 留神点1、在输出的时候families的下标是从0开始到n-1完结,这样不便遍历输出的成员并进行信息计算2、计算房子的均匀面积和数目的时候,得应用总共的信息total_*除以家庭人数,因为该计算过程会执行屡次。提交后果 AC代码#include<cstdio>#include<algorithm>#include<vector>using namespace std;struct Family { int ID; int father; int mother; vector<int> children; int estate; int area;} families[1001];// 存储后果集struct Node { int ID; int total_number; double Avg_sets; double Avg_area; int total_sets; int total_area; bool flag;// 标记以后家庭} nodes[10000];bool cmp(const Node &a, const Node &b) { return a.Avg_area != b.Avg_area ? a.Avg_area > b.Avg_area : a.ID < b.ID;}// 统计在[0,10000]中哪些是存在的成员bool visited[10000];// 记录每一个人的先人(可能不在输出中)int father[10000];// 找到x的先人,这里采纳了压缩门路的形式int findFather(int x) { int a = x; while (x != father[x]) { x = father[x]; } while (a != father[a]) { int z = father[a]; father[a] = x; a = z; } return x;}void Union(int a, int b) { int fa = findFather(a); int fb = findFather(b); if (fa < fb) { father[fb] = fa; } else { father[fa] = fb; }}int main() { for (int i = 0; i < 10000; ++i) { father[i] = i; } int n; scanf("%d", &n); Family member; int k; for (int i = 0; i < n; ++i) { scanf("%d %d %d %d", &member.ID, &member.father, &member.mother, &k); int child; for (int j = 0; j < k; ++j) { scanf("%d", &child); member.children.push_back(child); visited[child] = true; Union(member.ID, child); } scanf("%d %d", &member.estate, &member.area); families[i] = member; visited[member.ID] = true; if (member.father != -1) { visited[member.father] = true; Union(member.ID, member.father); } if (member.mother != -1) { visited[member.mother] = true; Union(member.ID, member.mother); } } // 将所有归类的家庭进行计算相干信息 for (int i = 0; i < n; ++i) { int id = findFather(families[i].ID); nodes[id].ID = id; nodes[id].total_area += families[i].area; nodes[id].total_sets += families[i].estate; nodes[id].flag = true; } int num = 0; // 统计家庭成员人数和家庭数目 for (int i = 0; i < 10000; ++i) { if (visited[i]) { int id = findFather(i); ++nodes[id].total_number; } if (nodes[i].flag) { ++num; } } // 计算房子的均匀面积和均匀数目 for (int i = 0; i < 10000; ++i) { if (visited[i]) { int id = findFather(i); nodes[id].Avg_area = nodes[id].total_area*1.0/nodes[id].total_number; nodes[id].Avg_sets = nodes[id].total_sets*1.0/nodes[id].total_number; } } printf("%d\n", num); sort(nodes,nodes+10000,cmp); for(int i=0;i<num;++i){ printf("%04d %d %.3f %.3f\n",nodes[i].ID,nodes[i].total_number,nodes[i].Avg_sets,nodes[i].Avg_area); } return 0;}

February 25, 2021 · 2 min · jiezi

关于c++:PAT甲级1113-Integer-Set-Partition

题目粗心给定一个汇合,含有n个数字,要求将其划分为长度为n1和n2的两个汇合,并且要求两个汇合和之差最大,n1与n2的差距最小 算法思路最为直观的感触就是将数组进行排序,而后选取n/2长度的前半部分为第一个局部,剩下的为第二局部,这样两者元素个数之差最小,和之差最大。 提交后果 AC代码#include <iostream>#include <algorithm>#include <vector>using namespace std;int main() { int n, sum = 0, temp = 0; scanf("%d", &n); int v[n]; for (int i = 0; i < n; i++) { scanf("%d", &v[i]); sum += v[i]; } sort(v, v+n); for (int i = 0; i < n / 2; i++) temp += v[i]; printf("%d %d", n % 2, sum - 2 * temp); return 0;}

February 25, 2021 · 1 min · jiezi

关于c++:PAT甲级1112-Stucked-Keyboard

题目粗心给定k和一个字符串s,因为一些键盘的按键损坏,损坏的键按一下会反复呈现k次,要求你找出所有的坏件和原始输出字符串。 算法思路一个键是坏键的前提是该字符每一次间断呈现的次数肯定是k的整数倍,那么咱们能够先采纳hasShownNotStucked哈希表记录那些肯定是好键的字符,又因为坏键只记录一次就好,所以应用hasShownStucked记录记录曾经呈现过并确定是坏键的按键,防止反复增加。咱们采纳两边遍历的形式进行求解 第一次遍历将所有肯定是好键的字符全副记录,具体做法就是比拟以后字符和后一字符是否相等,如果不等那么肯定不是坏键,否则统计其呈现的次数cnt,只有以后字符没有确定为好键并且cnt%n!=0,那么就阐明该键为好键。 第二次遍历进行获取反复的字符组成的字符串repeated和原始字符串origin,具体做法就是,如果以后字符肯定是好键,那么就增加进origin中,否则就得计算呈现的次数cnt,并让origin增加cnt/n个s[i],判断以后字符是否曾经记录为坏键,如果没有就记录该坏键并增加该字符到repeated中。 留神点1、一个键有可能之前呈现k的整数倍次,然而最初呈现的次数不能整除k,那么就阐明不是坏键,比方k=3,s=eeerre,其中e就是好键,所以须要先遍历一遍将所有肯定是好键的键进行记录。提交后果 AC代码#include<cstdio>#include<iostream>#include<unordered_map>using namespace std;unordered_map<char,bool> hasShownNotStucked;// 记录好键unordered_map<char,bool> hasShownStucked;// 记录曾经呈现过并确定是坏键的按键int main() { int n; scanf("%d",&n); string s; cin>>s; string repeated,origin; int i; // 第一次遍历记录所有肯定不是坏键的键 for(i=0;i<s.size()-1;){ if(s[i]!=s[i+1]){ // 以后按键只呈现了一次,肯定不是坏键 hasShownNotStucked[s[i]] = true; ++i; }else{ // 统计呈现反复的次数 int cnt = 0; for(int j=i;j<s.size();++j){ if(s[j]==s[i]){ ++cnt; }else{ break; } } if(!hasShownNotStucked[s[i]]&&cnt%n!=0){ // 以后字符之前不确定,然而无奈整除n阐明肯定不是坏键 hasShownNotStucked[s[i]] = true; } i += cnt; } } if(i<s.size()){ // s[i]!=s[i+1],阐明最初一个键也不是坏键 hasShownNotStucked[s[i]] = true; } for(i=0;i<s.size();){ if(hasShownNotStucked[s[i]]){ // 肯定不是坏键 origin += s[i++]; }else{ // 有可能是坏键 // 统计呈现反复的次数 int cnt = 0; for(int j=i;j<s.size();++j){ if(s[j]==s[i]){ ++cnt; }else{ break; } } // 反复增加cnt/n个s[i] int a = cnt/n; while(a--){ origin += s[i]; } if(!hasShownStucked[s[i]]){ // 之前没有增加过该反复字符 hasShownStucked[s[i]] = true; repeated += s[i]; } i += cnt; } } cout<<repeated<<endl<<origin; return 0;}

February 25, 2021 · 1 min · jiezi

关于c++:我整理了这篇指针的知识点想必对你有用

指针和援用的区别非空区别: 任何状况下都不能应用指向空值的援用,一个援用必须总是指向某些对象。 指针能够指向空。合法性区别: 援用在应用之前不须要测试合法性,指针应该总是要被测试,避免其为空可修改性区别: 援用在初始化时指定对象,当前不能批改。指针传递动态内存例1: 程序测试后会有什么后果? #include<iostream>#include<cstring>using namespace std;void getMemory(char*p, int num){ p = (char*)malloc(sizeof(char) * num);}int main(int argc, const char* argv[]){ char *str = NULL; getMemory(str, 100); strcpy(str, "hello"); return 0;}问题呈现在getMemory里,编译器总是为函数的每个参数制作一个长期正本。在本题中,p为str的拷贝,p申请了一个新的内存空间,然而并没有影响到str,str还是NULL,再调用strcpy(), 则会使代码解体,并且p申请的内存也始终没用开释,造成内存泄露。 正确的办法是把往getMemory内传入str的地址。 #include<iostream>#include<cstring>using namespace std;void getMemory(char**p, int num){ *p = (char*)malloc(sizeof(char) * num);}int main(int argc, const char* argv[]){ char *str = NULL; getMemory(&str, 100); strcpy(str, "hello"); cout << str << endl; return 0;}不过这样写有些麻烦,咱们能够间接把申请好的内存返回。 #include<iostream>#include<cstring>using namespace std;char* getMemory(int num){ return (char*)malloc(sizeof(char) * num);}int main(int argc, const char* argv[]){ char *str = NULL; str = getMemory(100); strcpy(str, "hello"); cout << str << endl; return 0;}例2: 这个函数有什么问题? ...

February 24, 2021 · 3 min · jiezi

关于c++:线程间到底共享了哪些进程资源

过程和线程这两个话题是程序员绕不开的,操作系统提供的这两个抽象概念切实是太重要了。 对于过程和线程有一个极其经典的问题,那就是过程和线程的区别是什么?置信很多同学对答案似懂非懂。 记住了不肯定真懂有的同学可能曾经“背得”滚瓜烂熟了:“过程是操作系统分配资源的单位,线程是调度的根本单位,线程之间共享过程资源”。 可是你真的了解了下面这句话吗?到底线程之间共享了哪些过程资源,共享资源意味着什么?共享资源这种机制是如何实现的?对此如果你没有答案的话,那么这意味着你简直很难写出能正确工作的多线程程序,同时也意味着这篇文章就是为你筹备的。 逆向思考查理芒格常常说这样一句话:“反过来想,总是反过来想”,如果你对线程之间共享了哪些过程资源这个问题想不分明的话那么也能够反过来思考,那就是有哪些资源是线程公有的。 线程公有资源线程运行的实质其实就是函数的执行,函数的执行总会有一个源头,这个源头就是所谓的入口函数,CPU从入口函数开始执行从而造成一个执行流,只不过咱们人为的给执行流起一个名字,这个名字就叫线程。 既然线程运行的实质就是函数的执行,那么函数执行都有哪些信息呢? 在《函数运行时在内存中是什么样子?》这篇文章中咱们说过,函数运行时的信息保留在栈帧中,栈帧中保留了函数的返回值、调用其它函数的参数、该函数应用的局部变量以及该函数应用的寄存器信息,如图所示,假如函数A调用函数B: 此外,CPU执行指令的信息保留在一个叫做程序计数器的寄存器中,通过这个寄存器咱们就晓得接下来要执行哪一条指令。因为操作系统随时能够暂停线程的运行,因而咱们保留以及恢复程序计数器中的值就能晓得线程是从哪里暂停的以及该从哪里持续运行了。 因为线程运行的实质就是函数运行,函数运行时信息是保留在栈帧中的,因而每个线程都有本人独立的、公有的栈区。 同时函数运行时须要额定的寄存器来保留一些信息,像局部局部变量之类,这些寄存器也是线程公有的,一个线程不可能拜访到另一个线程的这类寄存器信息。 从下面的探讨中咱们晓得,到目前为止,所属线程的栈区、程序计数器、栈指针以及函数运行应用的寄存器是线程公有的。 以上这些信息有一个对立的名字,就是线程上下文,thread context。 咱们也说过操作系统调度线程须要随时中断线程的运行并且须要线程被暂停后能够持续运行,操作系统之所以能实现这一点,依附的就是线程上下文信息。 当初你应该晓得哪些是线程公有的了吧。 除此之外,剩下的都是线程间共享资源。 那么剩下的还有什么呢?还有图中的这些。 这其实就是过程地址空间的样子,也就是说线程共享过程地址空间中除线程上下文信息中的所有内容,意思就是说线程能够间接读取这些内容。 接下来咱们别离来看一下这些区域。 代码区过程地址空间中的代码区,这里保留的是什么呢?从名字中有的同学可能曾经猜到了,没错,这里保留的就是咱们写的代码,更精确的是编译后的可执行机器指令。 那么这些机器指令又是从哪里来的呢?答案是从可执行文件中加载到内存的,可执行程序中的代码区就是用来初始化过程地址空间中的代码区的。 线程之间共享代码区,这就意味着程序中的任何一个函数都能够放到线程中去执行,不存在某个函数只能被特定线程执行的状况。 堆区堆区是程序员比拟相熟的,咱们在C/C++中用malloc或者new进去的数据就寄存在这个区域,很显然,只有晓得变量的地址,也就是指针,任何一个线程都能够拜访指针指向的数据,因而堆区也是线程共享的属于过程的资源。 栈区唉,等等!刚不是说栈区是线程公有资源吗,怎么这会儿又说起栈区了? 的确,从线程这个形象的概念上来说,栈区是线程公有的,然而从理论的实现上看,栈区属于线程公有这一规定并没有严格遵守,这句话是什么意思? 通常来说,留神这里的用词是通常,通常来说栈区是线程公有,既然有通常就有不通常的时候。 不通常是因为不像过程地址空间之间的严格隔离,线程的栈区没有严格的隔离机制来爱护,因而如果一个线程能拿到来自另一个线程栈帧上的指针,那么该线程就能够扭转另一个线程的栈区,也就是说这些线程能够任意批改本属于另一个线程栈区中的变量。 这从某种程度上给了程序员极大的便当,但同时,这也会导致极其难以排查到的bug。 试想一下你的程序运行的好好的,后果某个时刻忽然出问题,定位到出问题代码行后基本就排查不到起因,你当然是排查不到问题起因的,因为你的程序原本就没有任何问题,是他人的问题导致你的函数栈帧数据被写坏从而产生bug,这样的问题通常很难排查到起因,须要对整体的我的项目代码十分相熟,罕用的一些debug工具这时可能曾经没有多大作用了。 说了这么多,那么同学可能会问,一个线程是怎么批改本属于其它线程的数据呢? 接下来咱们用一个代码示例解说一下。 文件最初,如果程序在运行过程中关上了一些文件,那么过程地址空间中还保留有关上的文件信息,过程关上的文件也能够被所有的线程应用,这也属于线程间的共享资源。对于文件IO操作,你能够参考《读取文件时,程序经验了什么?》 One More Thing:TLS本文就这些了吗? 实际上本篇结尾对于线程公有数据还有一个项没有具体解说,因为再讲下去本篇就撑爆了,实际上本篇解说的曾经足够用了,剩下的这一点仅仅作为补充。 对于线程公有数据还有一项技术,那就是线程部分存储,Thread Local Storage,TLS。 这是什么意思呢? 其实从名字上也能够看出,所谓线程部分存储,是指寄存在该区域中的变量有两个含意: 寄存在该区域中的变量是全局变量,所有线程都能够拜访尽管看上去所有线程拜访的都是同一个变量,但该全局变量独属于一个线程,一个线程对此变量的批改对其余线程不可见。说了这么多还是没懂有没有?没关系,接下来看完这两段代码还不懂你来打我。 咱们先来看第一段代码,不必放心,这段代码十分十分的简略: int a = 1; // 全局变量void print_a() { cout<<a<<endl;}void run() { ++a; print_a();}void main() { thread t1(run); t1.join(); thread t2(run); t2.join();}怎么样,这段代码足够简略吧,上述代码是用C++11写的,我来解说下这段代码是什么意思。 ...

February 24, 2021 · 1 min · jiezi

关于c++:PAT甲级1111-Online-Map

题目粗心现有N个节点和M条边的图,给定终点和起点,找出一条间隔最短的门路和一条耗时起码的门路。其规定如下: 1、对于有多条最短门路,找到耗时最短的2、对于有多条耗时最短门路找到岔路口(顶点)起码的3、如果最短门路和耗时起码门路一样,合并输入。算法思路此题为比拟惯例的最短门路问题,应用两次迪杰斯特拉算法就能够进行求解,第一次求解间隔最短的门路,应用间隔作为第一标尺,耗时作为第二标尺。第二次应用耗时最短的门路,应用耗时作为第一标尺,门路上的节点数目作为第二标尺。迪杰斯特拉算法的过程不再这里赘述。对于两条最短门路是否齐全一样的判断办法就是间接比拟前驱数组,从起点开始始终往前走,遇到不同的阐明是不同门路。 留神点1、测试点4谬误的状况极有可能是在编写第二次迪杰斯特拉算法的时候复制了之前的代码导致有些变量的名字没有更改过去。2、第二次应用迪杰斯特拉算法求解的时候得初始化visited数组。提交后果 AC代码#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn = 501;int N, M;// 顶点和边的数目int Start, End;// 终点和起点bool visited[maxn];// 拜访标记数组// 间隔最短int L[maxn][maxn];// 每一对节点间的长度int disL[maxn]; // 每一个节点到终点的最短距离int preL[maxn]; // 求解最短距离门路中每一个节点的前驱节点// 耗时最短int T[maxn][maxn];// 每一对节点间的工夫int disT[maxn]; // 每一个节点到终点的最短耗时int preT[maxn]; // 求解最短耗时门路中每一个节点的前驱节点int num[maxn]; // 求解最短耗时门路中每一个节点到终点的最短节点数目void findShortestPath(int start) { fill(disL, disL + maxn, 0x3fffffff); fill(disT, disT + maxn, 0x3fffffff); disL[start] = 0; disT[start] = 0; for (int i = 0; i < N; ++i) { int minD = 0x3fffffff; int minIndex = -1; for (int j = 0; j < N; ++j) { if (!visited[j] && disL[j] < minD) { minD = disL[j]; minIndex = j; } } // 剩下的节点不连通 if (minIndex == -1) return; visited[minIndex] = true; // 依据minIndex更新disL数组 for (int j = 0; j < N; ++j) { if (!visited[j] && L[minIndex][j] != 0) { if (disL[j] > disL[minIndex] + L[minIndex][j]) { disL[j] = disL[minIndex] + L[minIndex][j]; disT[j] = disT[minIndex] + T[minIndex][j]; preL[j] = minIndex; } else if (disL[j] == disL[minIndex] + L[minIndex][j] && disT[j] > disT[minIndex] + T[minIndex][j]) { disT[j] = disT[minIndex] + T[minIndex][j]; preL[j] = minIndex; } } } }}void findFastestPath(int start) { fill(disT, disT + maxn, 0x3fffffff); fill(num, num + maxn, 0x3fffffff); disT[start] = 0; num[start] = 1; for (int i = 0; i < N; ++i) { int minD = 0x3fffffff; int minIndex = -1; for (int j = 0; j < N; ++j) { if (!visited[j] && disT[j] < minD) { minD = disT[j]; minIndex = j; } } // 剩下的节点不连通 if (minIndex == -1) return; visited[minIndex] = true; // 依据minIndex更新disT数组 for (int j = 0; j < N; ++j) { if (!visited[j] && T[minIndex][j] != 0) { if (disT[j] > disT[minIndex] + T[minIndex][j]) { disT[j] = disT[minIndex] + T[minIndex][j]; preT[j] = minIndex; num[j] = num[minIndex] + 1; } else if (disT[j] == disT[minIndex] + T[minIndex][j] && num[j] > num[minIndex] + 1) { preT[j] = minIndex; num[j] = num[minIndex] + 1; } } } }}// 判断最短距离和最短耗时门路是否一样bool isSame() { int t = End; while(preT[t]==preL[t]){ if(preT[t]==Start){ return true; } t = preT[t]; } return false;}// 输入最短门路void printPath(int end, int path[]) { if (Start == end) { printf("%d", Start); return; } printPath(path[end], path); printf(" -> "); printf("%d", end);}int main() { scanf("%d %d", &N, &M); int v1, v2, oneWay, length, time; for (int i = 0; i < M; ++i) { scanf("%d %d %d %d %d", &v1, &v2, &oneWay, &length, &time); L[v1][v2] = length; T[v1][v2] = time; if (oneWay == 0) { // 双向边 L[v2][v1] = length; T[v2][v1] = time; } } scanf("%d %d", &Start, &End); findShortestPath(Start); memset(visited, 0, sizeof(visited)); findFastestPath(Start); if (isSame()) { printf("Distance = %d; Time = %d: ", disL[End], disT[End]); printPath(End, preL); } else { printf("Distance = %d: ", disL[End]); printPath(End, preL); printf("\n"); printf("Time = %d: ", disT[End]); printPath(End, preT); } return 0;}

February 23, 2021 · 3 min · jiezi

关于c++:PAT甲级1110-Complete-Binary-Tree

题目粗心给定一棵含有N个节点的二叉树,判断是否是齐全二叉树 算法思路判断一颗二叉树是否是齐全二叉树的规定: 1、如果呈现只有右孩子节点的,肯定不是2、如果呈现只有左孩子或者没有孩子节点的,记录该状况3、如果以后有孩子,并且呈现了状况2,肯定不是4、遍历树中所有节点后,如果没有1和3,表明该树为齐全二叉树遍历形式采纳层序遍历。在遍历过程中应用count记录遍历的节点个数,在count=N的时候阐明来到了最初一个节点,应用lastNode记录。对于根节点的确定能够应用一个father数组记录每一个节点的父节点编号,初始化全副为-1,在输出完结后,遍历一遍,第一次遇到-1的编号就是根节点。 提交后果 AC代码#include <cstdio>#include <iostream>#include <queue>using namespace std;struct Node{ int left = -1; int right = -1; int index{};}nodes[25];queue<Node> que;int lastNode;bool isComplete(int root,int N){ que.push(nodes[root]); bool flag = false;// 标记是否呈现状况2 int count = 0; while(!que.empty()){ Node t = que.front(); que.pop(); ++count; if(count==N){ // 最初一个节点 lastNode = t.index; } if(t.left==-1&&t.right!=-1) { // 状况1 return false; }else if(t.left!=-1||t.right!=-1) { // 以后节点有孩子 if(flag){ return false; } }else if((t.left!=-1&&t.right==-1)||(t.left==-1&&t.right==-1)){ // 只有左孩子或者没有孩子 flag = true; } if(t.left!=-1){ que.push(nodes[t.left]); } if(t.right!=-1){ que.push(nodes[t.right]); } } return true;}int main() { int N; scanf("%d",&N); int father[N]; for(int i=0;i<N;++i){ father[i] = -1; } string left,right; for(int i=0;i<N;++i){ cin>>left>>right; nodes[i].index = i; if(left!="-"){ int leftChild = stoi(left); father[leftChild] = i; nodes[i].left = leftChild; } if(right!="-"){ int rightChild = stoi(right); father[rightChild] = i; nodes[i].right = rightChild; } } int root = 0; for(int i=0;i<N;++i){ if(father[i]==-1){ root = i; break; } } if(isComplete(root,N)){ printf("YES %d",lastNode); }else{ printf("NO %d",root); } return 0;}

February 23, 2021 · 1 min · jiezi

关于c++:PAT甲级1109-Group-Photo

题目粗心有N集体照相,现将N集体排成K行,每一行都有N/K集体,并依照如下规定排列 1、对于多进去的人全副在最初一排2、前一排的人都比后一排的人矮3、每一行最高的人都在两头地位4、视角从下往上看(面对着看),每一行最两头的人开始,先左再右造成非递增序列5、对于有雷同身高的人,依照字典序升序排列当初要求你输入该排列后果。 算法思路有两种办法解决该问题,能够间接进行输入,不过地位的计算较为简单并且不易浏览,这里采纳模仿排列的过程来求解。咱们将每一个人的姓名和身高进行绑定并且依照规定进行对立排序(高个子的在后面),而后从最初一排往前顺次进行排列(输入的程序),排列办法如下: 1、首先初始化以后队列temp,其容量为m2、应用index记录全局队列中待排列的以后人3、填充两头地位temp[m / 2] = allPeo[index++];4、应用pointLeft和pointRight别离指向两头地位的左右两边邻近的地位,并应用count记录以后曾经填充的人数5、在count为奇数的时候填充右边,否则填充左边6、每一行填充结束后输入temp并换行。提交后果 AC代码#include <cstdio>#include <algorithm>#include <vector>#include <iostream>using namespace std;struct People { string name; int height{};} people;vector<People> allPeo;bool cmp(const People &p1, const People &p2) { return p1.height != p2.height ? p1.height > p2.height : p1.name < p2.name;}int main() { int N, K; scanf("%d %d", &N, &K); for (int i = 0; i < N; ++i) { cin >> people.name >> people.height; allPeo.push_back(people); } sort(allPeo.begin(), allPeo.end(), cmp); int eachRowNum = N / K; int lastRowNum = eachRowNum + N % K; int index = 0; // 输入每一行的排列 for (int i = 0; i < K; ++i) { int m = i == 0 ? lastRowNum : eachRowNum; vector<People> temp(m); int pointLeft = m / 2 - 1; int pointRight = m / 2 + 1; temp[m / 2] = allPeo[index++]; int count = 1; while (count < m) { if (count % 2 != 0) { temp[pointLeft--] = allPeo[index++]; } else { temp[pointRight++] = allPeo[index++]; } ++count; } for (int j = 0; j < temp.size(); ++j) { cout << temp[j].name; if (j < temp.size() - 1) cout << " "; } if (i < K - 1) { cout << endl; } } return 0;}

February 23, 2021 · 1 min · jiezi

关于c++:PAT甲级1108-Finding-Average

题目粗心给定N个输出,这些输出中只有在[-1000,1000]内并且位数在2位以内的数字才是非法的,对于不非法的输出间接输入相干信息,对于非法的数字须要计算平均值并进行输入 算法思路此题惟一的考点就是判断输出是否非法,对于所有的非数字字符进行如下判断: 1、如果该字符为"-"并且呈现在第一位,跳过2、如果该字符为小数点,则计算小数点前面的位数,如果大于2,则非法,否则统计小数点呈现的次数,呈现超过1次,则非法3、对于其余非数字字符均为非法字符。4、如果该数字为实数,如果在$[-1000,1000]$内,间接返回,否则非法,返回-1.提交后果 AC代码#include <cstdio>#include <iostream>using namespace std;/* * 判断输出的字符串是否非法 * 含有非数字字符,超过两个小数点,范畴在[-1000,1000]以外的都是非法字符 */double isLegal(const string &num){ int decimalCount = 0;// 小数点个数 int n = num.size(); for(int i=0;i<n;++i){ char c = num[i]; if(!(c>='0'&&c<='9')){ if(c=='-'&&i==0){ continue; }else if(c=='.'){ // 呈现小数点 int rest = n-i-1;// 计算小数点前面的位数 if(rest>2){ return -1; } ++decimalCount; if(decimalCount>=2){ return -1; } }else{ return -1; } } } // 肯定为数字 double x = stof(num); if(x>=-1000&&x<=1000){ return x; }else{ return -1; }}int main(){ int N; scanf("%d",&N); string in; int legalNum = 0;// 非法输出个数 double sum = 0; for(int i=0;i<N;++i){ cin>>in; double a = isLegal(in); if(a!=-1){ sum += a; ++legalNum; }else{ cout<<"ERROR: "<<in<<" is not a legal number"<<endl; } } if(legalNum>1){ printf("The average of %d numbers is %.2f",legalNum,sum/legalNum); } else if(legalNum==1){ printf("The average of 1 number is %.2f",sum); } else{ printf("The average of 0 numbers is Undefined"); } return 0;}

February 23, 2021 · 1 min · jiezi

关于c++:C-priorityqueue的用法一看就会用

头文件 : include <queue> 定义 : priority_queue<Type, Container, Functional> 默认 : ==大顶堆==,比拟形式默认用operator< ,所以如果把前面2个参数缺省的话,优先队列就是大顶堆(降序),队头元素最大 个别应用 : // 留神 >>和 > >, c++11之后能够不必加空格// 大顶堆priority_queue<int, vector<int>, less<int> > q;// 小顶堆priority_queue<int, vector<int>, greater<int> > q;//greater 和 less 是 std 实现的两个仿函数//(就是使一个类的应用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了相似函数的行为,就是一个仿函数类了)// pair<int, int> 默认大顶堆,先比拟第一个,再比拟第二个;priority_queue<pair<int, int> > q;// 2 5// 2 4// 1 6// 自定义优先级// struct Node //运算符重载<struct Node{ int x; Node(int a) : x(a){} bool operator<(const Node& a) const // 返回 true 时 a的优先级高 { return x < a.x; //大顶堆 }};

February 23, 2021 · 1 min · jiezi

关于c++:766-托普利茨矩阵

766. 托普利茨矩阵给你一个 m x n 的矩阵 matrix 。如果这个矩阵是托普利茨矩阵,返回 true ;否则,返回 false 。 如果矩阵上每一条由左上到右下的对角线上的元素都雷同,那么这个矩阵是 托普利茨矩阵。 思路从(1,1)开始,每个节点左上角的元素与节点比拟,不同为falseclass Solution {public: bool isToeplitzMatrix(vector<vector<int>>& matrix) { int m = matrix.size(); int n = matrix[0].size(); for (int i = 1; i < m; i++) { for (int j = 1; j < n; j++) { if (matrix[i][j] != matrix[i-1][j-1]) { return false; } } } return true; }};

February 22, 2021 · 1 min · jiezi

关于c++:leetcode-229-周赛

5685. 交替合并字符串给你两个字符串 word1 和 word2 。请你从 word1 开始,通过交替增加字母来合并字符串。如果一个字符串比另一个字符串长,就将多进去的字母追加到合并后字符串的开端。 返回 合并后的字符串 。输出:word1 = "abc", word2 = "pqr"输入:"apbqcr" 思路通过up进行判断class Solution {public: string mergeAlternately(string word1, string word2) { int i = 0, j = 0; string res; bool up = true; while (i <= word1.size() && j <= word2.size()) { if (i < word1.size() && up == true) { res += word1[i++]; up = false; } else if (j < word2.size() && up == false) { res += word2[j++]; up = true; } if (i == word1.size() && j == word2.size()) { break; } else if (i == word1.size() && j < word2.size()) { while (j < word2.size()) { res += word2[j++]; } } else if (j == word2.size() && i < word1.size()) { while (i < word1.size()) { res += word1[i++]; } } } return res; } };5686. 挪动所有球到每个盒子所需的最小操作数有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes ,其中 boxes[i] 的值为 '0' 示意第 i 个盒子是 空 的,而 boxes[i] 的值为 '1' 示意盒子里有 一个 小球。 ...

February 21, 2021 · 3 min · jiezi

关于c++:STL总结

Vector数组初始化 int a[5] = {1,2,3,4,5}vector初始化 vector<int> list = {1,2,3,4,5}vector<int> nums;vector<vector<int>> f1(n, vector<int>(m,0));sort(nums.begin(), nums.end());nums.push_back(x);nums.insert(nums.begin(), x);nums.insert(nums.begin() + n , x);nums.erase(nums.begin());int rows=triangle.size();//求得行数int col=triangle[0].size();//求的列数auto x = max_element(a.begin() + i, a.end());用的时候*x整型转字符串to_string(i)字符串转整型int a=atoi(s.c_str());int b=stoi(s);vector中的find()vector<int>::iterator result = find(arr2.begin( ), arr2.end( ), arr1[i]);if ( result == arr2.end( ) ) //如果没找见去重 alls.erase(unique(alls.begin(), alls.end()), alls.end());Stringstr.insert(str.begin(), 'a')切分字符串:str.substr(str.begin(), str.end())字符串增加元素:str.push_back('a')字符串删除开端元素:str.pop_back('a')删除元素 str.substr(0, str.length() - 1);删除元素 str.erase(str.end() - 1);mapmap<int, int> loc;获取键、值for(auto x : f1) x.first,x.second s.find() 查找一个元素,如果容器中不存在该元素,返回值等于s.end()if(numSet.find(findNum)!=numSet.end()代表找到了 setset.insert();set遍历set<int>::iterator it;for(it=notAppearSet.begin ();it!=notAppearSet.end ();it++) cout << *it;autoauto c 主动推断c的类型

February 16, 2021 · 1 min · jiezi

关于c++:C并发与多线程-13线程池浅谈线程数量总结

浅谈线程池线程池(英语:thread pool):一种线程应用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池保护着多个线程,期待着监督管理者调配可并发执行的工作。这防止了在解决短时间工作时创立与销毁线程的代价。线程池不仅可能保障内核的充分利用,还能避免过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。 例如,线程数个别取cpu数量+2比拟适合,线程数过多会导致额定的线程切换开销。任务调度以执行线程的常见办法是应用同步队列,称作工作队列。池中的线程期待队列中的工作,并把执行完的工作放入实现队列中。C++11多线程总结线程创立的极限数量个别为 2000 个。线程创立数量倡议 采纳某些开发倡议和批示来确保程序高效执行。依据理论利用场景;思考可能被阻塞的最大线程数量,即创立多余最大被阻塞(耗时工作)线程数量的线程。如,有100个线程可能解决耗时工作,启动 110 个线程就能够保障有闲暇线程解决其它申请。线程的创立数量尽量不要超过 500 个,尽量管制在 200 个之内。C++线程相干参考网址cplusplus.comcppreference.com

February 16, 2021 · 1 min · jiezi

关于c++:CS144-Lab-Assignments-手写TCP-LAB3

CS 144: Introduction to Computer Networking, Fall 2020https://cs144.github.io/My Repohttps://github.com/wine99/cs1... 总体思路tick 不须要咱们来调用,参数的意义是间隔上次 tick 被调用过来的工夫,也不须要咱们来设定。咱们只须要在 tick 中实现,通过参数判断过来了多少工夫,须要执行何种操作即可。 留神依据文档,咱们要不须要实现抉择重传,而是相似回退 N,须要存储已发送并且未被确认的段,进行累计确认,超时时只有重传这些段中最早的那一个即可。 TCPReceiver 调用 unwrap 时的 checkpoint 是上一个接管到的报文段的 absolute_seqno,TCPSender 调用 unwrap 时的 checkpoint 是 _next_seqno。 我的实现中计时器开关的解决: 发送新报文段时若计时器未关上,开启ack_received() 中,如果有报文段被正确地确认,重置计时器和 RTO,如果所有报文段均被确认(bytes in flight == 0),敞开计时器tick() 中,若计时器为敞开状态,间接返回,否则累加计时而后解决超时增加的成员变量class TCPSender { private: bool _syn_sent = false; bool _fin_sent = false; uint64_t _bytes_in_flight = 0; uint16_t _receiver_window_size = 0; uint16_t _receiver_free_space = 0; uint16_t _consecutive_retransmissions = 0; unsigned int _rto = 0; unsigned int _time_elapsed = 0; bool _timer_running = false; std::queue<TCPSegment> _segments_outstanding{}; // See test code send_window.cc line 113 why the commented code is wrong. bool ack_valid(uint64_t abs_ackno) { return abs_ackno <= _next_seqno && // abs_ackno >= unwrap(_segments_outstanding.front().header().seqno, _isn, _next_seqno) + // _segments_outstanding.front().length_in_sequence_space(); abs_ackno >= unwrap(_segments_outstanding.front().header().seqno, _isn, _next_seqno); } public: void send_segment(TCPSegment &seg);};send_segment(TCPSegment &seg) 只在 fill_window() 中被调用,重传只须要 _segments_out.push(_segments_outstanding.front())_receiver_window_size 保留收到无效(无效的含意见下面 ack_valid())确认报文段时,报文段携带的接管方窗口大小_receiver_free_space 是在 _receiver_window_size 的根底上,再减去已发送的报文段可能占用的空间(_bytes_in_flight)fill_window() 实现如果 SYN 未发送,发送而后返回如果 SYN 未被应答,返回如果 FIN 曾经发送,返回如果 _stream 临时没有内容但并没有 EOF,返回如果 _receiver_window_size 不为 0 ...

February 16, 2021 · 3 min · jiezi

关于c++:C并发与多线程-12recursivemutextimedmutexrecursivetimedmutex

window 临界区window 临界区资源对象与C++的 std::mutex 对象相似,能够爱护多个线程对临界区资源的拜访。#include <iostream>#include <thread>#include <Windows.h>static CRITICAL_SECTION g_winsec;void print_block (int n, char c){ EnterCriticalSection(&g_winsec); // 2. 进入临界区 for (int i=0; i<n; ++i) { std::cout << c; } std::cout << '\n'; LeaveCriticalSection(&g_winsec); // 3. 来到临界区}int main (){ InitializeCriticalSection(&g_winsec); // 1. 初始化临界资源对象 std::thread th1 (print_block,50,'*'); std::thread th2 (print_block,50,'$'); th1.join(); th2.join(); return 0;}输入: **************************************************$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$屡次进入临界区试验window 临界资源对象能够在同一线程中多次重复进入,对应次数的来到,程序仍失常执行。std::mutex 对象只能在同一线程进行一次加锁并对应一次解锁,否则程序抛出异样。测试1:window 临界区 #include <iostream>#include <thread>#include <Windows.h>static CRITICAL_SECTION g_winsec;void print_block (int n, char c){ EnterCriticalSection(&g_winsec); // 2. 进入临界区 EnterCriticalSection(&g_winsec); // 屡次进入 。。。 EnterCriticalSection(&g_winsec); // 屡次进入 。。。 for (int i=0; i<n; ++i) { std::cout << c; } std::cout << '\n'; LeaveCriticalSection(&g_winsec); // 3. 来到临界区 LeaveCriticalSection(&g_winsec); // 屡次来到 。。。 LeaveCriticalSection(&g_winsec); // 屡次来到 。。。}int main (){ InitializeCriticalSection(&g_winsec); // 1. 初始化临界资源对象 std::thread th1 (print_block,50,'*'); std::thread th2 (print_block,50,'$'); th1.join(); th2.join(); return 0;}输入:[后果正确] ...

February 15, 2021 · 3 min · jiezi