ANSI/ISO C++ Professional Programmer's Handbook(3)

Competitive Programmer's Handbook 立即下载
  摘自: http://sttony.blogspot.com/search/label/C%2B%2B

3


运算符重载


by Danny Kalev





简介


内建运算符一样可以被扩展以支持用户定义类型。重载运算符预定义的含义是扩展而不是覆盖它。虽然普通函数可以提供相同的功能,但是运算符重载提供了一种统一的表示习惯,这比普通的函数调用语法更清楚。例如



Monday < Tuesday; //重载了<
Greater_than(Monday, Tuesday);

运算符重载的历史可以追述到早期的Fortran语言。Fortran,第一种高级程序设计语言,在50年代中后期以某种方式革命性的提出了运算符重载的概念。第一次,内建运算符如+或-能适用于变量数据类型:整型、实型和复数型。到那时为止,汇编语言——它甚至不支持运算符记号——是程序员的唯一选择。Fortran的运算符重载限于一套固定的内建数据类型;他们不能被程序员扩展。基于对象的程序设计语言提供了用户定义的重载运算符。在这样的语言里,将一套运算符和用户定义的类型联系起来成为可能。面向对象的程序设计语言一般也集成运算符重载。


C++中重新定义内建运算符含义的能力是引起批评的源头。大部分向C++移植的C程序员认为运算符重载和允许程序员增加、取消、改变语言关键字一样危险。虽然可能带来混乱,运算符重载还是成为C++最基本的特性之一,并且泛型程序设计的基本要求(泛型程序设计在第十章“STL和泛型程序设计”讨论)。今天,即使试图不要运算符重载的语言也在加入这种特性。


本章探索运算符重载的好处,也讨论潜在的问题。本章也讨论适用于运算符重载的小规则。最后,演示运算符重载的特殊形式——转换运算符。


运算符重载本质上来说是一个函数,其名字是由关键字operator打头的运算符。例如



class Book
{
private:
long ISBN;
public:
//...
long get_ISBN() const { return ISBN;}
};
bool operator < (const Book& b1, const Book& b2) //重载运算符<
{
return b1.get_ISBN() < b2.get_ISBN();
}

运算符重载的唯一规则


C++对运算符重载只有很少的规则。例如,不禁止程序员重载运算符++来执行一个减少操作(但对于期望运算符++执行增量操作的用户来说,不免感到惊奇)。这样的滥用和双关可以产生几乎不能理解的神秘代码风格。一般的,重载了运算符的源代码是不贴近用户的;因此,以一种意想不到、不直观的方式重载运算符是不推荐的。


另一种极端——完全放弃运算符重载——也是不实际的选择,因为这意味着放弃这种数据抽象和泛型程序设计的重要工具。当你重载运算符以支持用户定义类型时,推荐你保持相应内建运算符的基本语义。换句话说,重载运算符对操作数和有相同接口的内建运算符都有副作用。


成员和非成员


大多数重载运算符既可以申明为非静态的类成员也可以不是成员函数。下面的例子中,运算符==作为非静态的类成员重载:



class Date
{
private:
int day;
int month;
int year;
public:
bool operator == (const Date & d ); // 1:成员函数
};

作为选择的,它也能申明成一个friend函数(选择成员函数还是友函数的标准在这一章讨论):



bool operator ==( const Date & d1, const Date& d2); // 2:非成员函数
class Date
{
private:
int day;
int month;
int year;
public:
friend bool operator ==( const Date & d1, const Date& d2);
};

虽然如此,运算符[],(),=,和->只能被申明为非静态类成员;这是为了确保他们的第一个操作数是左值。


运算符的接口


当你重载运算符时,应该使其与它的内建副本有相同的接口。运算符的接口由用于的操作数组成,不管这些操作数是能被运算符改变的还是返回的结果。例如,考虑运算符==。它的内建版本可以接受广泛的类型,包括int,bool,float,char以及指针。检测运算符==的操作数是否相等的基础计算过程是它的实现细节。然而,内建==运算符检测左右操作数的一致性和返回一个bool值作为结果的过程可以被一般化。注意运算符==没有改变它的任何一个操作数是十分重要的;另外,在运算符==中操作数的顺序是无关紧要的。重载运算符==也必须遵循这些行为。


运算符的一致性


运算符==是二元和对称的。==的重载版本也应该遵循这种限制。它接受两个有相同类型的参数。实际上,常使用运算符==测试两个有截然不同基础类型的操作数的一致性,例如char和int。但是,在这种情况下C++自动的对操作数应用integral promotion(整数提升);结果,在比较之前表面上不同的类型被提升成了相同的类型。对称性限制意味着运算==定义成友函数而不是成员函数。为了使你看清为什么,这儿有对两种版本重载运算符 ==的比较:



class Date
{
private:
int day;
int month;
int year;
public:
Date();
bool operator == (const Date & d) const; // 1 不对称的
friend bool operator ==(const Date& d1, const Date& d2); //2 对称的
};
bool operator ==(const Date& d1, const Date& d2);

(1)中将重载运算符==申明为成员函数与内建运算符==不一致,因为它接受两个不同类型的实参。编译器将成员运算符==翻译成下列形式:



bool Date::operator == (const Date *const, const Date&) const;

第一个参数是指向const对象的const this指针(记住,this总是一const指针;当成员函数也是const时它指向一个const对象)。第二个参数是const Date的引用。很明显,两种类型之间没有标准的类型转换存在。另一方面,友函数 版本接受两个同类型的参数。支持友函数版本有实际的意义。STL算法依赖对称版本的重载运算符 ==。例如,存储不支持对称版本运算符==对象的容器不能排序。


另一个例子是内建运算符+=,其也接受两个参数,并改变左参数而不改变右参数。重载+=的接口需要反映它改变它的一个对象而保持另一个对象的事实。这通过申明函数为const来反映,函数自己是非const成员函数。例如



class Date
{
private:
int day;
int month;
int year;
public:
Date();
//内建+=改变它的左操作数而不改变右操作数
//同样的行为在这里保持
Date & operator += (const Date & d);
};

总的来说,每一个重载运算符都必须实现与它的内建运算符相同的接口。如何实现在定义基础操作和隐藏细节方面是自由的——只要遵循其接口。


运算符重载的限制


就象前面提到的,重载运算符是一个函数,这个函数申明时以关键字operator开头并紧接一个运算符id。运算符可以是下列之一:



new delete new[] delete[]
+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
() []

另外。下列运算符的一元和二元形式都能被重载:



+ - * &

重载运算符以同样的方式从基类继承。注意。这个规则不适用类隐含申明的而不是用户申明的赋值运算符。 因此,基类赋值运算符总是被派生类的拷贝赋值运算符隐藏。(赋值运算符在第四章“特殊成员函数:默认构造器,拷贝构造器,销毁器和分配运算符特殊成员函数:默认构造器,拷贝构造器,销毁器和赋值运算符”讨论)


重载运算符只能用于用户自定义类型


重载运算符必须至少接受一个用户自定义类型(运算符new和delete是例外——详细参见第十一章“内存管理”)。这条规则保证只包含基本类型的表达式的含义不能被用户改变。例如



int i,j,k;
k = i + j; //总是使用内建的=和+


创造新的运算符是不允许的


重载运算符扩展内建的运算符,所以你不能引入新的运算符到语言(转换运算符不同于普通的运算符)。下面的例子试图重载@运算符,但是因为上面的原因不能编译:



void operator @ (int); //非法,@不是内建运算符或类型名

优先级和参数个数


运算符的优先级和参数的个数都不能改变。例如,重载&&只能有两个参数——和内建运算符&&一样。另外,当运算符重载时它的优先级也不能改变。两个或多个重载运算符的序列,例如t2<t1/t2,按内建运算符的优先级规则求解。因为除法运算符的级别高于小于号,表达式总是被解释成t2<(t1/t2)。


默认参数


不象普通函数。重载运算符不能申明一个有默认值的参数(运算符()是这条规则的例外;稍后讨论)。



class Date
{
private:
int day;
int month;
int year;
public:
Date & operator += (const Date & d = Date() ); //error,默认参数
};

这条规则可能很武断,但这来源于内建运算符的行为,他们从没有操作数。


不能被重载的运算符


有一些运算符不能被重载。 因为他们接受一个名字而不是一个对象作为右值。这些运算符是:



  • 直接成员访问,运算符.



  • 指向类指针成员,.*




  • 范围决定,运算符::


  • 求占内存大小,运算符sizeof


条件运算符?:也不能被重载。


另外,新的类型转换运算符——static_cast<>,dynamic_cast<>,reinterpret_cast<>,和const_cast<>——和#、##预处理记号不能重载。


转换运算符


发现C++代码和C代码一起使用是常见的。例如,以前用C语言写的系统可以通过面向对象的接口重新包装。这种用两种语言构造的系统常常需要同时支持双重的接口——一种适应面向对象环境,另一种满足C的环境。实现特殊数字实体——比如复数和二进制数——和不足一字节数据的类也倾向于使用转换运算符来使自己和基本类型平滑的交互。


字符串是一个需要双接口的典型例子。字符串对象可能不得不用在仅仅支持以NULL结尾char数组 的上下文中。例如



class Mystring
{
private:
char *s;
int size;
public:
Mystring(const char *);
Mystring();
//...
};
#include <cstring>
#include "Mystring.h"
using namespace std;
int main()
{
Mystring str("hello world");
int n = strcmp(str, "Hello"); //编译期错误:
//str不是const char *类型
return 0;
}

C++为这种情况提供一种自动的类型转换。转换运算符可以被认为是用户自定义的类型转换运算符;在需要特定类型的上下文中将这种对象转换成不同的类型。转换是自动的。例如



class Mystring //现在有转换运算符
{
private:
char *s;
int size;
public:
Mystring();
operator const char * () {return s; } //将Mystring转换成C的字符串
//...
};
int n = strcmp(str, "Hello"); //OK,自动将str转换成const char *

转换运算符与普通的重载运算符相比有两点不同。第一,转换运算符不需要返回值(void也不需要)。返回值由运算符的名字推出。


第二,转换运算符不需要参数。


转换运算符可以将对象转换成任何类型,基本类型和用户定义类型都行:



struct DateRep //以前的C代码
{
char day;
char month;
short year;
};
class Date //面向对象包装
{
private:
DateRep dr;
public:
operator DateRep () const { return dr;} //自动转换成DateRep
};
extern "C" int transmit_date(DateRep); //基于C的通讯API
int main()
{
Date d;
//...使用d
//将数据对象作为二进制流传送到遥远的客户方
int ret_stat = transmit_date; //使用以前的通讯API
return 0;
}

标准转换VS.用户定义转换


用户定义转换和标准转换的交互能带来不受欢迎的惊奇和副作用,因此必须谨慎使用。测试下面具体的例子。


非显式的只带一个参数的构造器也是一个转换运算符,它将参数转换成这个类的对象。但编译器必须解决一个重载函数调用时,它除了考虑标准的以外还要考虑象这样用户定义的。例如



class Numeric
{
private:
float f;
public:
Numeric(float ff): f(ff) {} //构造器也是一个float到Numeric的
//转换运算符
};
void f(Numeric);
Numeric num(0.05);
f(5.f); //OK,调用void f(Numeric)。Numeric的构造器将
//参数转换成Numeric对象

假设你稍后又增加了一个重载版本的f():



void f (double);

现在相同的函数调用有不同的解决:



f(5.f); //现在调用f(double)而不是f(Numeric)

这是因为float被自动提升为double,以匹配一个函数特征。这是一个标准类型转换。另一方面,float到Numeric的转换是一个用户定义转换。用户定义转换级别低于标准转换——在重载函数确定中;结果函数调用解决不同。


因为这些现象,转换运算符收到强烈的批评。一些程序设计课禁止使用他们。但是,转换运算符对在双接口的程序中是——有时不可避免——一个有用的工具,就象你看到的。


后缀和前缀运算符


对于早期的类型,C++区别++x;和x++;,也区别--x;和x--;。在这些情况下,对象也必须区别前缀和后缀重载运算符(例如,作为优化的度量。参见第十二章“优化你的代码”)。后缀运算符申明了一个虚假的int参数,而他们的前缀版本没有。例如



class Date
{
public:
Date& operator++(); //前缀
Date& operator--(); //前缀
Date& operator++(int unused); //后缀
Date& operator--(int unused); //后缀
};
void f()
{
Date d, d1;
d1 = ++d;//前缀:先增加d然后赋值给d1
d1 = d++; //后缀:先赋值然后增加d
}

使用函数调用语法


重载运算符的调用只是普通函数调用的一种变形。你可以使用显式的函数调用来调用重载运算符,例如:



bool operator==(const Date& d1, const Date& d2);
void f()
{
Date d, d1;
bool equal;
d1.operator++(0); //等价于:d1++;
d1.operator++(); //等价于:++d1;
equal = operator==(d, d1);//等价于:d==d1;
Date&(Date::*pmf) (); //指向成员函数的指针
pmf = & Date::operator++;
}

和谐的运算符重载


无论什么时候你重载了象+或-的运算符,你也必须支持相应的+=和-=运算符。编译器不会自动的替你做。考虑下面的例子:



class Date
{
public:
Date& operator + (const Date& d); //注意:运算符+=未定义
};
Date d1, d2;
d1 = d1 + d2; //正确;使用重载的+和默认的赋值运算符
d1 += d2; //编译期错误:“没有用户为类Date定义运算符+=”

理论上讲,编译器能通过合并赋值运算符和重载运算符+来合成运算符+=,这样表达式d1 += d2;被自动的扩展成d1 = d1+d2。但是,这是不受欢迎的,因为自动扩展比一个用户自定义版本低效。自动扩展版本建立了一个临时对象,而用户定义版本避免了这一点。而且,不能想象,一个有重载运算符+的类故意没有运算符+=。


通过值返回对象


为了效率,大型对象常常通过引用或指针来传递给函数——或从函数返回。但是,仍然有少数情况最好的选择是通过值返回对象。运算符+是这种情况的例子。它必须返回一个对象,但不能改变它的任何一个操作数。表面上自然的选择是,分配一个对象返回它的地址。然而,这不是一个好主意。动态内存分配比本地存储要慢的多。动态分配也可能失败并抛出异常,异常再被处理程序截获。更糟的是,这个解决方案是一种错误倾向,因为谁删除这个对象成了不清楚的问题——创建者还是用户?


另一个解决方法是使用静态对象并返回引用。例如



class Year
{
private:
int year;
public:
Year(int y = 0) : year(y) {}
Year& operator + (const Year& other) const; //返回本地静态Year对象的
//引用
int getYear() const;
void setYear(int y);
};
Year& Year::operator + (const Year& other) const
{
static Year result;
result = Year(this->getYear() + other.getYear() );
return result;
}

静态对象解决了所有权问题,但它仍是有问题的:在重载运算符的每一个调用中,静态对象相同的实例被修改并返回给调用者。相同的对象能通过引用返回给许多用户。这些用户不知道他们保存的是一个共享的实例,这个实例在他们使用之后就被修改了。


最后,最安全和最有效的的解决方法仍然是通过值返回:



class Year
{
private:
int year;
public:
Year(int y = 0) : year(y) {}
Year operator + (const Year& other) const; //通过值返回Year对象
int getYear() const;
void setYear(int y);
};
Year Year::operator + (const Year& other) const
{
return Year(this->getYear() + other.getYear() );
}

多重重载


重载运算符服从函数重载的规则。因此,运算符 可以被重载多次。什么时候有用呢?考虑下面的Month类和它关联的运算符 ==:



class Month
{
private:
int m;
public:
Month(int m = 0);
};
bool operator == (const Month& m1, const Month &m2);

由于隐含int到Month转换运算符,可以用重载运算符==来比较简单的 int值和Month对象。例如



void f()
{
int n = 7;
Month June(6);
bool same =
(June == n); //calls bool operator == (const Month& m1, const Month &m2);
}

这工作的很好,但这是低效的:参数n先被转换成一个临时的Month对象,然后于 June对象比较。你可以通过定义运算符==的另外的重载版本来避免临时对象的构造:



bool operator == (int m, const Month& month);
bool operator == (const Month& month, int m);

因此,表达式June == n将调用下面的重载运算符:



bool operator == (const Month& month, int m);

这个重载版本不会创造临时对象,所以它是更有效的。同样是关于性能的考虑导致C++标准化委员会为std::string和其他的标准库中的类定义了三种不同版本的运算符==(参见第十章“STL和泛型程序设计”)。


用户定义类型的重载运算符


你也可以为enum类型定义重载运算符。例如,象++和--这样的运算符是很有用的,因此他们可以通过给定enum类型的枚举值来重复。你可以这样做:



#include <iostream>
using namespace std;
enum Days
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
Days& operator++(Days& d, int) //后缀++
{
if (d == Sunday)
return d = Monday; //转回去
int temp = d; //转换成一个int
return d = static_cast<Days> (++temp);
}
int main()
{
Days day = Monday;
for (;;) //将days作为整数显示
{
cout<< day <<endl;
day++;
if (day == Sunday)
break;
}
return 0;
}

如果你想以符号而不是整数来显示枚举值,重载运算符<<也可以:



ostream& operator<<(ostream& os, Days d) //以符号形式显示Days
{
switch
{
case Monday:
return os<<"Monday";
case Tuesday:
return os<<"Tuesday";
case Wednesday:
return os<<"Wednesday";
case Thursday:
return os<<"Thursady";
case Friday:
return os<<"Friday";
case Saturday:
return os<<"Satrurday";
case Sunday:
return os<<"Sunday";
default:
return os<<"Unknown";
}
}

重载下标运算符


对于包含数组元素的多样的类,可以通过重载下标运算符来方便的访问单一元素。记住,永远定义两个版本的下标运算符:const版本和非const版本。例如



class Ptr_collection
{
private :
void **ptr_array;
int elements;
public:
Ptr_collection() {}
//...
void * operator [] (int index) { return ptr_array[index];}
const void * operator [] (int index) const { return ptr_array[index];}
};
void f(const Ptr_collection & pointers)
{
const void *p = pointers[0]; //调用const版本的运算符[]
if ( p == 0)
return;
else
{
//...使用p
}
}

函数对象


函数对象是作为一个包含函数调用运算符重载版本的类来实现的。这样一个类的实例可以象一个函数一样使用。普通函数可以有任意数目的参数;因此,运算符()在其他运算符中是一个例外。因为它可以接受任意数目的参数。另外,它可以接受默认参数。在下面的例子里,一个函数对象实现了一个泛型的增量函数:



#include <iostream>
using namespace std;
class increment
{
//一个泛型的增量函数
public : template < class T > T operator() (T t) const { return ++t;}
};
void f(int n, const increment& incr)
{
cout << incr(n); //输出1
}
int main()
{
int i = 0;
increment incr;
f(i, incr);
return 0;
}

总结


运算符重载的概念并非新也不是C++独有。它是实现数据抽象和混合类的最基本的工具。在许多方面,C++中重载运算符象普通函数:他们被继承,他们可以被重载多次,他们即可申明为非静态成员也可申明为非成员函数。但是,有一些限制适用与重载运算符。重载运算符有确定的参数数目,而且不能有默认参数。另外,运算符的一致性和优先级不能被改变。内建运算符有一个由运算符适用的操作数和运算符返回的结果组成的接口。当你重载运算符时,建议你保持和内建运算符相同的接口。


转换运算符是一种特殊类型的重载运算符。他们与普通重载运算符相比在两个地方需要关注:他们不返回值,也不需要任何参数。


Competitive Programmer’s Handbook Competitive Programmer’s Handbook The purpose of this book is to give you a thorough introduction to competitive programming. It is assumed that you already know the basics of programming,but previous background on competitive programming is not needed 立即下载

相关推荐

Competitive Programmer&#39;s Handbook Antti Laaksonen

演算法的handbookPreface ix I Basic techniques 1 1 Introduction 3 1.1 Programming languages . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.2 Input and output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 1.3 Working with numbers . . . . . . . . . . . . . . . . . . . . . . . . . . 6 1.4 Shortening code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 1.5 Mathematics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 1.6 Contests and resources . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2 Time complexity 17 2.1 Calculation rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 2.2 Complexity classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 2.3 Estimating efficiency . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 2.4 Maximum subarray sum . . . . . . . . . . . . . . . . . . . . . . . . . 21 3 Sorting 25 3.1 Sorting theory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.2 Sorting in C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.3 Binary search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 4 Data structures 35 4.1 Dynamic arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 4.2 Set structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 4.3 Map structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 4.4 Iterators and ranges . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 4.5 Other structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 4.6 Comparison to sorting . . . . . . . . . . . . . . . . . . . . . . . . . . . 44 5 Complete search 47 5.1 Generating subsets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 5.2 Generating permutations . . . . . . . . . . . . . . . . . . . . . . . . . 49 5.3 Backtracking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 5.4 Pruning the search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 5.5 Meet in the middle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 6 Greedy algorithms 57 6.1 Coin problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 6.2 Scheduling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 6.3 Tasks and deadlines . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 6.4 Minimizing sums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 6.5 Data compression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 7 Dynamic programming 65 7.1 Coin problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 7.2 Longest increasing subsequence . . . . . . . . . . . . . . . . . . . . . 70 7.3 Paths in a grid . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 7.4 Knapsack problems . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 7.5 Edit distance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 7.6 Counting tilings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 8 Amortized analysis 77 8.1 Two pointers method . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 8.2 Nearest smaller elements . . . . . . . . . . . . . . . . . . . . . . . . . 79 8.3 Sliding window minimum . . . . . . . . . . . . . . . . . . . . . . . . . 81 9 Range queries 83 9.1 Static array queries . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 9.2 Binary indexed tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 9.3 Segment tree . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 9.4 Additional techniques . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 10 Bit manipulation 95 10.1 Bit representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 10.2 Bit operations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 10.3 Representing sets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 10.4 Bit optimizations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 10.5 Dynamic programming . . . . . . . . . . . . . . . . . . . . . . . . . . 102 II Graph algorithms 107 11 Basics of graphs 109 11.1 Graph terminology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 11.2 Graph representation . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 12 Graph traversal 117 12.1 Depth-first search . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 12.2 Breadth-first search . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 12.3 Applications . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121

我认为的好书

算法 TAOCP 计算机程序设计艺术。the art of computer programming(神书,无法看) 挑战程序设计竞赛 CLRS 算法导论(最权威的) 算法与数据结构 C语言描述(经典) 算法 java 算法设计与分析基础 levitin how to think about algorithms 算法的证明 Competitive programmer’s handbook acm的基础知识 the algorithm design manual 算法设计手册 algorithms j

惜兰blog 600

程序员的刷题软件-competitive-programmer-handbook-python:所有竞争程序员手册算法的Python实现

程序员的刷题软件竞技程序员手册 Python Python 3 实现 现有代码将转换为 python 3。 所有竞争性程序员手册的 Python 代码 试图用 Python 重写 Competitive Programmer Handbook 中编写的代码 图书链接: 所有学分归Antti Laaksonen一书的原作者所有。 MIT License ,随意使用实现

开源项目推荐:Competitive Programmer Handbook Python Implementation

开源项目推荐:Competitive Programmer Handbook Python Implementation 1. 项目基础介绍及编程语言 本项目是 Competitive Programmer Handbook 的 Python 实现。Competitive Programmer Handbook 是一本面向竞赛程序设计的算法手册,由 Antti Laaksonen 编写。本项目旨在...

gitblog_00187的博客 911

《Visual C++ Handbook Visual C++手册》作者: Chris H.Pappas Willian H.Murray 出版年: 1995年

简介:并列题名:Visual C++ handbook:据McGraw-Hill公司英文版译出

SAP FICO 资产会计AA后台配置 (上)

输入折旧表“CZ11”如有外币计价的资产,则需指定外币计价的资产折旧范围,以及规则。因为在设置资产类型时,需指定一个对应的 资产编号范围、资产账户确定类型(便于分配自动会计科目)、屏幕格式规则(便于设置资产字段必填与否)。因为在设置资产类型时,需指定一个对应的 资产编号范围、资产账户确定类型(便于分配自动会计科目)、屏幕格式规则(便于设置资产字段必填与否)。新增资产类型:Z101 固定资产-房屋及建筑物新增资产类型:Z301 固定资产-房屋及建筑物。

ghgxm520的博客 1762

竞争程序员手册Python实现项目常见问题解决方案

竞争程序员手册Python实现项目常见问题解决方案 项目基础介绍 该项目名为“competitive-programmer-handbook-python”,是一个开源项目,旨在将《Competitive Programmer's Handbook》中的所有算法实现为Python代码。该项目的目标是为那些希望在编程竞赛中使用Python的开发者提供一个参考实现。项目的主要编程语言是Python。 ...

gitblog_00137的博客 562

STM32N6570-DK ISP调试

将下载线接入STLINK V3EC接口,将BOOT1拨码拨至右边,即将其置为H,上电或已经上电的按复位,打开STM32CubeProgrammer,设置start address:0x70000000。板子消耗电流较大,即使缩短数据线接了USB供电依然不能正常点亮显示屏,最终通过板上CN2端子加了外部5V供电,仍然需要560mA的外部电流。3.烧录bin文件:STM32N6_ISP_IQTune_App_revC01-v1.1.0-trusted.bin。

liu的博客 1064

Competitive Programmer Handbook Python 项目教程

Competitive Programmer Handbook Python 项目教程 1、项目介绍 Competitive Programmer Handbook Python 是一个开源项目,旨在将《Competitive Programmer's Handbook》中的算法和数据结构实现转换为Python 3代码。该项目由Abhishek Gahlot发起,并获得了广泛的关注和贡献。 该项目...

gitblog_00435的博客 827

eNSP网络构建—建立小型局域网

企业私有网络构建运维 实战案例——使用模拟器构建局域网络 案例分析 1.架构分析 (1)需求分析 本实验的目的在于建立小型局域网。由于公司由不同部门组成,并分布在不同地点,因此需要划分不同网络实现互联互通。设计以下网络:两个部门各使用一台交换机连接,然后连接到总交换机,再通过路由器与外网以及其他部门网络相连。为了控制网络上的广播风暴,增加网络的安全性,在交换机上需要设置VLAN,在路由器与交换机之...

_ 4万+

SWAT模型非点源模拟原理

目录引言模型概述非点源污染模拟原理物质迁移内容过程营养物农药病原菌运移河道水质模型营养元素的地表水体循环河道水质模拟藻类模拟氮元素磷元素生化需氧量及溶解氧重金属的运移蓄水体水质模拟 引言 今天无意接触到这一块,就记录下来了。 模型概述 1、流域中非点源污染的来源主要是农业化肥和农药的施用。 2、氮、磷等营养物和农药的迁移和转化取决于化合物在土壤环境中经历的转化过程。 3、SWAT模型可以模拟水文相应单元内氮、磷完整的营养物循环过程和任何一种农药的降解过程。 4、SWAT模拟流域内泥沙、藻类、溶解氧、有机污染

DoWayChan的博客 4877

ANSI/ISO C++ Professional Programmer's Handbook 3

ANSI/ISO C++ Professional Programmers Handbook 3 运算符重载by Danny Kalev 简介 运算符重载的唯一规则 成员和非成员 运算符的接口 运算符的一致性

sttony的专栏 845

一次点击跑通:用 Postman 自动获取并携带 CSRF token,测试 SAP OData 写操作

SAP OData 写操作自动化测试方案 针对 SAP Gateway OData 服务写操作中常见的 CSRF token 验证问题,本文提出了一套基于 Postman 的自动化解决方案: 问题分析:解释了 CSRF token 的验证机制及其必要性,说明手工测试效率低下的原因 解决方案: 使用 Postman 环境变量存储 token 通过脚本自动获取并传递 token 利用 Collection Runner 实现一键执行 实施步骤: 创建环境变量存储 token 第一个请求获取 token 并存入变

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 303

ANSI/ISO C++ Professional Programmer's Handbook 14

ANSI/ISO C++ Professional Programmers Handbook 14总结和未来的方向by Danny Kalev 一些差一点被加入到标准的特性 Hashed联合容器 函数模板的默认类型参数 C++演

sttony的专栏 941

dify进阶:使用 FastAPI 的 StreamingResponse 实现高效的数据流传输

在 FastAPI 框架中,是一个非常有用的工具,它允许你以流的方式发送数据给客户端,而不是一次性加载所有内容到内存中再发送。这对于处理大型文件下载、视频流或者任何需要长时间处理的任务特别有用。

xiezhipu的博客 1433

STM32CubeMX之emWin移植

“前面两章介绍了SDRAM和LTDC的使用,本篇文章将介绍emWin移植到STM32。”硬件环境:STM32F429IGT6软件环境:STM32CubeMX v5.5.0...

zhang062061的专栏 2826

ANSI/ISO C++ Professional Programmer's Handbook 2

ANSI/ISO C++ Professional Programmers Handbook 2标准简报:ANSI/ISO C++的最新附加部分by Danny Kalev 简介 理解 ANSI/ISO标准 本章的目的和结构 标准的术语

sttony的专栏 1234

180.在 Vue3 中使用 OpenLayers 实现多个多边形的绘制、编辑、删除与清空

【摘要】本文介绍如何在Vue3+TypeScript环境下使用OpenLayers实现多边形地图交互功能。通过Composition API封装核心逻辑,实现了多边形绘制、编辑、删除及清空等操作。关键技术包括:使用VectorLayer管理图形数据,Draw/Modify/Select交互控制,以及通过ref()管理交互状态。文中提供了完整代码示例,并解决了交互对象叠加问题,为开发GIS应用提供了基础模块。该方案支持扩展为数据库存储、后端交互等高级功能。

小彭的博客 769

ANSI/ISO C++ Professional Programmer's Handbook(9)

 摘自:http://sttony.blogspot.com/search/label/C%2B%2B9Templatesby Danny Kalev简介 类模板 类模板的申明 实例化和特殊化 模板形参 默认类型参数 静态数据成员 友元 部分特殊化 类模板的显式特殊化 类模板函数的特殊化 函数模板 带参数的宏 void

SearchLife的专栏 1050

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

ANSI/ISO C++ Professional Programmer's Handbook 4

ANSI/ISO C++ Professional Programmers Handbook 4 特殊成员函数:默认构造器,拷贝构造器,销毁器和赋值运算符by Danny Kale 简介 构造器 从类的构造器调用对象的成员函数 不需要的构造器(Trivial

sttony的专栏 601

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

ANSI/ISO C++ Professional Programmer's Handbook(12)

  摘自:http://sttony.blogspot.com/search/label/C%2B%2B12优化你的代码by Danny Kalev 简介 本章的范围 在优化你的软件之前 申明的布局 初始化比赋值好 重新放置申明 成员初始化列表 前缀运算符VS后缀运算符 内联函数 函数调用的开销 内联函数的好处 被申明为内

SearchLife的专栏 1209

ANSI/ISO C++ Professional Programmer's Handbook

[在线阅读 或 下载] ANSI/ISO C++ Professional Programmers Handbook 文件格式: PDF/Adobe Acrobat  

Mobidogs Blog 1120

ANSI/ISO C++ Professional Programmer's Handbook(14)

摘自:http://sttony.blogspot.com/search/label/C%2B%2B14总结和未来的方向by Danny Kalev一些差一点被加入到标准的特性Hashed联合容器函数模板的默认类型参数C++演化过程与其他语言的比较用户的利益"Pay as You Go"将来可能加入C++的特性自动垃圾收集器对象永久化

SearchLife的专栏 1352

ANSI/ISO C++ Professional Programmer's Handbook 13

ANSI/ISO C++ Professional Programmers Handbook 13与C语言的兼容性问题by Danny Kalev 简介 ISO C 和ANSI/ISO C++的C子集之间的区别 函数形参列表 函数申明 空的形参列

sttony的专栏 1259

ANSI/ISO C++ Professional Programmer's Handbook 10

ANSI/ISO C++ Professional Programmers Handbook 10STL和泛型程序设计by Danny Kalev 简介 泛型程序设计 STL头文件的组织 容器 算法 迭代器

sttony的专栏 1218
上一篇: ANSI/ISO C++ Professional Programmer's Handbook(4)
下一篇: ANSI/ISO C++ Professional Programmer's Handbook(2)
SearchLife
博客等级 码龄20年 51粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值