一、选择题 1. 以下代码的运行结果是______
if($i=""){
echo"a";
}else{
echo"b";
}
A.输出a B.输出b C.条件不足,无法确定 D.运行出错
A B C D
B
[解析] 因为if条件内的$i是被赋值为空,在PHP中,空字符串、0、false、空数组都被认为是假,所以该程序执行else部分,输出b。选项B正确。 所以,本题的答案为B。
二、填空题 1. 能让类在整个脚本里只实例化一次的设计模式是______。
单例模式
[解析] 单例模式是一种用于确保程序中只有一个类实例化的程序。
2. 函数______能读取文本文件中的一行。读取二进制文件或者其他文件时,应当使用______函数。
fgets(),fread()
[解析] fgets()函数主要用于读取文本文件中的一行,而fread()函数主要用于读取二进制文件。fseek()函数主要用于在打开的文件中定位,fputs()函数用于写入文件。
3. 在PHP中,“+”操作符的功能包括______、______。
数组数据合并、变量数据相加
[解析] “+”操作符可以实现数组的合并,但是合并遵循如下的规则:如果两个数组存在相同的key,那么结果保留前面数组中的值,而丢弃掉后面数组中的值,如下例所示: $a=array( 1=>'a', ); $b=array( 2=>'b', 1=>'c' ); $c=$a+$b; vat_dump($c); 程序的运行结果为 array(2){ [1]=> string(1)"a" [2]=> string(1)"b" } “+”最常使用的功能就是实现变量数据的相加。
5. Cookie存储在______,Session是将数据存储在______,会话的生命周期默认为______。
客户端;服务端;1440s
[解析] Cookie存储在客户端,而Session存储在服务器端,PHP中的Session默认有效期是1440s(24min),即用户在24min内没有刷新,当前Session就会失效。如果用户关闭了浏览器,Session也会消失。
三、简答题 1. 正则表达式是什么?PHP中有哪些常用的与正则相关的函数?请写出一个email的正则表达式、中国手机号码和座机号码的正则表达式?
正则表达式是一种字符串匹配的模式,可以判断一个字符串的组成是否满足某种特定的模式,将匹配的子串替换或者从某个串中取出符合某个条件的子串等。 常用的正则函数有: 1)preg_match()函数用于通过正则表达式匹配字符串。 2)preg_quote()函数可以转义正则表达式字符。 3)preg_replace()函数用于执行正则表达式的搜索和替换。 4)preg_replace_callback()函数可以执行一个正则表达式搜索和使用回调替换。 5)preg_split()函数可以通过一个正则表达式分隔字符串。 email拥有固定的格式,可以根据这个格式匹配正确的email,它的正则表达式为/^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/。 中国手机号码的正则表达式如下: /^1[3-5,8]{1}[0-9]{9)$/ 座机号码的验证的正则表达式如下: /^([0,9]{3,4}-)?[0-9]{7,8}$/
2. GET和POST的区别有哪些?
主要区别有4个方面: 1)语义不同,GET是获取数据,POST是提交数据。 2)HTTP协议规定GET比POST安全,因为GET只做读取,不会改变服务器中的数据。但这只是规范,并不能保证请求方法的实现也是安全的。 3)GET请求会把附加参数带在URL上,而POST请求会把提交数据放在报文内。在浏览器中,URL长度会被限制,所以GET请求能传递的数据有限,但HTTP协议其实并没有对其做限制,都是浏览器在控制。 4)HTTP协议规定GET是幂等的,而POST不是,所谓幂等是指多次请求返回的结果相同。实际应用中,并不会这么严格,当GET获取动态数据时,每次的结果可能会有所不同。
3. 与数组相关的常用函数有哪些?
与数组相关的常用函数主要有以下几个: 1)count()函数,用于计算数组中的元素数量,sizeof()函数是count()函数的别名。 2)sort()函数,用于数组对键值进行升序排序。 3)in_array()函数,用于检查数组中是否存在某个键值。 4)explode()函数和implode()函数,用于数组与字符串相互转换。 explode()函数,通过使用一个分隔符对字符串进行切割,返回一个数组。例如,通过逗号对字符串进行分割成数组:$arr=implode(",",$String)。 implode()函数,通过设定一个连接符,将数组中的每个元素连接为一个字符串。例如,通过逗号对每个键值进行分隔拼接成字符串:implode(",","$array")。 5)array_merge()函数,可以将一个或多个数组的元素合并成一个数组,一个数组的值附加在前一个数组的后面,返回合并后的数组。但是需要注意,如果合并的数组中存在相同的字符串键名,那么后一个数组的键值会覆盖前一个数组的键值。但是,如果是数字的数组键名,那么不存在后一个数组键值覆盖前一个数组键值的情况。 6)array_unique()函数,用于移除数组中重复的值,并返回去除重复键值后的数组。 7)array_shift()函数,用于删除数组中的第一个值,并且返回被删除元素的值。 8)array_unshift()函数,用于向数组的开头插入新元素。 9)array_push()函数,用于向数组的尾部插入一个或多个元素(入栈),然后返回数组的长度。 10)array_pop()函数,用于删除数组中的最后一个元素(出栈)。 11)array_reverse()函数,用于把数组的键值反向排序并返回数组。
4. 请写出面向对象中接口和抽象类的区别及应用场景。
1)被abstract修饰的类叫作抽象类,抽象类可以没有抽象方法,但是一个类如果存在抽象方法,那么这个类一定是抽象类。抽象方法必须使用abstract关键字修饰。 2)接口中全部是抽象方法,方法不需要用abstract定义。 3)当多个同类的类要设计一个上层时,通常设计为抽象类,当多个异构的类要设计一个上层时,通常设计为接口。
5. 一、二、三、四范式有何区别?
在设计与操作维护数据库时,最关键的问题就是要确保数据正确地分布到数据库的表中,使用正确的数据结构,不仅有助于对数据库进行相应的存取操作,还可以极大地简化应用程序的其他内容(查询、窗体、报表、代码等),正确地进行表的设计称为“数据库规范化”,它的目的就是减少数据库中的数据冗余,从而增加数据的一致性。
范化是在识别数据库中的数据元素、关系,以及定义所需的表和各表中的项目这些初始工作之后的一个细化的过程。常见的范式有1NF、2NF、3NF、BCNF以及4NF。
1NF,第一范式。是指数据库表的每一列都是不可分割的基本数据项,同一列中不能有多个值,即实体中的某个属性不能有多个值或者不能有重复的属性。如果出现重复的属性,那么就可能需要定义一个新的实体,新的实体由重复的属性构成,新实体与原实体之间为一对多关系。第一范式的模式要求属性值不可再分裂成更小部分,即属性项不能是属性组合或由组属性组成。简而言之,第一范式就是无重复的列。例如,由“职工号”“姓名”“电话号码”组成的表(一个人可能有一个办公电话和一个移动电话),这时将其规范化化为1NF可以将电话号码分为“办公电话”和移动电话两个属性,即职工(职工号,姓名,办公电话,移动电话)。
2NF,第二范式。第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一范式(1NF)。第二范式(2NF)要求数据库表中的每个实例或行必须可以被唯一地区分。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。如果关系模式R为第一范式,并且R中每一个非主属性完全函数依赖于R的某个候选键,则称R为第二范式模式。(如果A是关系模式R的候选键的一个属性,则称A是R的主属性,否则称A是R的非主属性。)例如,在选课关系表(学号,课程号,成绩,学分)中,关键字为组合关键字(学号,课程号),但由于非主属性学分仅依赖于课程号,对关键字(学号,课程号)只是部分依赖,而不是完全依赖,所以此种方式会导致数据冗余以及更新异常等问题,解决办法是将其分为两个关系模式:学生表(学号,课程号,分数)和课程表(课程号,学分),新关系通过学生表中的外关键字课程号联系,在需要时进行连接。
3NF,第三范式。如果关系模式R是第二范式,且每个非主属性都不传递依赖于R的候选键,则称R是第三范式的模式。例如,学生表(学号,姓名,课程号,成绩),其中学生姓名无重名,所以该表有两个候选码(学号,课程号)和(姓名,课程号),则存在函数依赖:学号→姓名,(学号,课程号)→成绩,(姓名,课程号)→成绩,唯一的非主属性成绩对码不存在部分依赖,也不存在传递依赖,所以属于第三范式。
BCNF。它构建在第三范式的基础上,如果关系模式R是第一范式,且每个属性都不传递依赖于R的候选键,那么称R为BCNF的模式。假设仓库管理关系表(仓库号,存储物品号,管理员号,数量),满足一个管理员只在一个仓库工作;一个仓库可以存储多种物品。则存在如下关系:
(仓库号,存储物品号)→(管理员号,数量)
(管理员号,存储物品号)→(仓库号,数量)
所以,(仓库号,存储物品号)和(管理员号,存储物品号)都是仓库管理关系表的候选码,表中的唯一非关键字段为数量,它是符合第三范式的。但是,由于存在如下决定关系:
(仓库号)→(管理员号)
(管理员号)→(仓库号)
即存在关键字段决定关键字段的情况,所以其不符合BCNF范式。把仓库管理关系表分解为两个关系表:仓库管理表(仓库号,管理员号)和仓库表(仓库号,存储物品号,数量),这样的数据库表是符合BCNF范式的,消除了删除异常、插入异常和更新异常。
4NF,第四范式。设R是一个关系模式,D是R上的多值依赖集合。如果D中成立非平凡多值依赖X—Y时,X必是R的超键,那么称R是第四范式的模式。例如,职工表(职工编号,职工孩子姓名,职工选修课程),在这个表中同一个职工也可能会有多个职工孩子姓名,同样,同一个职工也可能会有多个职工选修课程,即这里存在着多值事实,不符合第四范式。如果要符合第四范式,那么只需要将上表分为两个表,使它们只有一个多值事实,例如,职工表一(职工编号,职工孩子姓名),职工表二(职工编号,职工选修课程),两个表都只有一个多值事实,所以符合第四范式。
下图为各范式关系图。
四、编程题 1. 编写一个应用程序,该程序包括2个类:Monkey类和People类。要求:
1)Monkey类中有个speak()方法,在speak方法中输出“咿咿呀呀......”的信息。
2)People类是Monkey类的子类,在People类中重写方法speak(),在speak方法中输出“不错嘛!会说话了!”的信息。
根据题意,实现代码如下: <?php class Monkey{ public function speak(){ echo "咿咿呀呀......"; } } Class People extends Monkey{ public function speak(){ echo "不错嘛!会说话了!"; } } ?>
2. 已知三个升序整数数组a[1]、b[m]和c[n],在三个数组中各找一个元素,使得组成的三元组距离最小。三元组距离的定义是,假设a[i]、b[j]和c[k]是一个三元组,那么距离为Distance=max(|a[i]-b[j]|,|a[i]-c[k]|,|b[j]-c[k]|),请设计一个求最小三元组距离的最优算法。
function maxNum($a,$b,$c){ $max=$a<$b?Sb:$a; $max=$max<$c?$c:$max; return$max; } function minNum($a,$b,$c){ $min=$a<$b?$a:$b; $min=$min<$c?$min:$c; return$min; } function getMinDistance($a,$aLen,$b,$bLen,$c,$cLen){ $curDist=0; $min=0; $minDist=max($a); $i=0;//数组a的下标 $j=0;//数组b的下标 $k=0;//数组c的下标 while(1){ $curDist=maxNum(abs($a[$i]-$b[$j]),abs($a[$i]-$c[$k]),abs($b[$j]-$c[$k])); if($curDist<$minDist) $minDist=$curDist; //找出当前遍历到三个数组中的最小值 $min=minNum($a[$i],$b[$j],$c[$k]); if($min==$a[$i]){ if(++$i>=$aLen) break; } else if($min==$b[$j]){ if(++$j>=$bLen) break; } else{ if(++$k>=$cLen) break; } } return $minDist; } 采用这种算法最多只需要对三个数组分别遍历一遍,因此,时间复杂度为O(1+m+n)。
[解析] 假设当前遍历到这三个数组中的元素分别为ai 、bi 、ci ,并且ai <=bi <=ci ,此时它们的距离肯定为Di =ci -ai ,那么可以分如下三种情况讨论: 1)如果接下来求ai 、bi 、ci+1 的距离,由于ci+1 >=ci ,此时它们的距离必定为Di+1 =ci+1 -ai ,显然Di+1 >=Di ,因此,Di+1 不可能为最小距离。 2)如果接下来求ai 、bi+1 、ci 的距离,由于bi+1 >=bi ,如果bi+1 <=ci ,此时它们的距离仍然为Di+1 =ci -ai ;如果bi+1 >ci ,那么此时它们的距离为Di+1 =bi+1 -ai ,显然Di+1 >=Di ,因此,Di+1 不可能为最小距离。 3)如果接下来求ai+1 、bi 、ci 的距离,如果ai+1 <ci -|ci -ai |,此时它们的距离Di+1 =max(ci -ai+1 ,ci =bi ),显然Di+1 <Di ,因此,Di+1 有可能是最小距离。 综上所述,在求最小距离的时候只需要考虑第3种情况即可。具体实现思路为,从三个数组的第一个元素开始,首先求出它们的距离minDist,接着找出这三个数中最小数所在的数组,只对这个数组的下标往后移一个位置,接着求三个数组中当前遍历元素的距离,如果比minDist小,则把当前距离赋值给minDist,以此类推,直到遍历完其中一个数组为止。 例如,给定数组$a=[3,4,5,7,15];$b=[10,12,14,16,17];$c=[20,21,23,24,37,30]; 1)从三个数组中找出第一个元素3、10、20,显然它们的距离为20-3=17。 2)由于3最小,因此,数组a往后移一个位置,求4、10、20的距离为16,由于16<17,因此,当前数组的最小距离为16。 3)同理,对数组a后移一个位置,依次类推,直到遍历到15的时候,当前遍历到三个数组中的值分别为15、10、20,最小距离为10。 4)由于10最小,因此,数组b往后移动一个位置遍历12,此时三个数组遍历到的数字分别为15、12、20,距离为8,当前最小距离是8。 5)由于12最小,数组b往后移动一个位置为14,依然是三个数中最小值,往后移动一个位置为16,当前的最小距离变为5,由于15是数组a的最后一个数字,因此,遍历结束,求得最小距离为5。
3. 给定以非递减顺序排序的三个数组,找出这三个数组中的所有公共元素。例如,给出下面三个数组:ar1=[2,5,12,20,45,85],ar2=[16,19,20,85,200],ar3=[3,4,15,20,39,72,85,190]。那么这三个数组的公共元素为[20,85]。
最容易想到的方法是首先找出两个数组的交集,然后把这个交集存储在一个临时数组中,最后找出这个临时数组与第三个数组的交集。这个算法的时间复杂度为O(N1+N2+N3),其中N1、N2和N3分别为三个数组的长度。这种方法不仅需要额外的存储空间,而且需要额外的两次循环遍历。下面介绍另外一种只需要一次循环遍历,而且不需要额外存储空间的方法。 假设当前遍历的三个数组的元素分别为ar1[i]、ar2[j]、ar3[k],则存在以下几种可能性: 1)如果ar1[i]、ar2[j]和ar3[k]相等,那么说明当前遍历的元素是三个数组的公有元素,可以直接打印出来,然后通过执行i++,j++,k++,使三个数组同时向后移动,此时继续遍历各数组后面的元素。 2)如果ar1[i]<ar2[j],那么执行i++来继续遍历ar1中后面的元素,因为ar1[i]不可能是三个数组公有的元素。 3)如果ar2[j]<ar3[k],同理可以通过j++来继续遍历ar2后面的元素。 4)如果前面的条件都不满足,那么说明ar1[i]>ar2[j]而且ar2[j]>ar3[k],此时可以通过k++来继续遍历ar3后面的元素。 实现代码如下: <?php function findCommon($ar1,$ar2,$ar3,$n1,$n2,$n3) { $i=0; $j=0; $k=0; /*遍历三个数组*/ while($i<$n1 && $j<$n2 && $k<$n3) { /*找到了公有的元素*/ if($ar1[$i]==$ar2[$j] && $ar2[$j]==$ar3[$k]) { printf("%d",$ar1[$i]); $i++;$j++;$k++; } /*ar[i]不可能是公有的元素*/ else if($ar1[$i]<$ar2[$j]) $i++; /*ar2[j]不可能是公有的元素*/ else if($ar2[$j]<$ar3[$k]) /*ar3[k]不可能是公有的元素*/ else $k++; } } $ar1=[2,5,12,20,45,85]; $ar2=[16,19,20,85,200]; $ar3=[3,4,15,20,39,72,85,190]; $n1=count($ar1); $n2=count($ar2); $n3=count($ar3); findCommon($ar1,$ar2,$ar3,$n1,$n2,$n3); ?> 程序的运行结果为 20 85 算法性能分析:这个算法的时间复杂度为O(N1+N2+N3)。
4. 给定一个数组,数组中含有重复元素,给定两个数字num1和num2,求这两个数字在数组中出现的位置的最小距离。
对于这类问题,最简单的方法就是对数组进行双重遍历,找出最小距离,但是这种方法效率比较低下。由于在求距离的时候只关心num1与num2这两个数,因此,只需要对数组进行一次遍历即可,在遍历的过程中分别记录num1或num2的位置就可以非常方便地求出最小距离。 主要思路:对数组进行双重遍历,外层循环遍历查找num1,只要找到num1,就开始内层循环,即对数组从头开始遍历查找num2,当遍历到num2时,就计算它们的距离dist。当遍历结束后,最小的dist值就是它们之间的最小距离。 实现代码如下: <?php function minDistance($arr,$num1,$num2){ $len=count($arr); if(!$arr||$len<=0){ printf("参数不合理"); return"; } $minDis=max($arr); //num1与num2的最小距离 $dist=0; for($i=0;$i<$len;++$i){ if($arr[$i]==$num1){ for($j=0;$j<$len;++$j){ if($arr[$j]==$num2){ $dist=abs($i-$j); //当前遍历的num1与num2的距离 if($dist<$minDis) $minDis=$dist; } } } } return $minDis; } $arr=array(4,5,6,4,7,4,6,4,7,8,5,6,4,3,10,8); $num1=4; $num2=8; printf("%d",minDistance($arr,$num1,$num2)); ?> 程序的运行结果为 2 算法性能分析:这个算法需要对数组进行两次遍历,因此,时间复杂度为O(N2 )。