文章目录
- [1. 前言](#1. 前言)
- [2. JavaScript 部分](#2. JavaScript 部分)
-
- [2.1 使用 JavaScript 打印一个树](#2.1 使用 JavaScript 打印一个树)
- [3. PHP 部分](#3. PHP 部分)
-
- [3.1 比较数组](#3.1 比较数组)
- [2.2 向数据库添加产品分类](#2.2 向数据库添加产品分类)
- [2.3 查书本信息](#2.3 查书本信息)
1. 前言
其实这里考试的题目都不难,JavaScript 和 Java 语言近似,所以难度不大。 PHP 稍微跟之前的 C 语言近似,虽然大家可能接触 C 语言比较少,但是如果平时写代码比较多,其实理解起来也不是很难。我觉得考试的难点主要在于数据库的部分,因为我当时学数据库学的不是很好,这里还涉及了数据库的查询知识。
整体上所有题目都与 Lab 里的示例代码类似,或者和之前的考试示例代码近似,这篇文章主要是一个分享,并不会做过多的解释。
2. JavaScript 部分
2.1 使用 JavaScript 打印一个树
写一个名为 treePrint 的函数,接受一个名为 height 的参数,使用嵌套循环写一个对称的树,其由 ~ 和 * 两个符号组成。而且输出使用 console.log()。
假设接受的 height = 3,那么输出的结果如下(这里用图片演示更清晰):

如果 height = 5,那么输出的结果

我们先观察这里的结构,其实一行的长度是 2 倍的 height 值再加 1。
最中间的位置是 *,然后每到下一行他就会往两边延申一个。如果用数学表达式的话那么 * 的数量那就是 n ( ∗ ) = 2 x − 1 n_{(*)} = 2x - 1 n(∗)=2x−1, x x x从1开始到height值结束,当然我们在写 for 循环的时候可以从0开始,那么这里就是 + + +而不是 − - −。
对于波浪线,我们发现第一层左半部分输出的数量正好是 height 数量个,然后另一半也是如此。
这里需要注意的是使用 console.log()输出,其实 lab3 有相关的代码。
我们前面分析了这里的三个部分的组成。
0到height-i-1的位置都是 ~(因此共height-i个长度)。
在这个后面是 2*i+1 个 * ,以及同样长度的 ~。
javascript
function printTree() {
// 1. 获取用户输入的数字(树的高度)
height = document.getElementById("num").value;
let tree = ''; // 用于存储整棵树的所有行
// 2. 外层循环:控制行数(从上到下)
for (let i=0; i<height; i++) {
let line = ''; // 当前这一行的内容
// 3. 第一个内层循环:打印左边的波浪号(空格效果)
// 随着 i 增大,左边的 ~ 越来越少
for (let j=0; j<height-i-1; j++) {
line += '~';
}
// 4. 第二个内层循环:打印中间的星号(树的实体)
// 第 i 行有 2*i+1 个星号(1, 3, 5, 7...)
for (let j=0; j<2*i+1; j++) {
line += '*';
}
// 5. 第三个内层循环:打印右边的波浪号(空格效果)
for (let j=0; j<height-i-1; j++) {
line += '~';
}
line += '\n'; // 换行
tree += line; // 把这一行加到整棵树里
}
// 6. 创建 <pre> 标签显示结果(保留空格和换行格式)
const pre = document.createElement('pre');
pre.textContent = tree;
document.body.appendChild(pre); // 添加到网页上
}
但这其实并不完全符合这道题的要求,我们需要对其做出一定的修改。
将其改为接受 height 参数,并且使用 console.log() 进行打印,因此结果如下。
javascript
function printTree(height) {
let tree = ''; // 用于存储整棵树的所有行
// 外层循环:控制行数(从上到下)
for (let i = 0; i < height; i++) {
let line = ''; // 当前这一行的内容
// 第一个内层循环:打印左边的波浪号
for (let j = 0; j < height - i - 1; j++) {
line += '~';
}
// 第二个内层循环:打印中间的星号
for (let j = 0; j < 2 * i + 1; j++) {
line += '*';
}
// 第三个内层循环:打印右边的波浪号
for (let j = 0; j < height - i - 1; j++) {
line += '~';
}
line += '\n'; // 换行
tree += line; // 把这一行加到整棵树里
}
// 使用 console.log() 打印到控制台
console.log(tree);
}
当然这道题的思路也可以从对称的角度出发。
一行的长度其实是 2 倍的 height 值再加 1。
我们确定一行的中心点,然后从这个地方将其分为两部分,那么其中的一部分就是这里的一半,这个长度其实可以看作是 height 值再加 1。
在这部分中由 ~ 和 * 组成,我们只需要确定现在分界线就能绘制出这两部分。
我们回到我们的例子中,第一行的时候是1个 *。
第二行的时候是2个 *,以此类推。
因此很容易理解,我们确定好中心点的位置后,这个位置固定一个 *,然后每次这个地方往前一个也是 *。
对于另一部分这里的减法就是加法。
代码如下所示。
javascript
function treePrint(height) {
// 外层循环:控制行数(从上到下,共 height 行)
for (let i = 0; i < height; i++) {
let line = ''; // 当前行的内容
let totalWidth = 2 * height + 1; // 每行的总宽度(固定值)
let center = height; // 中心点位置(固定值)
// 内层循环:从左到右遍历每个位置
for (let j = 0; j < totalWidth; j++) {
// 判断当前位置 j 是否在星号的范围内
// 第 i 行:星号从 (center - i) 到 (center + i)
if (j >= center - i && j <= center + i) {
line += '*'; // 在范围内:打印星号
} else {
line += '~'; // 在范围外:打印波浪号(背景填充)
}
}
console.log(line); // 输出当前行
}
}
3. PHP 部分
3.1 比较数组
这里的第一道题难度也相对比较简单。
我们需要使用 PHP 写一个名为 compareArrays 的函数,其接受 arr1,arr2 两个数组变量,这个函数会在内部计算这两个数组里面的数字乘积,然后打印告诉用户哪一个数组更大。
如果第一个数组结果更大,打印 "Array 1 is larger";第二个更大,打印 "Array 2 is larger";两个一样大,打印 "Both are equal"。
这个的算法就很简单,我们用两个临时变量计算这里两个数组乘积的结果。由于是乘法,因此这两个临时变量用 1 而不是 0。我们这里如果选择直接选取数组的第一个元素作为这个临时变量的值还需要判断是否是空数组,因此比较麻烦,直接用 1 作为临时变量,然后用 foreach 循环将数组里的每一项乘上去,然后根据结果进行最后的判断与输出即可。
我们可以参考考试前的测试代码,这个代码演示了如何判断一个数组的和是偶数还是奇数。
php
function sumEvenNumbers($arr) {
// here is the sample solution in PHP
// use echo to output results
// delete this line to run check
$sum = 0;
$found = false;
foreach ($arr as $num) {
if ($num % 2 == 0) {
$sum += $num;
$found = true;
}
}
if ($found) {
echo "Sum of even numbers: " . $sum;
} else {
echo "No even numbers found";
}
}
因此这一道题的代码如下所示。
php
function compareArrays($arr1, $arr2) {
// 1. 初始化两个乘积结果(从1开始,因为乘法的单位元是1)
$result1 = 1;
$result2 = 1;
// 2. 计算第一个数组所有元素的乘积
foreach ($arr1 as $num) {
$result1 *= $num; // 等价于 $result1 = $result1 * $num;
}
// 3. 计算第二个数组所有元素的乘积
foreach ($arr2 as $num) {
$result2 *= $num;
}
// 4. 比较两个乘积的大小,输出结果
if ($result1 > $result2) {
echo "Array 1 is larger";
} elseif ($result2 > $result1) {
echo "Array 2 is larger";
} else {
echo "Both are equal";
}
}
2.2 向数据库添加产品分类
这一道题让我们用 PHP 写一个函数add_product_category(name, priority, $parent_id) 可以让我们往 product_category 表里新增一条商品分类记录。
数据库示例如下,其包含 3 张表:
- product_category:商品分类表
- spu:商品信息表
- sku:具体库存单位表
其中这操作的是 product_category 这张表。
函数里的参数定义如下: - $name:分类名称
- $priority:分类优先级
- $parent_id:父分类 ID
这些都是 product_category 这张表里的属性。
需要实现的逻辑其实很简单,就是在插入之前,先去 product_category 表里检查,如果已经存在同名分类,就不要插入,返回"Category exists already";如果不存在同名分类,就执行插入,返回 TRUE。
这道题的题设看着复杂,但其实我们只需要使用 product_category 这一张表。因此难度并不大,只需要完成进行查询,处理对应的查询结果即可。
查询语句如下。
sql
SELECT * FROM product_category WHERE name='$name'
示例代码如下。
php
function add_product_category($name, $priority, $parent_id) {
global $con; // 引入全局数据库连接对象(通常是 PDO 或 mysqli)
// 1. 检查是否已存在同名分类(SQL 注入漏洞!)
$sql_check = "SELECT * FROM product_category WHERE name='$name'";
$query = $con->query($sql_check);
$results = $query->fetchAll();
$count = count($results);
// 2. 如果不存在,则插入新分类(SQL 注入漏洞!)
if ($count == 0) {
$sql_insert = "INSERT INTO product_category (name, priority, parent_id)
VALUES ('$name', $priority, $parent_id)";
$con->exec($sql_insert);
return TRUE; // 添加成功
} else {
return "Category exists already"; // 分类已存在
}
}
但其实这里会遇到 SQL 注入问题,因为这段代码直接把用户输入拼接到 SQL 语句中。
如下面的代码为例。
php
// 恶意输入
$name = "'; DROP TABLE product_category; -- ";
// 实际执行的 SQL 变成:
$sql_check = "SELECT * FROM product_category WHERE name=''; DROP TABLE product_category; -- '";
// 这会删除整个产品分类表!
因此可以使用预处理语句。
php
function add_product_category($name, $priority, $parent_id) {
global $con;
// 安全的查询方式(预处理语句)
$sql_check = "SELECT * FROM product_category WHERE name = ?";
$stmt = $con->prepare($sql_check);
$stmt->execute([$name]);
$results = $stmt->fetchAll();
if (count($results) == 0) {
// 安全的插入方式
$sql_insert = "INSERT INTO product_category (name, priority, parent_id)
VALUES (?, ?, ?)";
$stmt = $con->prepare($sql_insert);
$stmt->execute([$name, $priority, $parent_id]);
return TRUE;
} else {
return "Category exists already";
}
}
2.3 查书本信息
同样与数据库有关,需要我们写一个 PHP 函数getBookInfo($bookID),其能根据传入的书籍 ID,去数据库里查这本书的信息,并把书名和作者名输出出来。
有两张表:
authors
- author_id
- author_name
books - id
- title
- author_id_fk
这里books.author_id_fk 对应 authors.author_id,也就是一本书通过 author_id_fk 找到对应作者。
这里需要用 JOIN 把这两张表关联起来,关联条件就是:books.author_id_fk = authors.author_id。
如果找到了这本书,就输出 "Title: [title] | Author: [author_name]";如果没找到这本书,就输出 "Book not found"。
这个函数没有返回值。
这道题也不是很难,其实和上一道题类似。
我们只需要会写这里的 SQL 查询语句就能几乎完成这里的逻辑。
查询语句如下。
sql
SELECT books.title, authors.author_name
FROM books
JOIN authors ON books.author_id_fk = authors.author_id
WHERE books.id = '$bookID'
这里从 books 表里取 title,从 authors 表里取 author_name,两张表通过books.author_id_fk = authors.author_id连接起来,然后查找books.id = '$bookID' 的那本书。
接下来我们将得到的结果按照要求进行输出即可。
代码示例如下。
php
function getBookInfo($bookID) {
global $con; // 引入全局数据库连接对象
// 1. 构建 SQL 查询语句(关联查询 books 和 authors 表)
$sql = "SELECT books.title, authors.author_name
FROM books
INNER JOIN authors ON books.author_id_fk = authors.author_id
WHERE books.id = $bookID";
// 2. 执行查询
$query = $con->query($sql);
$results = $query->fetchAll();
$count = count($results);
// 3. 判断是否有结果
if ($count > 0) {
// 遍历并输出每本书的标题和作者
foreach ($results as $result) {
echo "Title: " . $result['title'] . " | Author: " . $result['author_name'];
}
} else {
echo "Book not found"; // 没有找到该书
}
}