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公司英文版译出

Mac上安装Label Studio

以上步骤详细说明了如何在Mac上通过Anaconda安装Label Studio。记得根据你的具体需求选择是否创建新的环境,并使用conda或pip进行安装。安装完成后,务必验证Label Studio是否成功运行。

g2526的博客 1958

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

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

gitblog_00137的博客 562

iOS开发者必备:从越狱设备里提取IPA的三种方法(含Filza、iTools实战)

本文详细介绍了iOS开发者从越狱设备提取IPA文件的三种实用方法,包括使用Filza文件管理器、AFC2配合桌面工具以及命令行高级技术。内容涵盖环境准备、操作步骤和技术原理,帮助开发者高效获取应用包文件,适用于逆向工程、测试分析等场景。

weixin_28717969的博客 812

Competitive Programmer Handbook Python 项目教程

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

gitblog_00435的博客 827

ESP8266红外控制美的空调:从协议解析到Home Assistant集成

红外遥控技术作为一种经典的非接触式控制方式,其原理是通过红外发光二极管发射经过特定频率调制的光信号来传递指令。在物联网和智能家居领域,利用微控制器如ESP8266模拟红外信号,成为将传统家电接入智能生态系统的关键技术。这项技术通过精准解析和生成符合设备厂商私有协议的红外编码,实现了对空调、电视等设备的远程与自动化控制,具有很高的实用价值。本文聚焦于美的空调的红外协议,深入探讨了其脉冲宽度调制(PWM)和全量式编码的特点,并提供了基于ESP8266的完整解决方案,包括硬件连接、库函数API详解以及如何将其无缝

weixin_30718391的博客 401

第二次 PX4仿真环境搭建过程全记录

参考链接 https://blog.youkuaiyun.com/yanwumuxi/article/details/80097294 https://www.ncnynl.com/archives/201709/2013.html 一、安装依赖项 我们使用Debian / Ubuntu LTS 作为Linux的标准支持版本,但是也支持Cent OS 和 Arch Linux的发行版本。 权限设置 警告:永远...

移步换景的博客 2262

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

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

sttony的专栏 845

Mixer-First Receiver适用于宽带阵列应用的接收机技术1

Mixer-First Receiver适用于宽带阵列应用的接收机技术1技术的背景单元级数字化阵列传统链路到Mixer-First链路的逻辑考虑三种场景小结 技术的背景 在文章《吊打全球的顶级毫米波数字阵列项目-MIDAS》中提到了DARPA在MIDAS项目中在毫米波实现宽带单元级数字化阵列用到了一个关键的叫做N-Path filter的技术,而这个技术同样出现在了《让微波射频工程师颤抖的芯片-MATRICs》的MATRICs芯片中。 为什么DARPA的各个合作研究机构选择这种技术?这篇来自Universi

ICONO的博客 2167

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

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

sttony的专栏 941

从DBC到ARXML:用RTA-CAR7实现车载通信协议栈自动化生成全流程

本文详细介绍了如何利用ETAS RTA-CAR7工具链,特别是ISOLAR-A和RTA-BSW ConfGen,实现从车载通信DBC文件到AUTOSAR ARXML系统描述文件,再到基础软件(BSW)协议栈代码的自动化生成全流程。该流程通过深度配置、信号路由、PDU复用及规则定制,大幅提升开发效率与一致性,是应对现代汽车电子复杂网络开发的关键实践。

brandy的博客 220

GVINS数据采集(u-blox fp9 + D435i)

1、配置千寻的RTK账号 采集数据当天购买千寻的RTK账号,然后在Window下,输入账号和密码 (具体的步骤如之前面连接所示),查看u-center软件上的信息,确保接收机可以收到RTK信息; 2、查看Linux系统下u-blox是否可以正常接受GPS信号和RTK数据流 1、GPS信号信息 // 打开端口 1 cd ublox_f9p/src // 或者在终端1下也可以 source devel/setup.bash r.....................

努力努力努力 2609

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

phpMyAdmin | mysqli::real_connect(): (HY000/2002): No such file or directory

第一次安装宝塔mysql服务是默认关闭的,需要手动打开,打开服务再次进入phpMyAdmin发现可以进入了。

吻等离子的博客 2871

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

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

SearchLife的专栏 1050

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

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

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

sttony的专栏 601

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

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

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

SearchLife的专栏 1209

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

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、付费专栏及课程。

余额充值