├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Lambert-hpx 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 复习目录 2 | 3 | - 常用函数:STL等常用数据结构(树、栈、队列、优先队列、向量、散列表、集合、字符串)和函数 4 | 5 | - 数学问题(最小公倍数、最大公因数、快速幂、进制转换、质因数、大数) 6 | 7 | - #### 搜索(BFS & DFS) 8 | 9 | - 图论(并查集、最小生成树、最短路径、拓扑排序、关键路径) 10 | 11 | - #### 动态规划 12 | 13 | # 常用函数 14 | 15 | ## memset-对数组中每个元素赋值0/-1 16 | 17 | ```c++ 18 | #include 19 | int a[5]; 20 | memset(a,0,sizeof(a)); 21 | ``` 22 | 23 | ## getchar/putchar && gets/puts 24 | 25 | ```c++ 26 | #include 27 | getchar(); //输入单个字符 有时候还可以用来吸收换行符 28 | putchar(); //输出单个字符 29 | gets(); //输入一行字符串,识别\n作为输入结束 30 | puts(); //输出一行字符串,并输出\n 若是字符串末尾没有\0则会乱码 31 | ``` 32 | 33 | ## string.h 34 | 35 | ```c++ 36 | #include 37 | strlen(str); 38 | strcmp(str1,str2); 39 | //比较字典序,小于返回负整数,等于返回0,大于返回正整数 40 | strcpy(str1,str2); //把str2复制给str1 41 | strcat(str1,str2); //把str2接到str1后面 42 | ``` 43 | 44 | ## math.h 45 | 46 | ```c++ 47 | #include 48 | fabs(-12.56); //12.56 49 | floor(-5.2); //-6 向下(小)取整 50 | ceil(-5.2); //5 向上(大)取整 51 | double db = pow(2.0,3.0); //8.000000 52 | sqrt(2.0); //1.414214 53 | log(1.0); //0.000000 以自然对数为底的对数 54 | //log(a)b=logb/loga 必须用换底公式 55 | const double pi = acos(-1.0); 56 | sin(pi*45/180); 57 | cos(pi*45/180); 58 | tan(pi*45/180); //弧度制!! 59 | asin(1); 60 | acos(-1.0); 61 | atan(0); 62 | round(3.5); //4 四舍五入 63 | ``` 64 | 65 | ## printf 66 | 67 | ```c++ 68 | #include 69 | #include 70 | %5d; //宽为5的整数,超过5位按实际输出,不够5位右对齐输出 71 | %05d; //宽为5的整数,超过5位按实际输出,不够5位前置补0右对齐 72 | %5.2f; //宽为5的浮点数,小数点有2位,小数点占一位, 不够5位右对齐输出 73 | %6.9s; //表示显示一个长度不小于6且不大于9的字符串。若大于9, 则第9个字符以后的内容将被删除。 74 | %ld; //表示输出long整数 75 | %lf; //表示输出double浮点数 76 | %-7d; //表示输出7位整数左对齐 空位补齐 77 | %-10s; //表示输出10个字符左对齐 78 | 79 | /*按整型输出,默认右对齐*/ 80 | printf("%d\n",PrintVal); 81 | /*按整型输出,补齐4位的宽度,补齐位为空格,默认右对齐*/ 82 | printf("%4d\n",PrintVal); 83 | /*按整形输出,补齐4位的宽度,补齐位为0,默认右对齐*/ 84 | printf("%04d\n",PrintVal); 85 | 86 | /*按16进制输出,默认右对齐*/ 87 | printf("%x\n",PrintVal); 88 | /*按16进制输出,补齐4位的宽度,补齐位为空格,默认右对齐*/ 89 | printf("%4x\n",PrintVal); 90 | /*按照16进制输出,补齐4位的宽度,补齐位为0,默认右对齐*/ 91 | printf("%04x\n",PrintVal); 92 | 93 | /*按8进制输出,默认右对齐*/ 94 | printf("%o\n",PrintVal); 95 | /*按8进制输出,补齐4位的宽度,补齐位为空格,默认右对齐*/ 96 | printf("%4o\n",PrintVal); 97 | /*按照8进制输出,补齐4位的宽度,补齐位为0,默认右对齐*/ 98 | printf("%04o\n",PrintVal); 99 | ``` 100 | 101 | # 常用模板代码 102 | 103 | ## 日期转换 104 | 105 | 闰年判断 106 | 107 | ```c++ 108 | bool IsLeapYear(int year){ 109 | return (year%4==0 && year%100!=0) || (year%400==0); 110 | } 111 | ``` 112 | 113 | ## 反序数 114 | 115 | ```c++ 116 | int Reverse(int x){ 117 | int revx=0; 118 | while(x != 0){ 119 | revx*=10; 120 | revx+=x%10; 121 | x/=10; 122 | } 123 | return revx; 124 | } 125 | ``` 126 | 127 | ## 最大公约数 & 最小公倍数 128 | 129 | ```c++ 130 | //最大公约数求法 131 | int GCD(int a,int b){ //辗转相除法 a大b小 132 | if(b==0){ 133 | return a; 134 | }else{ 135 | return GCD(b,a%b); 136 | } 137 | } 138 | //最小公倍数求法 139 | a * b / GCD(a,b); 140 | ``` 141 | 142 | ## 质数和分解质因数 143 | 144 | ### 1. 质数判定:若到sqrt(n)的整数,所有正整数均不能整除n,则可以判断n为素数(小于2必定不是素数) 145 | 146 | ### 2. 某范围内的素数筛法 147 | 148 | ```c++ 149 | const int MAXN = 10001; 150 | vector prime; //保存质数 151 | bool isPrime[MAXN]; //标记数组 152 | void Initial(){ 153 | //初始化 154 | for(int i=0;i 208 | #include 209 | #define SIZE 1000 210 | using namespace std; 211 | queue q_x, q_y, q_pos; 212 | int step[4][2] = { 213 | {1,0},{0,1},{-1,0},{0,-1} 214 | }; 215 | int m, n, board[SIZE][SIZE], start_x, start_y, target_x, target_y; 216 | bool vis[SIZE][SIZE]; 217 | void bfs() { 218 | bool flag = false; //判断是否成功的标志 219 | while (!q_x.empty() && !q_y.empty()) { //判断队是否为空 220 | for (int k = 0; k < 4; ++k) { //先遍历 221 | int tx = q_x.front() + step[k][0]; //状态转移 222 | int ty = q_y.front() + step[k][1]; 223 | if (tx < 1 || ty < 1 || tx > n || ty > m) //判断边界 224 | continue; 225 | if (!board[tx][ty] && !vis[tx][ty]) { 226 | vis[tx][ty] = true; //标记 227 | q_x.push(tx); //入队 228 | q_y.push(ty); 229 | q_pos.push(q_pos.front() + 1); 230 | } 231 | if (tx == target_x&&ty == target_y) { //判断到达目标的条件 232 | flag = true; 233 | break; 234 | } 235 | } 236 | if (flag) 237 | break; 238 | q_x.pop(); //队首出队 239 | q_y.pop(); 240 | q_pos.pop(); 241 | } 242 | return; 243 | } 244 | int main() 245 | { 246 | while (cin >> m >> n) { 247 | for (int i = 1; i <= m; ++i) { 248 | for (int j = 1; j <= n; ++j) { 249 | cin >> board[i][j]; 250 | } 251 | } 252 | cin >> start_x >> start_y >> target_x >> target_y; 253 | q_x.push(start_x); 254 | q_y.push(start_y); //初始入队 255 | q_pos.push(0); 256 | vis[start_x][start_y] = true; 257 | bfs(); 258 | cout << q_pos.back() << endl; 259 | } 260 | return 0; 261 | } 262 | ``` 263 | ## DFS 264 | 265 | ```c++ 266 | int check(参数) 267 | { 268 | if(满足条件) 269 | return 1; 270 | return 0; 271 | } 272 | 273 | void dfs(int step) 274 | { 275 | 判断边界 276 | { 277 | 相应操作 278 | } 279 | 尝试每一种可能 280 | { 281 | 满足check条件 282 | 标记 283 | 继续下一步dfs(step+1) 284 | 恢复初始状态(回溯的时候要用到) 285 | } 286 | } 287 | ``` 288 | 289 | ## 并查集 290 | 291 | 1. 用于判断图是否为连通图 292 | 2. 求图的连通分量 293 | 294 | ```c++ 295 | #include 296 | #include 297 | using namespace std; 298 | const int MAXN = 1000; 299 | int father[MAXN]; //父亲结点 300 | int height[MAXN]; //结点高度 301 | void Initial(int n){ //初始化 302 | for(int i=0;i<=n;i++){ 303 | father[i]=i; //每个结点的父亲为自己 304 | height[i]=0; //每个结点初始高度为0 305 | } 306 | } 307 | int Find(int x){ //查找根节点 308 | if(x!=father[x]){ //路径压缩 309 | father[x]=Find(father[x]); 310 | } 311 | return father[x]; 312 | } 313 | void Union(int x,int y){ //合并集合 314 | x=Find(x); 315 | y=Find(y); 316 | if(x!=y){ //矮树作为高数的子树 317 | if(height[x] 343 | struct Edge{ 344 | int from; //边的起点 345 | int to; //边的终点 346 | int length; //边的长度 347 | friend bool operator < (const Edge& e1,const Edge& e2) const { 348 | return e1.length < e2.length; 349 | } 350 | }; 351 | Edge edge[MAXN*MAXN]; //边集 352 | /*上边并查集的所有内容*/ 353 | void Kruskal(int n,int edgeNum){ //克鲁斯卡尔算法,n为点数,edgeNum为边数 354 | Initial(n); 355 | sort(edge,edge+edgeNum); //按权值排序,重载运算符,无需重写cmp 356 | int sum=0; //初始化最小生成树长度 357 | for(int i=0;i 374 | #include 375 | #include 376 | #include 377 | #include 378 | using namespace std; 379 | 380 | #define MAXN 9999 381 | #define MAXSIZE 10 382 | #define DLEN 4 383 | 384 | class BigNum 385 | { 386 | private: 387 | int a[1500]; //可以控制大数的位数 388 | int len; //大数长度 389 | public: 390 | BigNum(){ len = 1;memset(a,0,sizeof(a)); } //构造函数 391 | BigNum(const int); //将一个int类型的变量转化为大数 392 | BigNum(const char*); //将一个字符串类型的变量转化为大数 393 | BigNum(const BigNum &); //拷贝构造函数 394 | BigNum &operator=(const BigNum &); //重载赋值运算符,大数之间进行赋值运算 395 | 396 | friend istream& operator>>(istream&, BigNum&); //重载输入运算符 397 | friend ostream& operator<<(ostream&, BigNum&); //重载输出运算符 398 | 399 | BigNum operator+(const BigNum &) const; //重载加法运算符,两个大数之间的相加运算 400 | BigNum operator-(const BigNum &) const; //重载减法运算符,两个大数之间的相减运算 401 | BigNum operator*(const BigNum &) const; //重载乘法运算符,两个大数之间的相乘运算 402 | BigNum operator/(const int &) const; //重载除法运算符,大数对一个整数进行相除运算 403 | 404 | BigNum operator^(const int &) const; //大数的n次方运算 405 | int operator%(const int &) const; //大数对一个int类型的变量进行取模运算 406 | bool operator>(const BigNum & T)const; //大数和另一个大数的大小比较 407 | bool operator>(const int & t)const; //大数和一个int类型的变量的大小比较 408 | 409 | void print(); //输出大数 410 | }; 411 | ``` 412 | ### 将一个int类型的变量转化为大数 413 | ```c++ 414 | BigNum::BigNum(const int b) 415 | { 416 | int c,d = b; 417 | len = 0; 418 | memset(a,0,sizeof(a)); 419 | while(d > MAXN) 420 | { 421 | c = d - (d / (MAXN + 1)) * (MAXN + 1); 422 | d = d / (MAXN + 1); 423 | a[len++] = c; 424 | } 425 | a[len++] = d; 426 | } 427 | ``` 428 | ### 将一个字符串类型的变量转化为大数 429 | ```c++ 430 | BigNum::BigNum(const char*s) 431 | { 432 | int t,k,index,l,i; 433 | memset(a,0,sizeof(a)); 434 | l=strlen(s); 435 | len=l/DLEN; 436 | if(l%DLEN) 437 | len++; 438 | index=0; 439 | for(i=l-1;i>=0;i-=DLEN) 440 | { 441 | t=0; 442 | k=i-DLEN+1; 443 | if(k<0) 444 | k=0; 445 | for(int j=k;j<=i;j++) 446 | t=t*10+s[j]-'0'; 447 | a[index++]=t; 448 | } 449 | } 450 | ``` 451 | ### 拷贝构造函数 452 | ```c++ 453 | BigNum::BigNum(const BigNum & T) : len(T.len) 454 | { 455 | int i; 456 | memset(a,0,sizeof(a)); 457 | for(i = 0 ; i < len ; i++) 458 | a[i] = T.a[i]; 459 | } 460 | ``` 461 | ### 重载赋值运算符,大数之间进行赋值运算 462 | ```c++ 463 | BigNum & BigNum::operator=(const BigNum & n) 464 | { 465 | int i; 466 | len = n.len; 467 | memset(a,0,sizeof(a)); 468 | for(i = 0 ; i < len ; i++) 469 | a[i] = n.a[i]; 470 | return *this; 471 | } 472 | ``` 473 | ### 重载输入运算符 474 | ```c++ 475 | istream& operator>>(istream & in, BigNum & b) 476 | { 477 | char ch[MAXSIZE*4]; 478 | int i = -1; 479 | in>>ch; 480 | int l=strlen(ch); 481 | int count=0,sum=0; 482 | for(i=l-1;i>=0;) 483 | { 484 | sum = 0; 485 | int t=1; 486 | for(int j=0;j<4&&i>=0;j++,i--,t*=10) 487 | { 488 | sum+=(ch[i]-'0')*t; 489 | } 490 | b.a[count]=sum; 491 | count++; 492 | } 493 | b.len =count++; 494 | return in; 495 | 496 | } 497 | ``` 498 | ### 重载输出运算符 499 | ```c++ 500 | ostream& operator<<(ostream& out, BigNum& b) 501 | { 502 | int i; 503 | cout << b.a[b.len - 1]; 504 | for(i = b.len - 2 ; i >= 0 ; i--) 505 | { 506 | cout.width(DLEN); 507 | cout.fill('0'); 508 | cout << b.a[i]; 509 | } 510 | return out; 511 | } 512 | ``` 513 | ### 两个大数之间的相加运算 514 | ```c++ 515 | BigNum BigNum::operator+(const BigNum & T) const 516 | { 517 | BigNum t(*this); 518 | int i,big; //位数 519 | big = T.len > len ? T.len : len; 520 | for(i = 0 ; i < big ; i++) 521 | { 522 | t.a[i] +=T.a[i]; 523 | if(t.a[i] > MAXN) 524 | { 525 | t.a[i + 1]++; 526 | t.a[i] -=MAXN+1; 527 | } 528 | } 529 | if(t.a[big] != 0) 530 | t.len = big + 1; 531 | else 532 | t.len = big; 533 | return t; 534 | } 535 | ``` 536 | ### 两个大数之间的相减运算 537 | ```c++ 538 | BigNum BigNum::operator-(const BigNum & T) const 539 | { 540 | int i,j,big; 541 | bool flag; 542 | BigNum t1,t2; 543 | if(*this>T) 544 | { 545 | t1=*this; 546 | t2=T; 547 | flag=0; 548 | } 549 | else 550 | { 551 | t1=T; 552 | t2=*this; 553 | flag=1; 554 | } 555 | big=t1.len; 556 | for(i = 0 ; i < big ; i++) 557 | { 558 | if(t1.a[i] < t2.a[i]) 559 | { 560 | j = i + 1; 561 | while(t1.a[j] == 0) 562 | j++; 563 | t1.a[j--]--; 564 | while(j > i) 565 | t1.a[j--] += MAXN; 566 | t1.a[i] += MAXN + 1 - t2.a[i]; 567 | } 568 | else 569 | t1.a[i] -= t2.a[i]; 570 | } 571 | t1.len = big; 572 | while(t1.a[len - 1] == 0 && t1.len > 1) 573 | { 574 | t1.len--; 575 | big--; 576 | } 577 | if(flag) 578 | t1.a[big-1]=0-t1.a[big-1]; 579 | return t1; 580 | } 581 | ``` 582 | ### 两个大数之间的相乘运算 583 | ```c++ 584 | BigNum BigNum::operator*(const BigNum & T) const 585 | { 586 | BigNum ret; 587 | int i,j,up; 588 | int temp,temp1; 589 | for(i = 0 ; i < len ; i++) 590 | { 591 | up = 0; 592 | for(j = 0 ; j < T.len ; j++) 593 | { 594 | temp = a[i] * T.a[j] + ret.a[i + j] + up; 595 | if(temp > MAXN) 596 | { 597 | temp1 = temp - temp / (MAXN + 1) * (MAXN + 1); 598 | up = temp / (MAXN + 1); 599 | ret.a[i + j] = temp1; 600 | } 601 | else 602 | { 603 | up = 0; 604 | ret.a[i + j] = temp; 605 | } 606 | } 607 | if(up != 0) 608 | ret.a[i + j] = up; 609 | } 610 | ret.len = i + j; 611 | while(ret.a[ret.len - 1] == 0 && ret.len > 1) 612 | ret.len--; 613 | return ret; 614 | } 615 | ``` 616 | ### 大数对一个整数进行相除运算 617 | ```c++ 618 | BigNum BigNum::operator/(const int & b) const 619 | { 620 | BigNum ret; 621 | int i,down = 0; 622 | for(i = len - 1 ; i >= 0 ; i--) 623 | { 624 | ret.a[i] = (a[i] + down * (MAXN + 1)) / b; 625 | down = a[i] + down * (MAXN + 1) - ret.a[i] * b; 626 | } 627 | ret.len = len; 628 | while(ret.a[ret.len - 1] == 0 && ret.len > 1) 629 | ret.len--; 630 | return ret; 631 | } 632 | ``` 633 | ### 大数对一个int类型的变量进行取模运算 634 | ```c++ 635 | int BigNum::operator %(const int & b) const 636 | { 637 | int i,d=0; 638 | for (i = len-1; i>=0; i--) 639 | { 640 | d = ((d * (MAXN+1))% b + a[i])% b; 641 | } 642 | return d; 643 | } 644 | ``` 645 | ### 大数的n次方运算 646 | ```c++ 647 | BigNum BigNum::operator^(const int & n) const 648 | { 649 | BigNum t,ret(1); 650 | int i; 651 | if(n<0) 652 | exit(-1); 653 | if(n==0) 654 | return 1; 655 | if(n==1) 656 | return *this; 657 | int m=n; 658 | while(m>1) 659 | { 660 | t=*this; 661 | for( i=1;i<<1<=m;i<<=1) 662 | { 663 | t=t*t; 664 | } 665 | m-=i; 666 | ret=ret*t; 667 | if(m==1) 668 | ret=ret*(*this); 669 | } 670 | return ret; 671 | } 672 | ``` 673 | ### 大数和另一个大数的大小比较 674 | ```c++ 675 | bool BigNum::operator>(const BigNum & T) const 676 | { 677 | int ln; 678 | if(len > T.len) 679 | return true; 680 | else if(len == T.len) 681 | { 682 | ln = len - 1; 683 | while(a[ln] == T.a[ln] && ln >= 0) 684 | ln--; 685 | if(ln >= 0 && a[ln] > T.a[ln]) 686 | return true; 687 | else 688 | return false; 689 | } 690 | else 691 | return false; 692 | } 693 | ``` 694 | ### 大数和一个int类型的变量的大小比较 695 | ```c++ 696 | bool BigNum::operator >(const int & t) const 697 | { 698 | BigNum b(t); 699 | return *this>b; 700 | } 701 | ``` 702 | ### 输出大数 703 | ```c++ 704 | void BigNum::print() 705 | { 706 | int i; 707 | cout << a[len - 1]; 708 | for(i = len - 2 ; i >= 0 ; i--) 709 | { 710 | cout.width(DLEN); 711 | cout.fill('0'); 712 | cout << a[i]; 713 | } 714 | cout << endl; 715 | } 716 | ``` 717 | ### 使用 718 | ```c++ 719 | BigNum a,b,c; 720 | char s1[1000]; 721 | char s2[1000]; 722 | int main(){ 723 | scanf("%s",s1); 724 | scanf("%s",s2); 725 | 726 | a = s1; 727 | b = s2; 728 | c = a + b; 729 | cout<1000) puts("yeah"); //只能用>,不能用<、<=、.>= 757 | 758 | return 0; 759 | } 760 | ``` 761 | 762 | ## 最短路径 763 | ### Dijkstra 764 | ```c++ 765 | #include 766 | #include 767 | #include 768 | #include 769 | #include 770 | #include 771 | #define MAXN 20000 772 | #define INF 2147483647 773 | using namespace std; 774 | typedef pair pii; 775 | priority_queue, greater > pq; 776 | struct edge 777 | { 778 | int to; 779 | int cost; 780 | }; 781 | vector G[MAXN];//g[i]--i to g[i].to cost cost 782 | int n, m, s; 783 | int dis[MAXN]; 784 | void dijk(int s) 785 | { 786 | for(int i = 1; i <= n; i++) 787 | dis[i] = INF; 788 | dis[s] = 0; 789 | pq.push(make_pair(0,s)); 790 | // cout< dis[x] + e.cost) 801 | { 802 | dis[e.to] = dis[x] + e.cost; 803 | pq.push(make_pair(dis[e.to], e.to)); 804 | // cout<> n >> m >> s; 812 | int from, to, cost; 813 | edge in; 814 | for(int i = 0; i < m; i++) 815 | { 816 | scanf("%d%d%d",&from ,&to ,&cost); 817 | in.to = to; in.cost = cost; 818 | G[from].push_back(in); 819 | } 820 | // cout< 841 | #include 842 | #include 843 | #include 844 | using namespace std; 845 | struct node{ 846 | int eEarlist; 847 | int eLatest; 848 | int eDiff; 849 | int expd; 850 | int startV, endV; 851 | }; 852 | void DFS(int u); 853 | int G[510][510]; 854 | const int MAX = 0x7FFFFFFF; 855 | vector vEarlist, vLatest;//状态最早开始时间和最迟开始时间 856 | vector actvt;//活动记录 857 | vector topoSeq;//拓扑排序的序列 858 | vector inDegree;//入度(配合拓扑排序使用) 859 | vector > path;//关键路径(可能不止一条) 860 | vector tempPath;//DFS作临时路径 861 | vector > pre;//状态的父状态(通过某条活动a->b,则pre[b]=a(其中一条)) 862 | int N, M, startV, endV; 863 | int main() 864 | { 865 | fill(G[0], G[0]+510*510, MAX); 866 | cin >> N >> M >> startV >>endV; 867 | vEarlist.resize(N); vLatest.resize(N); 868 | actvt.resize(M); 869 | inDegree.resize(N); 870 | fill(inDegree.begin(), inDegree.end(), 0); 871 | for(int i=0; i> u >> v >> expd; 874 | G[u][v] = expd; 875 | actvt[i].expd = expd; actvt[i].startV = u; actvt[i].endV = v; 876 | inDegree[v]++; 877 | } 878 | //1.先进行拓扑排序,获得拓扑序列 879 | queue q; 880 | q.push(startV); 881 | while(!q.empty()){ 882 | int u = q.front(); 883 | q.pop(); 884 | for(int i=0; i vEarlist[j]){ 900 | vEarlist[j] = vEarlist[u] + G[u][j]; 901 | } 902 | } 903 | } 904 | //3.找到最终结束点的最早开始时间,更新vLatest的最终点 905 | fill(vLatest.begin(), vLatest.end(), vEarlist[endV]); 906 | //4.对拓扑排序的逆序,进行操作,找到其相邻边,更新vLatest 907 | reverse(topoSeq.begin(), topoSeq.end());//直接改就行,因为用不到了 908 | for(int i=0; i顶点最早开始时间 917 | // 更新其最迟开始时间-->后顶点最迟开始时间-时长 918 | // 更新可休息时间-->最迟-最早 919 | // 如果可休息时间为0-->pre[后].push_back(前) 920 | // 921 | pre.resize(M); 922 | for(int i=0; i=0; j--){ 936 | if(j!=path[i].size()-1)cout << "-->"; 937 | cout << path[i][j]; 938 | } 939 | cout << endl; 940 | } 941 | } 942 | void DFS(int u) 943 | { 944 | tempPath.push_back(u); 945 | if(u == startV){ 946 | path.push_back(tempPath); 947 | tempPath.pop_back(); 948 | return; 949 | } 950 | for(int i=0; i= Arr[j] && List[i]arr[j]为递增 973 | //lis[i] Stack[top]) 994 | Stack[++top] = Arr[i]; 995 | else 996 | { 997 | int low = 1; 998 | int high = top; 999 | while(low <= high) 1000 | { 1001 | int mid = (low + high)/2; 1002 | if(Arr[i] > Stack[mid]) 1003 | low = mid + 1; 1004 | else 1005 | high = mid - 1; 1006 | } 1007 | Stack[low] = Arr[i]; 1008 | } 1009 | } 1010 | return top; 1011 | } 1012 | ``` 1013 | ### 最大连续子序列和 1014 | 给定一个整数序列,找出所有连续子序列中元素和最大的一个,并找到起点和终点。 1015 | ```c++ 1016 | int a[110000],N,pos1,pos2,Start,End; 1017 | //Start、End存储最大连续子序列的起点和终点 1018 | int MaxSubSum(int *a) 1019 | { 1020 | int MaxSum = a[0],Sum = a[0]; 1021 | pos1 = pos2 = Start = End = 0; 1022 | for(int i = 1; i < N; ++i) 1023 | { 1024 | Sum += a[i]; 1025 | if(Sum < a[i]) 1026 | { 1027 | Sum = a[i]; 1028 | pos1 = i; 1029 | pos2 = i; 1030 | } 1031 | else 1032 | { 1033 | pos2 = i; 1034 | } 1035 | 1036 | if(MaxSum < Sum) 1037 | { 1038 | MaxSum = Sum; 1039 | Start = pos1; 1040 | End = pos2; 1041 | } 1042 | } 1043 | return MaxSum; 1044 | } 1045 | ``` 1046 | ### 最大连续子矩阵和 1047 | 给你一个N,接下来是N*N的矩阵。数有正有负,求最大的子矩阵和。 1048 | ```c++ 1049 | #include 1050 | #include 1051 | #include 1052 | using namespace std; 1053 | 1054 | int map[110][110],dp[110][110]; 1055 | int main() 1056 | { 1057 | int N,a; 1058 | while(~scanf("%d",&N) && N) 1059 | { 1060 | memset(map,0,sizeof(map)); 1061 | memset(dp,0,sizeof(dp)); 1062 | for(int i = 1; i <= N; i++) 1063 | { 1064 | for(int j = 1; j <= N; j++) 1065 | { 1066 | scanf("%d",&a); 1067 | map[i][j] = map[i][j-1] + a; 1068 | //map[i][j]表示第i行前j列的和 1069 | } 1070 | } 1071 | int Max = -0xffffff0; 1072 | for(int j = 1; j <= N; j++) 1073 | { 1074 | for(int i = 1; i <= j; i++) 1075 | { 1076 | dp[i][j] = 0; 1077 | for(int k = 1; k <= N; k++) 1078 | { 1079 | //ans求的是前k行,第i到第j列的最大和 1080 | dp[i][j]= max(dp[i][j]+map[k][j]-map[k][i-1],map[k][j]-map[k][i-1]); 1081 | if(dp[i][j] > Max) 1082 | Max = dp[i][j]; 1083 | } 1084 | } 1085 | } 1086 | printf("%d\n",Max); 1087 | } 1088 | return 0; 1089 | } 1090 | ``` 1091 | ### 最大M个连续子段的和 1092 | ```c++ 1093 | #include 1094 | #include 1095 | #include 1096 | using namespace std; 1097 | 1098 | int dp[1000010]; 1099 | int maxn[1000010]; 1100 | int num[1000010]; 1101 | int main() 1102 | { 1103 | int M,N; 1104 | while(~scanf("%d%d",&M,&N)) 1105 | { 1106 | dp[0] = maxn[0] = 0; 1107 | for(int i = 1; i <= N; i++) 1108 | { 1109 | scanf("%d",&num[i]); 1110 | dp[i] = maxn[i] = 0; 1111 | } 1112 | int MAXN; 1113 | for(int i = 1; i <= M; i++)//分为i段 1114 | { 1115 | MAXN = -0xffffff0; 1116 | for(int j = i; j <= N; j++)//第j个数字 1117 | { 1118 | dp[j] = max(dp[j-1]+num[j],maxn[j-1]+num[j]); 1119 | maxn[j-1] = MAXN; 1120 | MAXN = max(MAXN,dp[j]); 1121 | } 1122 | } 1123 | printf("%d\n",MAXN); 1124 | } 1125 | 1126 | return 0; 1127 | } 1128 | ``` 1129 | ### 最大不连续子序列和 1130 | 给你一个矩阵,不能选择每行中相邻的数字,也不能选当前行的上一行和下一行,问使所选数和最大的值是多少? 1131 | 对于每一行,都是求最大不连续子段和。 1132 | ```c++ 1133 | #include 1134 | #include 1135 | #include 1136 | using namespace std; 1137 | const int MAXN = 200000; 1138 | int dpa[MAXN+20],dpb[MAXN+20],row[MAXN+20]; 1139 | int main() 1140 | { 1141 | int M,N,num; 1142 | while(~scanf("%d%d",&M,&N)) 1143 | { 1144 | memset(row,0,sizeof(row)); 1145 | for(int i = 0; i < M; i++) 1146 | { 1147 | dpa[0] = dpb[0] = 0; 1148 | for(int j = 0; j < N; j++) 1149 | { 1150 | scanf("%d",&num); 1151 | dpa[j+1] = max(dpa[j],dpb[j]);// dp[j+1] 是到j为止,不吃j所能吃到的最大值 1152 | dpb[j+1] = dpa[j] + num;//吃j所能吃到的最大值 1153 | } 1154 | row[i] = max(dpa[N],dpb[N]); 1155 | } 1156 | dpa[0] = dpb[0] = 0; 1157 | for(int i = 0; i < M; i++) 1158 | { 1159 | dpa[i+1] = max(dpa[i],dpb[i]); 1160 | dpb[i+1] = dpa[i] + row[i]; 1161 | } 1162 | int ans = max(dpa[M],dpb[M]); 1163 | printf("%d\n",ans); 1164 | } 1165 | return 0; 1166 | } 1167 | ``` 1168 | ### 最长公共子序列 1169 | 给定两个序列,找出在两个序列中同时出现的最长子序列的长度。一个子序列是出现在相对顺序的序列,但不一定是连续的。 1170 | #### 1.求最长公共子序列长度 1171 | ```c++ 1172 | char s1[220],s2[220]; 1173 | int dp[220][220]; 1174 | //求串s1和串s2的公共子序列 1175 | int lcs(char *s1,char *s2) 1176 | { 1177 | int len1 = strlen(s1); 1178 | int len2 = strlen(s2); 1179 | for(int i = 0; i <= len1; ++i) 1180 | { 1181 | for(int j = 0; j <= len2; ++j) 1182 | { 1183 | if(i == 0 || j == 0) 1184 | dp[i][j] = 0; 1185 | else if(s1[i-1] == s2[j-1]) 1186 | dp[i][j] = dp[i-1][j-1] + 1; 1187 | else 1188 | dp[i][j] = max(dp[i-1][j],dp[i][j-1]); 1189 | } 1190 | } 1191 | return dp[len1][len2]; 1192 | } 1193 | ``` 1194 | #### 2.求最长公共子序列长度,并输出路径 1195 | ```c++ 1196 | int dp[110][110],pre[110][110],len1,len2; 1197 | char s1[110],s2[110]; 1198 | 1199 | void LCS(char *s1,char *s2) 1200 | { 1201 | for(int i = 0; i <= len1; ++i) 1202 | pre[i][0] = 1; 1203 | for(int i = 0; i <= len2; ++i) 1204 | pre[0][i] = 2; 1205 | //得到最长公共子序列,并标记dp[i][j]的上一个状态,用来回溯寻找路径 1206 | for(int i = 0; i <= len1; ++i) 1207 | { 1208 | for(int j = 0; j <= len2; ++j) 1209 | { 1210 | if(i == 0 || j == 0) 1211 | dp[i][j] = 0; 1212 | if(s1[i-1] == s2[j-1]) 1213 | { 1214 | dp[i][j] = dp[i-1][j-1] + 1; 1215 | pre[i][j] = 0; 1216 | } 1217 | else if(dp[i-1][j] >= dp[i][j-1]) 1218 | { 1219 | dp[i][j] = dp[i-1][j]; 1220 | pre[i][j] = 1; 1221 | } 1222 | else 1223 | { 1224 | dp[i][j] = dp[i][j-1]; 1225 | pre[i][j] = 2; 1226 | } 1227 | } 1228 | } 1229 | } 1230 | 1231 | void Print(int i,int j) //回溯输出新的字符串序列 1232 | { 1233 | if(i == 0 && j == 0) 1234 | return ; 1235 | if(pre[i][j] == 0) 1236 | { 1237 | Print(i-1,j-1); 1238 | printf("%c",s1[i-1]); 1239 | } 1240 | else if(pre[i][j] == 1) 1241 | { 1242 | Print(i-1,j); 1243 | printf("%c",s1[i-1]); 1244 | } 1245 | else 1246 | { 1247 | Print(i,j-1); 1248 | printf("%c",s2[j-1]); 1249 | } 1250 | } 1251 | 1252 | void solve(char *s1,char *s2) 1253 | { 1254 | len1 = strlen(s1); 1255 | len2 = strlen(s2); 1256 | LCS(s1,s2); 1257 | Print(len1,len2); 1258 | printf("\n"); 1259 | } 1260 | ``` 1261 | ### 最长回文子序列 1262 | 给一个字符串,找出它的最长的回文子序列LPS的长度。例如,如果给定的序列是“BBABCBCAB”,则输出应该是7,“BABCBAB”是在它的最长回文子序列。 1263 | ```c++ 1264 | char s[2020]; 1265 | int dp[2020][2020]; 1266 | //dp[i][j]表示s[i~j]最长回文子序列 1267 | int LPS(char *s) 1268 | { 1269 | memset(dp,0,sizeof(dp)); 1270 | int len = strlen(s); 1271 | for(int i = len-1; i >= 0; --i) 1272 | { 1273 | dp[i][i] = 1; 1274 | for(int j = i+1; j < len; ++j) 1275 | { 1276 | if(s[i] == s[j]) //头尾相同,最长回文子序列为去头尾的部分LPS加上头和尾 1277 | dp[i][j] = dp[i+1][j-1] + 2; 1278 | else //头尾不同,最长回文子序列是去头部分的LPS和去尾部分LPS较长的 1279 | dp[i][j] = max(dp[i][j-1],dp[i+1][j]); 1280 | } 1281 | } 1282 | 1283 | return dp[0][len-1]; 1284 | } 1285 | ``` 1286 | ### 最长回文子串 1287 | 给一个字符串,找出它的最长的回文子串(连续的串)的长度。 1288 | ```c++ 1289 | char str[2000020],s[2000020]; 1290 | //str为待求的原串,s为处理后的新串 1291 | int P[2000020]; 1292 | //P[i]记录的是以字符str[i]为中心的最长回文串的半径 1293 | void Pre(char *str) 1294 | { 1295 | int len = strlen(str); 1296 | s[0] = '$'; 1297 | s[1] = '#'; 1298 | for(int i = 0; i < len; ++i) 1299 | { 1300 | s[i*2+2] = str[i]; 1301 | s[i*2+3] = '#'; 1302 | } 1303 | s[len*2+2] = '\0'; 1304 | } 1305 | //返回最长回文子串长度 1306 | int Manacher(char *s) 1307 | { 1308 | int Max = 0; 1309 | int len = strlen(s); 1310 | int id = 0; 1311 | for(int i = 1; i < len; ++i) 1312 | { 1313 | if(Max > i) 1314 | P[i] = min(P[2*id-i],P[id]+id-i); 1315 | else 1316 | P[i] = 1; 1317 | while(s[i+P[i]] == s[i-P[i]]) 1318 | P[i]++; 1319 | if(P[i]+i > Max) 1320 | { 1321 | Max = P[i]+i; 1322 | id = i; 1323 | } 1324 | } 1325 | int ans = 0; 1326 | for(int i = 2; i < len; ++i) 1327 | if(ans < P[i]) 1328 | ans = P[i]; 1329 | return ans-1;//返回最长回文子串长度 1330 | } 1331 | 1332 | 1333 | int main() 1334 | { 1335 | while(~scanf("%s",str)) 1336 | { 1337 | Pre(str); 1338 | printf("%d\n",Manacher(s)); 1339 | } 1340 | 1341 | return 0; 1342 | } 1343 | ``` 1344 | ### 最小编辑距离 1345 | 给定一个长度为m和n的两个字符串,设有以下几种操作:替换(R),插入(I)和删除(D)且都是相同的操作。寻找到转换一个字符串插入到另一个需要修改的最小(操作)数量。 1346 | ```c++ 1347 | int dp[1010][1010],len1,len2; 1348 | char s1[1010],s2[1010]; 1349 | int EditDist(char *s1,char *s2) 1350 | { 1351 | int len1 = strlen(s1); 1352 | int len2 = strlen(s2); 1353 | //当两个字符串的大小为0,其操作距离为0。 1354 | //当其中一个字符串的长度是零,需要的操作距离就是另一个字符串的长度. 1355 | for(int i = 0; i <= len1; ++i) 1356 | dp[i][0] = i; 1357 | for(int i = 0; i <= len2; ++i) 1358 | dp[0][i] = i; 1359 | 1360 | for(int i = 1; i <= len1; ++i) 1361 | { 1362 | for(int j = 1; j <= len2; ++j) 1363 | { 1364 | if(s1[i-1] == s2[j-1]) //对齐s1[i-1]和s2[j-1],不需改变 1365 | dp[i][j] = dp[i-1][j-1]; 1366 | else 1367 | dp[i][j] = min(dp[i-1][j],min(dp[i][j-1],dp[i-1][j-1])) + 1; 1368 | //s1前缀右对齐,s2前缀右为' ',删除s1第i个字符 -> dp[i-1][j] 1369 | //s2前缀右对齐,s1前缀右为' ',删除s2第j个字符 -> dp[i][j-1] 1370 | //s1前缀右对齐,s2前缀右对齐,i、j不一样,替换 -> dp[i-1][j-1] 1371 | } 1372 | } 1373 | return dp[len1][len2]; 1374 | } 1375 | ``` 1376 | ### 01背包 1377 | 有 N 件物品和一个容量为 V 的背包。放入第 i 件物品耗费的空间是 C i ,得到的价值是 W i 。求解将哪些物品装入背包可使价值总和最大。 1378 | 要求恰好装满背包,在初始化时 F[0] = 0 ,其它 F[1~V ] 均设为 −∞ 。 1379 | 没有要求必须把背包装满,而是只希望价格尽量大,初始化时应该将 F[0~V ] 全部设为 0 。 1380 | 求方案数时,在初始化时F[0] = 1,其他F[1~V]均设为0。 1381 | 1382 | ```c++ 1383 | int c[1100],w[1100],dp[1100],V; 1384 | //c[]:物品所占容量;w[]物品的价值;V为背包容量 1385 | memset(dp,0,sizeof(dp)); 1386 | for(int i = 0; i < N; ++i)//第i件物品 1387 | { 1388 | for(int j = V; j >= c[i]; j--)//填满空间j 1389 | { 1390 | dp[j] = max(dp[j],dp[j-c[i]] + w[i]); 1391 | } 1392 | } 1393 | ``` 1394 | ### 完全背包 1395 | 有 N 种物品和一个容量为 V 的背包,每种物品都有无限件可用。放入第 i 种物品的耗费的空间是 C i ,得到的价值是 W i 。求解:将哪些物品装入背包,可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。 1396 | ```c++ 1397 | int c[1100],w[1100],dp[1100],V; 1398 | //c[]:物品所占容量;w[]物品的价值;V为背包容量 1399 | memset(dp,0,sizeof(dp)); 1400 | for(int i = 0; i < N; ++i) 1401 | { 1402 | for(int j = c[i]; j <= V; j++) 1403 | { 1404 | dp[j] = max(dp[j],dp[j-c[i]] + w[i]); 1405 | } 1406 | } 1407 | ``` 1408 | ### 多重背包 1409 | 有 N 种物品和一个容量为 V 的背包。第 i 种物品最多有 M i 件可用,每件耗费的空间是 C i ,价值是 W i 。求解将哪些物品装入背包可使这些物品的耗费的空间总和不超过背包容量,且价值总和最大。 1410 | 二进制优化做法: 1411 | ```c++ 1412 | int c[110],w[110],m[110],dp[100010],V; 1413 | //c[]:物品所占容量;w[]物品的价值;m[]物品的数量;V为背包容量 1414 | void ZeroOne(int cost,int weight)//01背包 1415 | { 1416 | for(int i = V; i >= cost; i--) 1417 | dp[i] = max(dp[i],dp[i-cost]+weight); 1418 | } 1419 | 1420 | void Complete(int cost,int weight)//完全背包 1421 | { 1422 | for(int i = cost; i <= V; i++) 1423 | dp[i] = max(dp[i],dp[i-cost]+weight); 1424 | } 1425 | 1426 | void Multiple(int cost,int weight,int cnt)//多重背包 1427 | { 1428 | //如果总容量比这个物品的容量要小,那么这个物品可以直接取完,相当于完全背包 1429 | if(V <= cnt*cost) 1430 | { 1431 | Complete(cost,weight); 1432 | return; 1433 | } 1434 | else//否则就将多重背包转化为01背包 1435 | { 1436 | int k = 1; 1437 | while(k <= cnt) 1438 | { 1439 | ZeroOne(k*cost,k*weight); 1440 | cnt -= k; 1441 | k <<= 1; 1442 | } 1443 | ZeroOne(cnt*cost,cnt*weight); 1444 | } 1445 | } 1446 | /* 1447 | for(int i = 0; i <= V; i++)//初始化,不要求恰好装满背包 1448 | dp[i] = 0; 1449 | */ 1450 | for(int i = 0; i <= V; i++)//初始化:是否恰好装满背包 1451 | dp[i] = -0xffffff0; 1452 | dp[0] = 0; 1453 | for(int i = 0; i < N; i++) 1454 | Multiple(v[i],v[i],c[i]); 1455 | ``` 1456 | ### 二维费用背包 1457 | ```c++ 1458 | int c1[110],c2[110],w[110],dp[1010][1010],V1,V2; 1459 | memset(dp,0,sizeof(dp)); 1460 | 1461 | for(int i = 0; i < N; i++)//第i个 1462 | { 1463 | for(int j = c1[i]; j <= V1; j++)//一维费用 1464 | { 1465 | for(int k = c2[i]; k <= V2; k++)//二维费用 1466 | { 1467 | dp[j][k] = max(dp[j][k],dp[j-c1[i]][k-c2[i]] + w[i]); 1468 | } 1469 | } 1470 | } 1471 | ``` 1472 | ### 切割钢条 1473 | 给定一段长度为n英寸的钢条和一个价格表Pi,求切割方案,使得销售收益Rn最大。 1474 | 自底向上法 1475 | ```c++ 1476 | #include 1477 | #include 1478 | using namespace std; 1479 | const int INF = 0xffffff0; 1480 | int p[110],r[110];//r[n]来保存子问题 1481 | 1482 | int BOTTOM_UP_CUT_ROD(int n) 1483 | { 1484 | r[0] = 0;//长度为0的钢条没有收益 1485 | for(int j = 1; j <= n; j++)//对j=1,2,3,…,n按升序求解每个规模为j的子问题。 1486 | { 1487 | int q = -INF; 1488 | for(int i = 1; i <= j; i++) 1489 | { 1490 | q = max(q,p[i]+r[j-i]);//直接访问数组r[j-i]来获得规模为j-i的子问题的解 1491 | } 1492 | r[j] = q; 1493 | } 1494 | return r[n]; 1495 | } 1496 | int main() 1497 | { 1498 | int N; 1499 | while(~scanf("%d",&N)) 1500 | { 1501 | for(int i = 1; i <= N; i++) 1502 | scanf("%d",&p[i]); 1503 | 1504 | int ans = BOTTOM_UP_CUT_ROD(N); 1505 | printf("%d\n",ans); 1506 | } 1507 | return 0; 1508 | } 1509 | ``` 1510 | ### 最大矩形问题 1511 | 给你一个直方图,告诉你各个条形矩形的高度,求基线对齐构成的矩形中面积最大的矩形的面积。 1512 | ```c++ 1513 | #include 1514 | #include 1515 | 1516 | int l[100010],r[100010]; 1517 | __int64 h[100010]; 1518 | int main() 1519 | { 1520 | int N; 1521 | while(~scanf("%d",&N) && N!=0) 1522 | { 1523 | memset(h,0,sizeof(h)); 1524 | for(int i = 1; i <= N; i++) 1525 | { 1526 | scanf("%I64d",&h[i]); 1527 | l[i] = r[i] = i; 1528 | } 1529 | 1530 | l[0] = 1; 1531 | r[N+1] = N; 1532 | h[0] = -1; 1533 | h[N+1] = -1; 1534 | //这上边不加就会超时,不加的话下边就可能一直while,跳不出循环 1535 | for(int i = 1; i <= N; i++) 1536 | { 1537 | while(h[l[i]-1] >= h[i])//找位置i的左边界 1538 | l[i] = l[l[i]-1]; 1539 | } 1540 | for(int i = N; i >= 1; i--) 1541 | { 1542 | while(h[r[i]+1] >= h[i])//找位置i的右边界 1543 | r[i] = r[r[i]+1]; 1544 | } 1545 | __int64 MaxArea = -0xffffff0; 1546 | for(int i = 1; i <= N; i++) 1547 | { 1548 | if(h[i]*(r[i]-l[i]+1) > MaxArea) 1549 | MaxArea = h[i]*(r[i]-l[i]+1); 1550 | } 1551 | printf("%I64d\n",MaxArea); 1552 | } 1553 | return 0; 1554 | } 1555 | ``` 1556 | 1557 | ## 二叉树 1558 | 1559 | *二叉排序树(左小右大)中序遍历为升序序列 1560 | 1561 | ### 定义&创建 1562 | 1563 | ```c++ 1564 | struct Node{ 1565 | char data; 1566 | Node * left; 1567 | Node * right; 1568 | Node(char c): data(c),left(NULL),right(NULL){} //构造函数写法 1569 | }; 1570 | //创建二叉树 1571 | Node * root = Build(str); 1572 | Node * Build(string str){ 1573 | //判断返回空树条件 1574 | if(str.size()==0){ 1575 | return NULL; 1576 | } 1577 | //创建新节点 1578 | Node * root = new Node(); 1579 | //递归创建左右子树 1580 | root -> left = Build(str); 1581 | root -> right = Build(str); 1582 | return root; 1583 | } 1584 | //插入节点也可以采用递归的方式,同样需要给出退出递归的判断条件,通常是判断为空(二叉排序树的建法),插入的话边输入边插 1585 | Node* Insert(Node* root,int x){ 1586 | if (root==NULL){ 1587 | root = new Node(x); 1588 | }else if(xdata){ 1589 | root->left = Insert(root->left,x); 1590 | }else{ 1591 | root->right=Insert(root->right,x); 1592 | } 1593 | return root; 1594 | } 1595 | ``` 1596 | 1597 | ### 遍历 1598 | 1599 | ```c++ 1600 | //前序遍历 1601 | void PreOrder(Node* root){ 1602 | if (root == NULL){ 1603 | return; 1604 | } 1605 | Visit(root->data); //可以是任何的操作,printf之类的 1606 | PreOrder(root->left); 1607 | PreOrder(root->right); 1608 | return; 1609 | } 1610 | //中序遍历 1611 | void InOrder(Node* root){ 1612 | if (root == NULL){ 1613 | return; 1614 | } 1615 | InOrder(root->left); 1616 | Visit(root->data); //可以是任何的操作,printf之类的 1617 | InOrder(root->right); 1618 | return; 1619 | } 1620 | //后序遍历 1621 | void PostOrder(Node* root){ 1622 | if (root == NULL){ 1623 | return; 1624 | } 1625 | PostOrder(root->left); 1626 | PostOrder(root->right); 1627 | Visit(root->data); //可以是任何的操作,printf之类的 1628 | return; 1629 | } 1630 | ``` 1631 | 1632 | # STL 总结 1633 | 1634 | ## vector(变长数组) 1635 | 1636 | ```c++ 1637 | #include 1638 | using namespace std 1639 | vector vi; 1640 | vi.push_back(1); 1641 | vi.pop_back(); 1642 | vi.size(); 1643 | vi.clear(); 1644 | vi.begin(); //vi[0] 1645 | vi.end(); //尾元素地址的下一个地址 1646 | vi.insert(vi.begin()+2,100); 1647 | vi.erase(vi.begin()+3); 1648 | vi.erase(vi.begin()+1,vi.begin()+3); //[first,last) 1649 | for(vector::iterator it = vi.begin(); it!= vi.end(); it++){ 1650 | printf("%d ",*it); //*(it + 2) 1651 | } 1652 | ``` 1653 | 1654 | ## set 1655 | 1656 | ```C++ 1657 | #include 1658 | using namespace std; 1659 | set si; //自动去重并按升序排序 1660 | si.insert(3); 1661 | 1662 | set::iterator it = si.find(2); 1663 | printf("%d\n",*it); //*(si.find(2)); 1664 | 1665 | si.erase(si.find(100)); 1666 | si.erase(100); 1667 | set::iterator it = si.find(30); 1668 | si.erase(it,si.end()); 1669 | 1670 | si.size(); 1671 | si.clear(); 1672 | ``` 1673 | 1674 | ## string 1675 | 1676 | ```c++ 1677 | #include 1678 | using namespace std; 1679 | string str; 1680 | string str = "abcd"; 1681 | str.length(); 1682 | str.size(); 1683 | cin>>str; 1684 | cout< 1710 | using namespace std; 1711 | map mp; // 1712 | for(map::iterator it = mp.begin();it !=mp.end();it++){ 1713 | printf("%c %d\n",it -> first, it -> second); 1714 | } //自动实现从小到大的字典排序功能 1715 | map::iterator it = mp.find('b'); 1716 | mp.erase(it); 1717 | mp.erase('b'); 1718 | mp.erase(it,mp.end()); //删除[it,end)之间的映射 1719 | mp.size(); 1720 | mp.clear(); 1721 | ``` 1722 | 1723 | ## queue 1724 | 1725 | ```c++ 1726 | #include 1727 | using namespace std; 1728 | queue q; 1729 | q.push(i); 1730 | q.front(); 1731 | q.back(); 1732 | q.pop(); //队首元素出队 1733 | q.empty() == true;//判断是否为空 1734 | q.size(); 1735 | ``` 1736 | 1737 | ## priority_queue 1738 | 1739 | ```c++ 1740 | #include 1741 | using namespace std; 1742 | priority_queue q; //队首(堆顶)返回优先级最大的 1743 | priority_queue< int, vector, less > q; //与上面等价,数字越大优先级越大 1744 | priority_queue< int, vector, greater > q; //数字小的优先级大 1745 | q.top(); // 使用之前先用empty()判断优先队列是否为空 1746 | q.push(3); 1747 | q.pop(); 1748 | q.empty(); 1749 | q.size(); 1750 | struct fruit{ 1751 | string name; 1752 | int price; 1753 | friend bool operator < (fruit f1, fruit f2){ 1754 | return f1.price > f2.price; 1755 | } 1756 | }; //水果价格低的为优先级高 1757 | //或者用以下这种形式 1758 | struct fruit{ 1759 | string name; 1760 | int price; 1761 | }f1,f2,f3; 1762 | struct cmp{ 1763 | bool operator()(fruit f1,fruit f2){ 1764 | return f1.price > f2.price; //与上面一致 1765 | } 1766 | }; 1767 | priority_queue, cmp> q; 1768 | //对于字符串或者数组比较重载使用 const 和 & 1769 | friend bool operator < (const fruit &f1, const fruit &f2){ 1770 | return f1.price > f2.price; 1771 | } 1772 | bool operator () (const fruit &f1,const fruit &f2){ 1773 | return f1.price >f2.price; 1774 | } 1775 | ``` 1776 | 1777 | ## stack 1778 | 1779 | ```c++ 1780 | #include 1781 | using namespace std; 1782 | stack st; 1783 | st.push(1); 1784 | st.top(); 1785 | st.pop(); 1786 | st.empty(); 1787 | st.size(); 1788 | ``` 1789 | 1790 | ## pair 1791 | 1792 | 相当于有两个元素的结构体,用于代替二元结构体及其构造函数 1793 | 1794 | ```c++ 1795 | #include //#include 也行 1796 | using namespace std; 1797 | pair p; 1798 | pair p("haha",5); 1799 | //临时创建pair 1800 | pair p; 1801 | p = make_pair("haha",5); 1802 | p = pair ("haha",5); 1803 | p.first = "haha"; 1804 | p.second = 5; 1805 | //比较时候先以first大小为准,相等时比较second大小 1806 | p1 < p2; 1807 | //用途 1808 | map mp; 1809 | mp.insert(make_pair("heihei",5)); 1810 | mp.insert(pair("haha",10)); 1811 | ``` 1812 | 1813 | ## algorithm 1814 | 1815 | ```c++ 1816 | #include 1817 | using namespace std; 1818 | ``` 1819 | 1820 | ```c++ 1821 | max(x,y); 1822 | ``` 1823 | 1824 | ```c++ 1825 | min(x,y); 1826 | ``` 1827 | 1828 | ``` 1829 | abs(x); 1830 | ``` 1831 | 1832 | ```c++ 1833 | int x=1,y=2; 1834 | swap(x,y); 1835 | ``` 1836 | 1837 | ```c++ 1838 | int a[5]={0,1,2,3,4}; 1839 | reverse(a,a+3); //[0,3) 1840 | string str="abcdefghi"; 1841 | reverse(str.begin()+2,str.begin()+6); //[2,6) 1842 | ``` 1843 | 1844 | ```c++ 1845 | int a[10]={1,2,3}; 1846 | do{ 1847 | printf("%d%d%d\n",a[0],a[1],a[2]); 1848 | }while(next_permutation(a,a+3)); 1849 | /*给出一个序列a在全排列中的下一个序列,(a,a+3)就是定义这个序列a 1850 | 123 1851 | 132 1852 | 213 1853 | ... 1854 | 321 1855 | */ 1856 | ``` 1857 | 1858 | ```c++ 1859 | int a[5]={1,2,3,4,5}; 1860 | fill(a,a+5,233); //[0,5)赋值为233 1861 | ``` 1862 | 1863 | ```c++ 1864 | bool cmp(int a, int b){ 1865 | return a > b; //降序输出 1866 | } 1867 | sort(a,a+4,cmp); // cmp是比较函数,默认递增,a是数组 1868 | //vector、string、deque才可以用sort,set、map本身有序不行 1869 | sort(vi.begin(),vi.end(),cmp) 1870 | ``` 1871 | 1872 | ```c++ 1873 | int a[10]={1,2,2,3,3,3,4,4,4,5,5,5}; 1874 | //使用lower_bound()或upper_bound()要求用在有序的数组或容器 1875 | printf("%d, %d\n",lower_bound(a,a+10,3)-a); //-a之后可以直接获取位置索引 1876 | //用来寻找[0,10)范围内第一个值大于或等于3的元素的位置,返回指针或迭代器 1877 | printf("%d, %d\n",upper_bound(a,a+10,3)-a);//-a之后可以直接获取位置索引 1878 | //用来寻找[0,10)范围内第一个值大于3的元素的位置,返回指针或迭代器 1879 | //找不到则返回可以插入该元素的位置指针或迭代器,即元素应在的位置 1880 | ``` 1881 | 1882 | ## STL 对比 1883 | 1884 | | | set | string | map | vector | queue | priority_queue | stack | 1885 | | -------- | ---- | :----: | ---- | --------- | ----- | -------------- | ----- | 1886 | | 尾部添加 | | | | push_back | push | push | push | 1887 | | pop | | | | pop_back | pop | pop | pop | 1888 | | size | √ | √ | √ | √ | √ | √ | √ | 1889 | | clear | √ | √ | √ | √ | | | | 1890 | | begin | √ | √ | √ | √ | front | top | top | 1891 | | end | √ | √ | √ | √ | back | | | 1892 | | insert | √ | √ | √ | √ | | | | 1893 | | erase | √ | √ | √ | √ | | | | 1894 | | empty | √ | √ | √ | √ | √ | √ | √ | 1895 | | find | √ | √ | √ | | | | | 1896 | 1897 | # 参考资料 1898 | [1] 胡凡,曾磊主编.算法笔记[M].北京:机械工业出版社.2016. 1899 | 1900 | [2] 王道论坛组编.计算机考研 机试指南[M].北京:电子工业出版社.2019. 1901 | 1902 | [3] https://www.cnblogs.com/ray-coding-in-rays/p/6498345.html 1903 | 1904 | [4] https://blog.csdn.net/u011676797/java/article/details/45424703 1905 | 1906 | [5] https://blog.csdn.net/mig_davidli/article/details/8591504 1907 | 1908 | [6] https://www.jianshu.com/p/a85fbd6527a7 1909 | 1910 | [7] https://www.cnblogs.com/bestsort/p/10588907.html 1911 | 1912 | [8] https://blog.csdn.net/Mashiro_ylb/java/article/details/78287724 1913 | 1914 | [9] https://blog.csdn.net/qq_33904395/java/article/details/103794896 1915 | --------------------------------------------------------------------------------