C++ 复合类型

C++11使得大括号初始化成为通用初始化,可用于所有类型,甚至可以省略等号的赋值。

cin和scanf一样,对字符串不会录入空格,在istream中的类cin提供了一些面向行的类成员函数:getline()和get(),这两个函数都读取一行输入,直到到达换行符,然而,getline将舍弃换行符,而get将换行符保留在输入序列中。

getline输入两个参数,第一个参数是存储输入行的数组名称,第二个参数是要读取的字符数,

cin.getline(name,20);

其实getline还有第3个参数,只不过第4章木有讲~

对于get()来说,一定要注意的是get并不在读取并丢弃换行符,而是将其留在输入队列中,如果连续两次调用get()

cin.get(name,ArSize);
cin.get(dessert,ArSize);

因为当第一行录入成功之后,因为没有把换行符录入进去,而运行到下一行,

但是换行符留在了输入缓冲中,第二行没有获得任何东西便读到了换行符,也就是dessert没有得到任何东西,这样,如果不借助帮助,这条语句就无法被跳过。

所幸,因为函数重载,get还有另外一种使用方法:

cin.get(name,ArSize);
cin.get();
cin.get(dessert,ArSize);

第二行,这样使用可以吃掉一个任意字符,包括换行符。

其实还有一种炫酷的使用方法,

cin.get(name,ArSize).get();

因为get会返回cin,便可以继续“.”下去~~

不光get,getline也可以。炫酷技能get it (⊙v⊙)。

《C++ PP》推荐使用get而不是getline,原因有二:一是getline在老版本中并没有,也就是说老版本编译器支持性不好,二是如果一行文字录入完成之后,怎么判断是因为录入到换行符才结束的还是因为超出固定长度而结束的?用get只需要判断输入缓冲中最前面那个字符是不是换行符即可。

失效位问题,像封装好的类,里面总有各种各样的标记,而istream中有个失效位标记,标记着是否还能被录入进去数据。

get在读取空行的时候,便会设置失效位,导致后面所有的输入语句失效,

当输入的语句比设置长度要长,get和getline都会只录入规定长度的内容,其他的保留在缓冲区中,但不同的是getline还会设置失效位,关闭后续的输入,在一定长度上讲,起到了保护后续数据的作用。

其实非法输入,EOF等都可以引起失效,失效的解决方法就是清除函数

cin.clear();

其实这样讲,我认为getline也可以判断到底是因为输入过长还是确实是行结束,判断失效位就可以了。

换行符问题,如果是这样的代码:

cin >> year;
cin.getline(address,80);

第二行会被录入什么东西呢?其实只会被录入一个换行符,因为录入year之后,换行符留在了缓冲区,最后被address吃掉了。

和之前敲代码的时候在scanf中加%*c的原因相同,这个也要相似处理,当然,又是一个高大上的语句

(cin >> year).get();

灵活的语句,十分炫酷。

因为string类不需要规定长度,因此有个不是cin成员函数的getline,

string str;
getline(cin,str);

他将cin作为参数而不是以cin的成员函数使用,其实就是因为string出现的晚,cin没有支持string 的成员函数,便加声明了getline以支持string。

面向对象编程与传统的过程性编程的区别在于OOP强调的是在运行阶段而不是编译阶段,最好的诠释就是动态分配,用指针等,在运行过程中决定使用多大的空间而不是在编译的时候决定,更加灵活。

C++中添加了vector作为数组的代替品,vector总的来说属于动态数组,功能要比数组强大,但付出的代价是效率低下。

但在C++11标准中,增加了array类,他的长度和数组一样是固定的,可以认为是定长数组,但是存储在栈(静态内存分配)而不是自由存储区,因此和数组一样快,并且同样强大。

2014/10/08 00:59 am posted in  C++

C++ 数据处理与规范

面向对象(OOP)的本质是设计并拓展自己的数据类型C++的命名规则,与C语言一样,但一般以两个下划线或下划线和大写字母打头的名称被保留给实现(编译器及其使用的资源)使用。以一个下划线开头的名称被保留给实现,用作全局标识符。

在C++所有主观风格中,一致性和精度是最重要的。

头文件climits(见下表)定义了符号常量,来表示类型的限制

define编译指令是C语言遗留下来的,C++有一种更好的创建符号常量的方法(使用关键字const)

C++11更广泛的支持大括号初始化器。对于变量初始化可以:

int emus{7};
int rheas = {12};

其次,大括号内可以不包含任何东西,在这种情况下,变量将被初始化为零。

以前,C++使用不同的方式来初始化不同的类型,初始化不同类变量的方式不于初始化常规结构的方式,而初始化常规的方式不同于初始化简单变量的方式,通过使用C++新增的大括号初始化器,初始化常规变量的方式与初始化类变量的方式更像。C++11使得将大括号初始化器用于任何类型(可以使用等号,也可以不使用),这是一种通用的初始化语法。

int被设置为对目标计算机而言最“自然”的长度。自然长度止的是计算机处理起来效率最高的长度。如果没有非常有说服力的理由来选择其他类型,则应使用int。

仅当有大整数数组时,才有必要使用short,尽管有时其长度和int是一样的,但要将程序从16位操作系统移动到32位操作系统,则用于存储int数组的内存量将加倍,而short数组不受影响,记住,节省一点就是赢一点

如果要让cout输出十进制、十六进制和八进制,可以使用控制符dec,hex和oct,用来修改格式,使用方式,如:

cout

C++是如何确定常量的类型呢?如cout但由于l和1很像,一般使用大写L。

char可用作字符和小整数

cout.put()函数用来打印一个字符(C++的历史遗留的原因)

基于字符字符的八进制和十六进制编码来使用转义序列,如ctrl+Z的ASCII码为26,对应的八进制为032,十六进制为0x1a,可以用转义序列来便是该字符如�32,x1a。

C++有一种表示特殊字符的机制,它独立于任何特定的键盘,使用的是通用字符名。

使用通用字符名类似于转义序列,通用字符名可以用u或U打头,u后是8个十六进制位,U后面是16个十六进制位。

与int不同的是,char在默认情况下既不是没有符号的,也不是有符号的,是否有符号由C++决定(这也就是用char和wchar_t表示中文时,正数负数均表示“你好”,见下文wchar_t部分)。

如果用char来存储数值,则unsigned char和signed char的差别将非常重要,前者可以表示0~255,后者为-128~127。

如果程序要处理的字符集无法用一个8位的字节表示,如日文汉字系统。对于这种情况,C++的处理方式有两种,首先如果大型字符是实现的基本的字符集,则编译器厂商可以将char定义为一个16位的字节或则更长的字节,其次,使用一个较大的拓展字符集wchar_t(宽字符类型)

wchar_t是一种整数类型,它有足够的空间,可以表示系统使用的最大拓展字符集。

但cin和cout将输入输出看做char流,因此,适用于处理wchar_t类型,iostream头文件的最新版本提供了作用相似的工具——wcin和wcout,可用于处理wchar_t流,另外,还可以单纯加上前缀L来指示宽字符常量和宽字符串。

在进行国际编程或使用Unicode或ISO10646时应注意使用宽字符类型。

在进行字符串编码是,如果有特定长度和符号特征的类型,将很有帮助,因此,C++11使用前缀u(这回注意大小写)来表示char16_t字符常量和字符串常量,用前缀****U来表示char32_t常量。

和wchar_t一样,chat16_t和chat32_t也都有底层类型也都有底层类型(一种内置的整型),但底层类型可能随系统不同而不不同。

const常量比define常量要好些,首先,前者可以明确常量类型,其次,可以使用C++的作用域规则将定义限制在特定范围的函数或者文件中,第三,const可被用于更复杂的数据类型。

C++有3中浮点类型:float、double、long double,float至少32位,double至少48位且不小于float,long double至少和double一样长,通常float为32位,double为64位,long double为80、96、128位(区分在系统),它们的存储范围在cfloat(见下表)头文件中找到。

name value stands for expresses
FLT_RADIX 2 or greater RADIX Base for all floating-point types (floatdouble and long double).
FLT_MANT_DIG DBL_MANT_DIG LDBL_MANT_DIG   MANTissa DIGits Precision of significand, i.e. the number of digits that conform thesignificand.
FLT_DIG DBL_DIG
LDBL_DIG 6 or greater 10 or greater
10 or greater DIGits Number of decimal digits that can be rounded into a floating-point and back without change in the number of decimal digits.
FLT_MIN_EXP DBL_MIN_EXP
LDBL_MIN_EXP   MINimum EXPonent Minimum negative integer value for the exponent that generates a normalized floating-point number.
FLT_MIN_10_EXP DBL_MIN_10_EXP
LDBL_MIN_10_EXP -37 or smaller -37 or smaller
-37 or smaller MINimum base-10 EXPonent Minimum negative integer value for the exponent of a base-10 expression that would generate a normalized floating-point number.
FLT_MAX_EXP DBL_MAX_EXP
LDBL_MAX_EXP   MAXimum EXPonent Maximum integer value for the exponent that generates a normalized floating-point number.
FLT_MAX_10_EXP DBL_MAX_10_EXP
LDBL_MAX_10_EXP 37 or greater 37 or greater
37 or greater MAXimum base-10 EXPonent Maximum integer value for the exponent of a base-10 expression that would generate a normalized floating-point number.
FLT_MAX DBL_MAX
LDBL_MAX 1E+37 or greater 1E+37 or greater
1E+37 or greater MAXimum Maximum finite representable floating-point number.
FLT_EPSILON DBL_EPSILON
LDBL_EPSILON 1E-5 or smaller 1E-9 or smaller
1E-9 or smaller EPSILON Difference between 1 and the least value greater than 1 that is representable.
FLT_MIN DBL_MIN
LDBL_MIN 1E-37 or smaller 1E-37 or smaller
1E-37 or smaller MINimum Minimum representable floating-point number.
FLT_ROUNDS   ROUND Rounding behavior. Possible values: -1 undetermined
 0 toward zero
 1 to nearest
 2 toward positive infinity
 3 toward negative infinity
Applies to all floating-point types (floatdouble and long double).
FLT_EVAL_METHOD   EVALuation METHOD Properties of the evaluation format. Possible values: -1 undetermined
 0 evaluate just to the range and precision of the type
 1 evaluate float and double as double, and long double as long double.
 2 evaluate all as long double Other negative values indicate an implementation-defined behavior.
Applies to all floating-point types (floatdouble and long double).
DECIMAL_DIG   DECIMAL DIGits Number of decimal digits that can be rounded into a floating-point type and back again to the same decimal digits, without loss in precision.

附中文参考:

FLT_RADIX 用于表述三种浮点数类型的基数(Radix)
DECIMAL_DIG C++11一个可以与 long double 类型互相转化而不会损失精度的十进制数中的数字个数的最大值
FLT_MIN float类型的最小值
DBL_MIN double类型的最小值
LDBL_MIN long double 类型的最小值
FLT_MAX float 类型的最大值
DBL_MAX double类型的最大值
LDBL_MAX long double 类型的最大值
FLT_EPSILON 返回 float 类型的机器精度(Machine epsilon),即 1.0 与下一个可被 float 类型描述的值的差(Difference)
DBL_EPSILON 返回 double 类型的机器精度,即 1.0 与下一个可被 double 类型描述的值的差
LDBL_EPSILON 返回 long double 类型的机器精度,即 1.0 与下一个可被 long double 类型描述的值的差
FLT_DIG 返回在不损失精度的前提下,float 类型可描述的基于基数 10 的数的最大数字(Digit)个数
DBL_DIG 返回在不损失精度的前提下,double 类型可描述的基于基数 10 的数的最大数字个数
LDBL_DIG 返回在不损失精度的前提下,long double 类型可描述的基于基数 10 的数的最大数字个数
FLT_MANT_DIG 返回在不损失精度的前提下,float 类型可描述的基于基数 FLT_RADIX 的数的最大数字个数
DBL_MANT_DIG 返回在不损失精度的前提下,double 类型可描述的基于基数 FLT_RADIX 的数的最大数字个数
LDBL_MANT_DIG 返回在不损失精度的前提下,long double 类型可描述的基于基数 FLT_RADIX 的数的最大数字个数
FLT_MIN_EXP 用 FLT_RADIX 的 x-1 次幂表示 float 类型的规格化(Normalized)时,x 所能取的最小负整数值
DBL_MIN_EXP 用 FLT_RADIX 的 x-1 次幂表示 double 类型的规格化时,x 所能取的最小负整数值
LDBL_MIN_EXP 用 FLT_RADIX 的 x-1 次幂表示 long double 类型的规格化时,x 所能取的最小负整数值
FLT_MIN_10_EXP 用 10 的 x 次幂表示 float 类型的规格化时,x 所能取的最小负整数值
DBL_MIN_10_EXP 用 10 的 x 次幂表示 double 类型的规格化时,x 所能取的最小负整数值
LDBL_MIN_10_EXP 用 10 的 x 次幂表示 long double 类型的规格化时,x 所能取的最小负整数值
FLT_MAX_EXP 用 FLT_RADIX 的 x-1 次幂表示 float 类型的规格化(Normalized)时,x 所能取的最大正整数值
DBL_MAX_EXP 用 FLT_RADIX 的 x-1 次幂表示 double 类型的规格化时,x 所能取的最大正整数值
LDBL_MAX_EXP 用 FLT_RADIX 的 x-1 次幂表示 long double 类型的规格化时,x 所能取的最大正整数值
FLT_MAX_10_EXP 用 10 的 x 次幂表示 float 类型的规格化时,x 所能取的最大正整数值
DBL_MAX_10_EXP 用 10 的 x 次幂表示 double 类型的规格化时,x 所能取的最大正整数值
LDBL_MAX_10_EXP 用 10 的 x 次幂表示 long double 类型的规格化时,x 所能取的最大正整数值
FLT_ROUNDS 默认的浮点数类型的舍入(Rounding)模式
FLT_EVAL_METHOD C++11默认的浮点数类型的反规格化(Denormalization)模式

计算机默认将浮点型常量看做double类型,如果希望为float。则使用后缀f,如果希望long double则使用后缀L(一样大小写任意)。

C++11将使用大括号的初始化称为列表初始化,因为这种初始化常用与给复杂的数据类型提供值列表,它对类型转换的要求更严格。具体的说,列表初始化不允许“缩窄”,即变量的类型可能无法表示赋给它的值(就是我们说的精度损失)。

C++想让强制转换就像函数一样调用,因此允许

typeName(Value);

这样强制转换。

C++认为C语言的强制转换是危险的,便引入了static_cast<>,用法是:

static_cast (Value);

C++11新增了以一个工具,让编译器根据初始值的类型推断变量的类型,为此,它重新定义了auto的含义,使得auto称为不指定变量类型,编译器将把变量的类型设置于初始值相同,在STL中,auto将发挥巨大的作用!

auto i = 1; //i将为int类型
auto d = 1.0; //d将为double类型

2014/09/26 00:53 am posted in  C++

串(字符串)是有另个或多个组成的有限序列,一般记为S=‘a1...an’(n>=0),其中的S是串名,用单引号括起来的的字符序列是串的值,串中字符的数目n称为串的长度,零个字符的串称为空串,它的长度为零。

串中任意个连续的字符组成的子序列称为该串的子串。包含子串的串相应的称为主串。

称两串相等是指两个串的长度相等且对应位置的字符都相等,也可以说当且仅当两个串的值相等。

由一个或者多个空格组成的串叫做空格串,它的长度为串中空格的个数。为了清楚起见,一般用Ø来表示空串(不是空格串)。

串的逻辑结构和线性表极为相似,区别仅在于传的数据对象约束为字符集。然而串的基本操作和线性表有很大差别。在线性表的基本操作中,大多以“单个元素”作为操作对象,而串通常以“串的整体”作为操作对象。

如果在程序设计语言中,串只是作为输入输出的常量出现,则只需存储此串的串值,即字符串序列即可,但大多数非数值处理的程序中,串也以变量的形式出现。

类似于线性表的顺序存储结构,用一组地址连续的存储串值的字符序列。在串的定长顺序存储结构中,按照预定义的大小,为每个定义的串变量分配一个固定长度的存储区,则可用定长数组描述。串的实际长度可在预定义长度的范围内随意分配,超过预定义长度的串值则被舍去,称之为“截断”。而对串长的有两种表示方法,一是以下标为0的数组分量存放串的实际长度(PASCAL),二是在串值后面加一个不计入串长的标记字符(C)。

实现串操作的原操作(各种操作的基础)为“字符序列的复制”,其操作的时间复杂度基于复制的字符序列的长度,如果在操作中出现串值序列的长度超过上限时,约定用截尾处理,克服这个弊病的唯用不限定串长的最大长度,即动态分配串值的存储空间。

堆分配存储表示

名字什么的在高大上也不过是个名字,堆是机内和栈一样重要的自由存储区,一般栈是由计算机管理的,而堆是由程序员管理的。

这种存储表示的特点是:仍以一组地址连续的存储单元存放串值字符序列,但他们的存储空间是程序执行过程中动态分配而得。

这种存储结构表示时的串操作仍然是基于“字符序列的复制”进行的。

其实也不是多么NB,只不过是在拼接序列等可能超出空间的操作,先开一个两个串长度和的空间(保证操作完能完全存放),然后把两个串复制过去,最后把两个串释放掉。

由于堆分配存储结构的串既有顺序存储结构的特点,处理方便,操作中对串长无限制,更显灵活。(但个人感觉好)。

曾经幻想过能不能把字符串像链表一样连起来,自己最终得到的模型是n段离散的字符串,通过一个记录地址的链表来把n段字符串链接起来。

但看到第四章发现还真有这样的结构,称为块链结构,为了便于进行串的操作,当以链表存储串值时,除头指针外,还附属一个尾指针指示链表中的最后一个元素。

对串进行操作时,只需要从头到尾顺序扫描即可,设尾指针的目的是为了便于进行联接操作,但联结时一定要注意处理串尾的无效字符。

#define CHUNKSIZE 80 //由用户定义的块大小

typedef struct Chunk{
    char ch[CHUNKSIZE];
    struct Chunk * next;
}Chunk;

typedef struct{
    Chunk * head,*tail; //头指针和尾指针
    int len;            //串的当前长度
}Lstring;

在链式存储方式中,结点大小的选择和顺序存储方式的格式选择一样都很重要,它直接影响着串处理的效率,由于所处理的串往往很长或很多,这就要求我们考虑串值的存储密度:

存储密度= 串值所有的存储位/实际分配的存储位

显然存储密度小(如结点为1),运算处理方便,然而存储占用量大,如果在串处理过程中需进行内、外存交换的话,则会因为内外存交换操作过多而影响总效率。

还有,串的字符集的大小也是一个重要因素,一般地,字符集小,其机内编码就短,这也影响串值的存储方式的选取。

串值的链式结构对某些串操作,如联接等,有一定的方便性,但对插入等又有着大量的复杂操作,又不是十分方便。总的来说,顺序结构和链式结构各有优势,分具体情况选用是最棒的选择。

话说数据结构包括存储方式和操作,下面说说操作。

在串中最常用的功能莫过于子串的定位操作,也称模式串的匹配。一般使用一个被王政称为朴素算法的时间复杂度为O(n*m)的东西,其中n和m分别是主串和模式串的长度,其实这也是最容易想到的算法。

节选《数据结构》中关键代码:

while(i  T[0])
    return i - T[0];
return 0;

算法的基本思想是:从主串S的第一个字符起和模式串的第一个字符进行比较,若相等则继续逐个比较后续字符,否者从主串的下一个字符起再重新和模式串的第一个字符起逐个比较,以此类推,直到把主串比较完(匹配不成功)或找到匹配串的位置(匹配成功)。

这种算法容易理解也是最直接简单的查找算法,但往往越好理解的都是越笨拙的,这个算法最大的问题就是慢。有慢的,便必然有快的算法,要看快的,要先直到这个慢的是为什么慢的。

上面那个笨拙的算法,须要两个指针分别指向两个串,当匹配不成功时,两个串都有不同程度的回溯,如果指针不回溯哪怕只有一个指针回溯想必也是极好的。这样,新算法(也可以说是改良算法)就诞生了。

K(Knuth)M(Morris)P(Prate)算法,由前面那3个人同时发现,简称KMP算法,它便实现了只让一个指针回溯,这个指针利用已经得到的“部分匹配”,滑动到模式串的“敲到好处”的位置,继续匹配。

至于怎么确定这个“恰到好处”的位置,就是利用了一个被称之为next函数的东西。

若令next[j] = k,则next[j]表明当模式中第j个字符与主串中相应字符“失配”时,在模式中需重新和主串中该字符进行比较的字符的位置。由此可以引出模式串的next函数的含义:

next[j] = 0当j=1时

        = Max(R|1 1...pk-1'=='pj-k+1...pj-1')

        = 1 其他情况

说白了,就是记录了模式串当前位置和前面几个元素相同。

在寒假ACM集训的时候第一次接触了,KMP,其实KMP中的精华就在next函数中,其中的意义使用灵活,但实在是一种难以总结感觉,更像是经验问题,详情见当时的总结 月球美容计划之那些天我们学过的KMP

在暑假给第3批队员培训的时候,又好好的研究了 KMP,但最后发现王政的KMP理解的比较深入,他的教案文档《Kmp 算法引论》深入浅出,在集训群里又找到这份文档,作为最后对KMP的补充吧。资源链接:KMP字符串匹配算法

总结

  1. 传统算法(朴素算法)最坏情况是O(nm),但一般是在O(n+m),也不是太坏,所以一般没有特殊要求的程序还在用这种算法;

  2. KMP算法在有大量重复的情况下优化明显,对主串较大时比较有效;

  3. next函数还是有缺陷的,比如模式串'aaaab'与主串'aabaaaab'匹配时,用时几乎和朴素算法一样;

  4. 以发展的眼光看问题,其实作为常用算法之一,匹配算法也有发展,KMP已经不是主流。

文本编辑程序问题

文本编辑程序中通过设立页指针、行指针和字符指针,分别指示当前操作的页、行和字符。如果在某行内插入或删除若干字符,则要修改行表中该行的长度。若该行的长度超出了分配给它的存储空间,则要为该行重新分配存储空间,同时还要修改该行的起始位置。

为了查找方便,行表是按行号递增顺序存储的,因此,对行表惊醒的插入或删除运算需移动操作位置以后的全部表项,页表的维护则和行表类似。

由于访问是以页表和行表作为引索的,所以在作行和页的删除操作时,可以只对行表和页表相应的修改,不必删除所涉及的字符,可以节省不少空间。


第四章其实已经看完三天了,这三天又是亚洲区域赛网络赛又是去打针,没有什么比不健康的身体状况更糟糕了,几天终于好些了,一口气把第四章的笔记整理完毕,好收拾旧行装,继续上路。

2014/09/22 15:40 pm posted in  算法&数据结构

C++ 基本概念

又回到了刚开始大学生活的感觉,当时在啃蓝书,厚厚的一本,感觉没有尽头。现在的C++更厚,而且,做笔记看书是最慢的,感觉没有尽头。


C++能够使用printf,scanf和其他所有标准C的输入输出函数,只需要包含常规C语言的stdio.h头文件。

 

C++和C一样,也是用终止符,而不是分隔符,终止符是一个分毫,它是语句的结束标记,是语句的组成部分。而不是语句之间的标记。

C++句法要求main()函数的定义以函数头int main()开始,main函数有返回值是个好习惯,到现在为止上课老师依然是void实在蛋疼。

Main函数如果没有参数,则应加上void。对于main函数来说,可以不加return 0,ANSI/ISO C++标准允许这么做,当不加return语句时会自动补上return 0,但是这个标准不适用于其他函数,当然,不加return 是个不好的习惯。

 

像iostream这样的文件叫做包含文件,C语言的传统是头文件使用拓展名.h,将其作为一种通过名称标识文件类型的简单方式,而C++的头文件没有拓展名,有些头文件被转换为C++头文件,这些文件被重新命名,去掉了拓展名.h,并加上了前缀c,如stdio.h变为cstdio。对于去掉h,,不是C++形式上的变化,没有h的头文件可以包含名称空间。

 

如果使用iostream而不是iostream.h(C++旧式风格)应使用using namespace std;名称空间编译指令来使iostream中的定义对程序可用。

名称空间支持是一项C++的特性,旨在编写大型程序以及将多个厂商现有的代码组合起来的程序时更容易。

名称空间::内容;

按着这种方法,类、函数和变量便是C++编辑器的标准组件。

 

“C++拓展了运算符重载的概念允许为用户定义的类型(类)重定义运算符的含义。

 

诸如endl等对cout来说有特殊含义的特殊符号被称为控制符。

C++也支持C的旧式方法换行“n”,但与endl的区别是endl确保程序继续运行前刷新输出(刷新缓冲区,立即显示在屏幕上),而是用“n”不能提供这样的保证。

C++源代码的风格

  1. 每条语句占一行
  2. 每个函数都有一个开始花括号和一个结束花括号,这两个花括号各占一行
  3. 函数中的语句都相对于花括号进行缩进
  4. 与函数名称相关的圆括号周围没有空白

空行将声明语句与程序其它部分非开,这是C常用方法但在C++中不那么常见。

与printf相比,cont能识别类型,cout的设计更灵活,更好用,另外,它是可扩展的,也就是说可以重新定义“<<”运算符,使cout能够识别和显示所开发的新数据类型,如果喜欢printf提供的细致的控制功能,可以使用更高级的cout来获得相同的效果。(C的输入输出和C++的输入输出是两个不同的缓存区,导致C++输入输出较慢,如何解决C++输入慢)。

类之于对象,如同数据类型之于变量,类定义描述了数据格式及其用法。类描述了一种数据类型的全部属性(包括可执行的操作)。

C++程序应当为程序中使用的每个函数提供原理。

在特定函数中使用类似using std::cont这样的编译命令而不是using namespace std;更节省。

个人命名风格特别值得注意,它有助于保持一致性和精确性,精确、让人一目了然的个人命名约定是良好的软件工程的标识。

C++有六种类型的语句:

  1. 声明语句
  2. 赋值语句
  3. 消息语句:将消息发送给对象,激发某种行为
  4. 函数调用
  5. 函数原型
  6. 返回语句

类是用户定义的数据类型规范,详细描述了如何表示信息以及可对数据执行哪些操作,对象是根据类规范创建的实体。


C++ PP前十章的内容算是过第二遍了,而前七章和蓝书无差,都看烦了= =,感觉再看下去就要背下来了。不过再简单的东西,为了十章以后的深入,就这样忍了吧。

2014/09/17 00:52 am posted in  C++

栈和队列

从数据结构的角度看,栈和队列也是线性表,其特殊性在于栈和队列的基本操作是线性表操作的子集。他们是操作受限的线性表(其实,更可以认为栈和队列是两种思想,而线性表是它们的具体实现),但在现实上,栈和队列和线性表是大不相同的两类重要的抽象数据类型。

:是限定仅在表尾进行插入或者删除操作的线性表。因此,对栈来说,表尾端有特殊含义,称栈顶,相应的,表头元素称为栈底。

栈又称先进先出(Last In First Out,LIFO)的线性表。

栈的基本操作除了在栈顶进行插入或删除外,还有栈的初始化、判空及取栈顶元素等。

和线性表相似,栈也有两种存储表示方法:

顺序栈:即栈的顺序存储结构是利用一组地址连续的存储单元依次存放自栈底到栈顶的数据元素。

在应用过程中,当站的空间不够使用的时候再逐段扩大。

链式栈:用链表来模拟栈,灰常节省内存。

栈的应用:

  1. 数制(进制)转换(逆序)
  2. 括号匹配(延时判断)
  3. 行编辑程序(延时处理)
  4. 表达式求值(延时计算)
  5. 走迷宫(DFS)

求迷宫从入口到出口的所有的路径是一个经典的程序设计问题。由于计算解迷宫问题时通常用到的是“穷举求解”的方法,即从入口出发,顺着某一方向向前探索,若能走通,则继续往前走,否则原路返回,换一个方向再继续探索直至所有可能的通路都探索为止。但所求线路必须为简单路径,即在求得的路径上不能重复出现同一通道块。

设定当前位置的初值的入口位置

do{
    若当前位置可通过且没有走过
    则{
        将当前位置插入栈顶;
        若该位置是出口位置,则结束
        否则切换当前位置的相邻方块为新的当前位置;
    }
    否则
        若栈不空且站定位置尚有其他方向未经探索
        则设定新的当前位置为沿顺时针方向旋转找到栈顶位置的下一相邻块
        若栈不空但栈顶位置的四周均不可通
        则{
            删去栈顶位置
            若栈不同,这重新测试新的栈顶位置
                直至找到一个可通 的相邻快或者栈空
        }
}while(栈不空)

其实就是用循环实现DFS,因为DFS用大量递归,且递归本来就慢些,因此,循环将消耗更少的时间。

递归的本质就是栈,只不过使用栈的是编译器,而不是程序员本身。有的数据结构如二叉树、广义表等,由于结构本事固有递归的特性,则它们 的操作可递归地描述,还有的一类问题,虽然本身没有明显的递归结构,但用递归求解比迭代求解更简单,如八皇后问题、Hanoi塔问题。

汉诺塔问题,可以把整个问题抽象化为

  1. 将第1-n-1个盘子通过使用Y、Z移动到Y柱子上
  2. 将第n个盘子移动到Z柱子上
  3. 将1-n-1个盘子通过使用X、Y移动到Z柱子上。

伪代码:

void move(int x,int n,int z)
{
//c为记录步数的全局变量
printf("%d Move disk %d frome %c to %cn",++c,n,x,z);
}

void hanoi(int n,char x,char y,char z)
{
if(n==1)
move(x,1,z);
else
{
hanoi(n - 1,x,z,y);
move(x,n,z);
hanoi(n - 1,y,x,z);
}
}

这就是把大问题化简为为小的问题然后用递归处理的过程。

在汇编程序设计中,主程序和子程序之间的链接及信息交换相类似,在高级语言编制的程序中,调用函数和被调函数之间的链接及信息交换需通过栈来进行。

通常为在一个函数的运行期间,调用一另个函数时,在运行被调用函数之前,系统需要完成3件事:

  1. 将所有的实在参数返回地址等信息传递给被调用函数
  2. 为被调用函数的局部变量非配存储空间
  3. 将控制转移到被调函数入口。

而从被调用函数返回调用函数之前,系统也应完成3件事:

  1. 保证被调函数的计算结果
  2. 释放被调函数的数据区
  3. 依照被调函数保存的返回地址将控制转移到调用函数

当有多个函数构成的嵌套调用时,按照“后调用先返回”的原则,上述函数之间的信息传递和控制转移必须通过“栈”来实现,即系统将整个程序运行时所需的数据空间安排到一个栈里,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放他的存储区,则当前正运行的函数的数据区必在栈顶。

这也就是为什么递归深度太大导致“爆栈”。

系统需设立一个“递归工作域”作为整个递归函数运行期间使用的数据存储区。每一层递归所需信息构成一个“工作记录”,其中包括所有的实在参数,所有局部变量以及上一层的返回地址。

每进入一层递归,就会产生一个新的纪录压入栈顶。每出一层递归,就从栈顶弹出一个工作记录,成这个记录为“活动记录”,并称指示活动记录的栈顶指针为“当前环境指针”。

队列

和栈相反,队列是一种先进先出(First In First Out,FIFO)的线性表,它之允许在表的一端进行插入,而在另一端删除元素。

在队列中,允许拆入的一段叫做队尾,允许删除的一端则称作队头。除了栈和队列之外,还有一种限定性数据结构是双向队列(双端队列)。

两个方向均可以出入,分为左入,左出,右入,右出等操作,但应用远不如栈和队列。

就像链栈一样,队列一样有两种存储结构(相性表的两种实现)

用链表表示的队列简称链队列,一个链队列虽然需要两个分别指向队头和队尾的指针(分别称作头指针尾指针)才能唯一确定。空的链队列的操作即为单向链表的插入和删除操作的特殊情况,只是尚需修改尾指针或头指针。

循环队列

在队列的顺序存储结构中,除了用一组地址连续的存储单元依次存放从队列头到队列尾的元素之外,尚需附属 两个指针front和rear分别只是队列头元素及队列尾元素的位置。

初始化建空队列时,另front = rear = 0,每当插入新的队列尾元素时,头指针增1,因此,在非空队列中,头指针始终指向队列头元素,而尾指针始终指向尾元素的下一个位置。

但这样的操作有个致命的问题,如果不知道队列元素最多是多少,就需要开尽量大的空间,原因头尾指针只增不减,已经被使用过的空间就此浪费。因此为了更经济,为了存放更多的元素,循环队列诞生了。

将顺序队列臆造为一个环状的空间,指针和队列之间的关系不变,判断“空”还是“满”,可有两种处理方法:1.设另一个标志位以区别队列是“空”还是“满”;2.少用一个元素空间,约定以队列头指针在队列尾的下一位置作为队列满的状态。

队列还有很多应用,也有很多拓展,不如优先队列,在先进先出的基础上,为每个元素添加优先级,使队列中优先级高的先先出,在BFS等搜索中,可以进行优化。

2014/09/16 15:39 pm posted in  算法&数据结构