章节:算法设计 — 你的解难指南!

同学们好!欢迎来到信息及通讯技术(ICT)其中一个最重要的课题:算法设计。别被这个听起来很「深奥」的名字吓倒。如果你曾跟着食谱煮菜,或者给朋友指路,那么你其实已经应用了算法!

在这一章,我们将学习如何像计算机科学家一样思考。我们会把问题拆解成计算机能够理解的简单逻辑步骤。这项技能超级有用,不仅限于编写程序,更能帮助你解决生活中的各种问题。准备好成为解难高手了吗?让我们开始吧!


算法到底是什么?

算法(Algorithm)是为了解决特定问题或执行某项任务而设计的有限、按部就班的指令集。把它想象成一个完美的食谱。

比喻:烘焙蛋糕
想象你手上有一个巧克力蛋糕的食谱。

  • 问题:你想做出一个美味的巧克力蛋糕。
  • 输入:面粉、糖、鸡蛋和可可粉等材料。
  • 算法:食谱中按部就班的指示(1. 预热烤箱。2. 混合面粉和糖。3. 加入鸡蛋...等等。)。
  • 输出:完成的巧克力蛋糕!

一个好的算法,就像一个好的食谱一样,必须清晰、精确,并有一个明确的终点。你不能有一个步骤写着「加一点糖」——它需要明确地写着「加 200 克糖」。

重点提示

算法只是一个清晰的计划或一套解决问题的规则。它是我们在开始编写程序之前所撰写的「操作指南」。


表达算法:编写程序的蓝图

在建造房屋之前,建筑师会绘制蓝图。同样地,在我们编写程序之前,我们也会先制定计划。我们将学习两种主要方法来实现这一点:伪代码(Pseudocode)流程图(Flowcharts)

1. 伪代码(Pseudocode)

伪代码是一种将算法以结合日常英语和类似程序语言的语句写出来的方法。它没有严格的语法规则,因此你可以专注于逻辑,而无需担心完美的语法。

例子:一个找出两个数字平均值的算法。

输入 数字1
输入 数字2
总和 = 数字1 + 数字2
平均值 = 总和 / 2
输出 平均值

看吧?它简单易读,清楚地展示了步骤。

2. 程序流程图(Program Flowcharts)

流程图是算法的图形表示。它使用由箭头连接的标准符号来显示逻辑流程。这对于将过程可视化非常有用。

常见流程图符号:
  • 终端(椭圆形):用于算法的「开始」和「结束」点。
  • 输入/输出(平行四边形):用于从用户获取输入或显示输出。
  • 处理(长方形):用于任何计算或操作,如加法或赋值。
  • 判断(菱形):用于提出问题(通常有「是」或「否」的答案)。这是算法分支的地方。
  • 流程线(箭头):连接符号并显示流程方向。

例子:与上面「两个数字平均值」算法相同的流程图。

(开始)--> [输入 数字1] --> [输入 数字2] --> [总和 = 数字1 + 数字2] --> [平均值 = 总和 / 2] --> [输出 平均值] --> (结束)
(想象这些是正确形状并由箭头连接!)

快速回顾

伪代码:以简单、有结构的语言写出来。
流程图:使用标准符号绘制出来。


基本构件:数据类型与数据结构

为了解决问题,我们需要处理数据。但计算机需要知道它正在处理什么类型的数据。它是一个数字?一个词汇?一个真/假值?这就是数据类型(Data Types)的用武之地。

简单数据类型

课程大纲要求我们掌握四种简单类型:

  • 整数(Integer):没有小数点的正数或负数。例子:10, -45, 0, 12345
  • 实数(Real):带有小数点的数字。例子:99.9, 3.14, -0.05
  • 字符(Character):单一字母、数字或符号,以单引号括起来。例子:'A', 'h', '7', '$'
  • 布尔值(Boolean):表示一个真值。它只能是两者之一:真(True)假(False)。把它想象成一个开关,只能是开或关。

布尔逻辑(真/假值的力量)

我们可以使用逻辑运算符组合布尔值。你需要知道的三个是AND(与)OR(或)NOT(非)

  • AND(与):只有当两个条件都为真时,结果才为真。
    例子:「如果你吃完蔬菜清洁你的房间,你就可以得到甜品。」(你必须完成两项!)
  • OR(或):如果至少一个条件为真,结果就为真。
    例子:「如果我考试得到 A B,我就会很高兴。」(任何一个都足够了!)
  • NOT(非):反转真值。NOT 真 变为 假,NOT 假 变为 真。
    例子:如果「isRaining」(正在下雨)为真,那么「NOT isRaining」(没有下雨)就为假。

我们经常使用真值表(truth tables)来查看所有可能的结果:

AND 的真值表

A AND B 为真吗?

A=真, B=真 --> 真
A=真, B=假 --> 假
A=假, B=真 --> 假
A=假, B=假 --> 假

OR 的真值表

A OR B 为真吗?

A=真, B=真 --> 真
A=真, B=假 --> 真
A=假, B=真 --> 真
A=假, B=假 --> 假

简单数据结构

有时我们需要将数据组合起来。这就是数据结构的作用。

  • 字符串(String):一连串的字符。基本上,任何文字、单词或句子。
    例子:「Hello World」、「HKDSE ICT」、「Student123」
  • 一维数组(One-Dimensional Array):一列相同数据类型的项目。想象它像一个编号的储物柜列表或一个鸡蛋纸盒。每个项目都有一个位置(称为索引),通常从 0 开始。
    例子:一个名为 `scores` 的数组,用于储存 3 个考试成绩:`[95, 88, 72]`。这里,`scores[0]` 是 95,`scores[1]` 是 88,依此类推。
重点提示

选择正确的数据类型(整数、实数等)和结构(如数组)是设计一个良好算法的至关重要的第一步。


控制流程:三种基本结构

令人惊讶的是,任何算法,无论多么复杂,都可以仅使用三种基本控制结构来构建。让我们掌握它们吧!

你知道吗?这个概念是「结构化程序定理」的一部分,这是计算机科学中的一个基本原则,它证明了这三种结构足以解决任何可计算问题!

1. 顺序结构(Sequence)

这是最直接的结构。指令按照它们书写的顺序一个接一个地执行。没有步骤被跳过,也没有分支。
我们的「两个数字平均值」算法就是一个完美的顺序结构例子。

2. 选择结构(Selection)

这与做出选择有关。算法根据条件是真还是假来遵循不同的路径。这是通过 IF-THEN-ELSE(如果-那么-否则)逻辑来完成的。

  • 二元选择(IF-ELSE):有一个条件和两个可能的路径。
    伪代码例子:
    输入 分数
    如果 分数 >= 50 那么
        输出 「合格」
    否则
        输出 「不合格」
    结束如果
  • 多重选择:当有两个以上可能的路径时使用。你可以使用多个 IF-ELSEIF 语句来实现。
    伪代码例子:
    输入 分数
    如果 分数 >= 80 那么
        输出 「A 级」
    否则如果 分数 >= 60 那么
        输出 「B 级」
    否则
        输出 「C 级」
    结束如果

3. 重复结构(Iteration,即循环)

这是用于重复执行一组指令多次。这也称为循环(looping)
比喻:想象你要洗 10 个碗碟。你对每个碗碟重复相同的过程(擦洗、冲洗)。这就是一个循环!

伪代码例子:一个循环,用于打印数字 1 到 5。
对于 计数器 从 1 到 5
    输出 计数器
结束对于

这将输出:1, 2, 3, 4, 5。

重点提示

每个算法都是顺序结构(按顺序做事)、选择结构(做出选择)和重复结构(重复做事)的组合。


测试你的算法:它会奏效吗?

建立算法很棒,但我们如何知道它是正确的呢?我们不能仅仅抱持着『但愿成功』的想法;我们必须积极地找出错误。

手动追踪与追踪表

手动追踪(Dry run)是使用一组测试数据,手动逐步执行你的算法,假装你就是计算机。组织手动追踪的最佳方式是使用追踪表(trace table)

追踪表是一个表格,它追踪算法中每个变量在每个步骤的值。这绝对是找出逻辑错误最强大的工具!

循序渐进的例子:使用追踪表

算法目的:找出列表中最大的数字。列表为 `[5, 8, 3]`。

伪代码:
1. 数字列表 = [5, 8, 3]
2. 最大值 = 数字列表[0]
3. 对于 i 从 1 到 2
4.     如果 数字列表[i] > 最大值 那么
5.         最大值 = 数字列表[i]
6.     结束如果
7. 结束对于
8. 输出 最大值

追踪表:
| 步骤 | i | numbers[i] | 条件:numbers[i] > largest | largest |
| :--- | :-: | :---: | :---: | :---: |
| 1-2 | - | - | - | 5 |
| 3 | 1 | 8 | 8 > 5 为真 | 5 |
| 4-5 | 1 | 8 | 真 | 8 |
| 3 | 2 | 3 | 3 > 8 为假 | 8 |
| 4 | 2 | 3 | 假 | 8 |
| 7 | - | - | - | 8 |
| 8 | - | - | - | 输出:8 |

透过追踪这些值,我们可以清楚地看到 `largest` 是如何从 5 变成 8 的,并且我们可以确认最终输出是正确的。

找出逻辑错误

逻辑错误(logic error)是算法设计中的错误,即使程序可以运行而不会崩溃,它也会产生不正确的结果。
例子:如果我们在条件中写成 `numbers[i] < largest`,追踪表就会向我们展示算法正在寻找最小的数字,而不是最大的。

常见的错误要避免

要小心循环计数器和数组索引!一个常见的错误是「差一」错误("off-by-one" error),即循环多运行一次或少运行一次。追踪表非常适合捕捉这种错误!


构建大型项目:模块化的力量

如果你有一个非常大、复杂的问题,例如构建一个完整的应用程序,该怎么办?尝试将所有内容写成一个巨大的算法将会是一场噩梦!

解决方案就是模块化(modularity)。这意味着将一个大问题分解成更小、更简单、更易于管理的小问题,称为模块(modules)

比喻:制造汽车
你不会一次性地制造一整辆汽车。你会为引擎、车身、车轮和电子设备等设置不同的团队。每个部分都是一个模块。它们会被独立地制造和测试,最后再组装起来。

模块化的优点:
  • 更容易理解:小模块比一大块代码更容易阅读和管理。
  • 更容易测试和调试:你可以单独测试每个模块,确保它完美运行,然后再将其与其他模块组合。
  • 可重用:你经常可以在许多不同的程序中重复使用一个模块(例如用于计算税款的模块)。
  • 团队合作:不同的程序员可以同时处理不同的模块。
重点提示

对于任何复杂的问题,总是思考:「我如何将其分解成更小的部分?」这种模块化的方法是成功解决问题的关键。