资讯动态

算法入门第一步:理解程序结构,掌握顺序选择循环

发布时间:2026/10/20 4:46:09 来源:尧图企业网站定制
最近总有刚入门的朋友跑来问我算法到底怎么学是不是先把题单刷一遍再把常用模板背下来就行我基本都是先拦一下然后反问一句你写代码的时候有没有想过程序本身是怎么一步一步走完的大多数人的回答都是愣一下然后说没想过。这就是问题所在。我自己的体会是算法入门最要紧的不是记住某个题的解法而是先把程序结构这层骨架打牢。算法说白了就是用程序表达解决问题的步骤你连顺序、选择、循环这三种基本结构都没吃透后面看再多的题解都是空中楼阁。这篇文章就是系列第一篇专门聊程序结构。没有高深数学不贴复杂模板只把三种基本结构拆开揉碎再用几个入门的算法例子告诉你它们是怎么配合的。适合刚接触编程、或者写代码总感觉逻辑乱成一团的人。1. 为什么算法入门要先研究程序结构而不是直接刷题1.1 算法不是算数是一份能执行的菜谱很多人一听到算法两个字脑子里蹦出来的是数学公式、矩阵、高数符号于是下意识觉得自己数学不好就学不了算法。这是个很大的误解。算法的经典定义是解决某个问题的有限、明确、有效的步骤序列。这句话里没有一个字提到数学。你想想哪怕是把大象关进冰箱这种玩笑式的步骤也勉强算一个流程设计只是不够严谨而已。更生活化的类比是做菜。一道菜从洗菜、切菜、下锅、调味到出锅每一步都有先后顺序有的步骤要看情况比如盐放少了就加点有的步骤要重复比如翻炒到变色。这其实就是算法。计算机就是一个特别较真、又没有常识的厨子你给它什么步骤它就一丝不苟地执行什么步骤。程序结构就是组织这些步骤的方式。所以算法入门第一课不是背题型而是学会把脑子里的想法翻译成计算机能明白的步骤。这个翻译能力核心就是程序结构。1.2 三种结构就够用结构化编程定理的底气这里有个特别重要的结论来自结构化编程理论任何可计算的问题都可以只用顺序、选择、循环三种基本控制结构来表达。简单说顺序就是照着顺序做选择就是看条件决定做哪个循环就是重复做到满足条件为止。不容易理解可以这么记程序的世界里只有这三块积木剩下的都是这三块的组合和嵌套。知道了这个定理学程序结构就有了底气。你不用去背几十种所谓的模板因为本质上所有复杂算法最后都能拆成这三种结构。你在网上看到的各种花哨解法剥掉外壳以后里面就是反复的先判断再循环再赋值。1.3 读这篇文章需要什么基础几乎不需要什么基础。会定义变量、会写简单的输入输出就够了。下面我用 Python 来演示因为它的写法最接近自然语言适合理解结构。但顺序、选择、循环这套逻辑放到 C、Java、Go 里是一样的只是关键字和写法略有不同。你看懂了结构换成别的语言只是查个语法的事。2. 顺序、选择、循环三种基本结构是怎么工作的2.1 顺序结构一句一句往下走顺序结构是最朴素的程序从第一行开始一句接一句地执行直到最后一行。很多时候你觉得写代码难其实难的是顺序错了结果就全错了这件事。举个例子交换两个变量 a 和 b。很多新手直接写a 10 b 20 a b b a print(a, b) # 输出 20 20交换失败了为什么失败因为赋值语句是先算右边再存到左边。执行到第三行时a 已经变成了 20原来的 10 已经找不回来了。正确的做法是引入一个临时变量a 10 b 20 temp a # 先把 a 的值存到 temp a b # 把 b 的值放进 a b temp # 再把原来的 a 放进 b print(a, b) # 输出 20 10这类问题的本质就是顺序结构里每一步都会改变程序状态这个事实。状态这个词听着抽象你可以把它理解成目前所有变量长什么样。每一步执行完这个状态都会更新。顺序结构就是不断更新状态的过程。很多隐蔽的 bug都是因为中间某一步的先后顺序没想清楚。2.2 选择结构给程序装上岔路口遇到需要判断的情况就要用到选择结构。最基础的就是if / elif / else。看这个例子score 78 if score 90: print(优秀) elif score 60: print(及格) else: print(不及格)三个点要注意。第一判断条件是按顺序从上往下匹配的谁先满足就执行谁后面不再看。第二边界条件要抠清楚 90和 90差了一个值直接影响 90 分这个档位归到哪边。第三elif其实是否则如果的缩写它隐含了前面的条件不成立这个前提。所以上面代码里score 60实际是在score 90的前提下判断的。选择结构还有一种常见情况是逻辑组合。比如判断某年是不是闰年条件是能被4整除但不能被100整除或者能被400整除。可以直接写成year 2024 if (year % 4 0 and year % 100 ! 0) or (year % 400 0): print(闰年) else: print(平年)这里and和or要注意短路求值and只要左边为假就不再算右边or只要左边为真就不再算右边。这既是性能优化也能避免一些边界错误。你写条件的时候最好先问自己一句这个条件的边界到底要不要包含等于2.3 循环结构把重复劳动承包出去循环解决的是重复问题。for适合知道要循环多少次的场景while适合不知道多少次只知道什么时候该停的场景。比如从 1 加到 100用 for 写total 0 for i in range(1, 101): total i print(total) # 5050这里range(1, 101)是左闭右开区间包含 1 不包含 101。这个约定是很多边界 bug 的源头后面专门讲。用 while 写同样的事total 0 i 1 while i 100: total i i 1 print(total)while 循环有一个非常重要的点循环条件一定要在循环体里朝着变假的方向前进。上面代码里i 1就是那个前进的步一旦漏掉i 100永远为真程序就死循环卡住了。不少新手会纠结到底用 for 还是 while。我的建议很粗暴能确定次数就用 for次数不确定就用 while。另外break可以提前跳出整个循环continue可以跳过本次循环的剩余部分。这两个都是控制循环节奏的工具但别滥用用多了代码会像迷宫一样难读。3. 组合的力量嵌套、函数封装和高频结构模式3.1 嵌套结构里还可以再套结构三种基本结构之所以够用是因为它们可以嵌套。循环里套判断判断里再套循环这种组合能把复杂问题表达出来。举个最简单的例子输出 100 以内的所有质数。外层循环负责一个一个检查数字内层判断负责检查这个数字是不是质数for num in range(2, 101): is_prime True for i in range(2, num): if num % i 0: is_prime False break if is_prime: print(num)这段代码就是循环套判断、判断里又有循环的典型。每多一层嵌套逻辑复杂度就上一个台阶。所以我一直建议写嵌套之前先在纸上画出层次外层管什么内层管什么哪个变量是外层控制哪个是内层控制。画清楚了代码基本不会乱。嵌套还会引出一个初级概念时间复杂度。上面这段代码的外层跑 99 次内层最多跑 num-2 次总操作量大致是 n 的平方级别。这个概念现在不用深究但你要有体感嵌套层数越多数据量一大就越慢。以后学算法优化很多时候就是在想办法减少嵌套层数。3.2 函数封装把结构打包成语义块顺序、选择、循环是控制结构函数是另一种层面的结构它是把一段逻辑打包成一个有名字的积木。函数的本质是子程序一次定义多处调用。继续用质数判断举例。上面那段主代码就有点拥挤如果把判断一个数是不是质数抽成函数def is_prime(n): if n 2: return False for i in range(2, n): if n % i 0: return False return True for num in range(2, 101): if is_prime(num): print(num)主逻辑一下就清爽了。函数让你不必每次都去重复那些判断细节也方便以后直接复用。很多算法题解里写的所谓套路其实就是把某些常见的结构组合封成了一个函数比如排序函数、查找函数。写函数的时候注意两点。第一边界输入一定要处理比如上面的n 2必须返回 False否则 1 会被误判成质数。第二一个函数最好只做一件事不要既求平方又做排序那样别人看不懂你自己过几天也会忘。3.3 算法题里反复出现的小结构模式刷题不多的人可能意识不到很多题目拆开以后用的都是同样的几套结构模式。我列几个高频的模式结构特征典型场景累加器循环体里total 当前值求和、求均值、多项式计算计数器循环体里count 1统计满足条件的个数标记变量定义一个布尔变量循环里根据情况修改判断是不是质数、是不是全对哨兵值用特殊值表示结束或未找到读数据直到遇到某个结束标志极值追踪循环里不断比较更新最大/最小值求数组最大值、最小值这些模式不是背诵的模板而是你写代码时自然会浮现的肌肉记忆。我在带新人时都会说哪怕你现在只会这三种基本结构只要把累加器、计数器、标记变量这几个小模式练熟已经能解决一大批入门算法题了。关键是理解每个模式里变量如何随着循环更新这是程序结构的核心动作。4. 四个入门题拆解看程序结构如何真正落地4.1 例1累加求和——顺序循环的主场从 1 加到 n。核心是累加器模式n int(input()) total 0 for i in range(1, n 1): total i print(total)这个题虽然简单但有两个值得抠的细节。第一range(1, n 1)用到了左闭右开很多人写成range(1, n)导致少加了最后一个数。这种 bug 不靠眼睛看靠的是你明确知道区间规则。第二如果 n 很大其实有等差数列公式n * (n 1) // 2这是算法优化的思路同一个结果可以用更少的步骤算出来。我建议入门者动手跑一下这个题并且在循环里加一行print(i, total)观察累加器每一步怎么变。亲眼看到状态更新比背十遍概念都有用。4.2 例2判断质数——循环选择提前退出判断是不是质数是练习循环和选择的经典题def is_prime(n): if n 2: return False i 2 while i * i n: if n % i 0: return False i 1 return True这里有个优化点不需要遍历到 n只需要到sqrt(n)。为什么因为如果一个数能被分解成 a × b那么必然有一个因子小于等于 sqrt(n)。举个例子36 一旦能被 2 整除那就不用再检查 18、12 了。这样循环次数从 n 降到了 sqrt(n)数据一大差距非常明显。另外注意i * i n这个条件。有人会写成i n结果更慢但功能对有人会写成i * i n这样会把平方数误判。比如 49i 到 7 时7 × 7 49进去就能发现 49 % 7 0改成就漏掉了。这种边界差一个符号的问题必须靠手动代入一个具体数去走查才能发现。4.3 例3整数倒序——循环取模除法的配合给一个正整数比如 12345输出 54321。这道题很考验对整除和取模的理解n int(input()) result 0 while n 0: digit n % 10 result result * 10 digit n n // 10 print(result)走查一遍n12345取个位 5result 变成 5n 变成 1234再取个位 4result 变成 54n 变成 123…… 每一步n 都在往 0 走循环最终必停。这个例子里n n // 10就是那个让循环条件朝假前进的关键步骤。你可以试试如果少写这一行程序会无限循环输出一串 5。这种题练的不是记住解法而是让你感受循环体里每一行都在为下一次循环和循环退出做准备。程序结构的优雅之处就在这里。4.4 例4二分查找雏形——循环两路分支的边界收缩这个例子稍微进阶但特别能说明选择循环怎么配合。假设数组已经升序排列我们要找一个目标值的位置。二分查找的思路是每次都看中间元素等于目标就返回比目标大说明目标在左半边比目标小说明在右半边。def binary_search(arr, target): left, right 0, len(arr) - 1 while left right: mid (left right) // 2 if arr[mid] target: return mid elif arr[mid] target: left mid 1 else: right mid - 1 return -1这里最容易出问题的是边界为什么是left right而不是为什么更新时是mid 1和mid - 1而不是mid我直接告诉你结论因为mid已经判断过了不在目标区间里所以要跳过它。如果写成right mid当区间缩小到两个元素时可能永远卡住造成死循环。这个例子背后的结构是循环 两路分支 边界收缩它几乎是一切高级查找和搜索算法的基础。现在不用急着深究答案但你可以把这个代码抄下来手动拿[1, 3, 5, 7]去走查每一步 left、right、mid 的变化。纸上走查三遍比看十遍视频都管用。5. 边界的魔鬼入门期最常踩的坑和排查方法5.1 最隐蔽的坑边界值差一个代码逻辑本身没写错但结果就是差一个数这是入门阶段最常见的挫败来源。典型场景有三个range的结束值该不该加 1、判断条件用还是、数组下标拿n还是n-1。这种问题的根源是程序里大量使用左闭右开或闭区间的约定而人脑默认用的是自然语言的包含。比如range(0, 5)包含 0 不包含 5可你嘴上说从 0 到 5下意识就以为有 5 个数。我的排查方法是一旦发现边界相关的 bug立刻把具体数字代进去手动算第一遍、最后一遍看输出落在哪里。绝大多数情况算一遍就找到了。5.2 死循环程序永远跑不完死循环是循环结构独有的坑。最常见的三种原因循环变量忘了更新更新方向反了导致离退出条件越来越远边界条件写错比如while i n但 i 每次加 2n 是奇数时永远追不上。遇到程序卡住不要干瞪眼直接在循环体里加打印语句把循环变量和关键结果打出来i 0 while i 10: print(i) # 看看 i 到底怎么变 # i 1 # 假设这句被注释掉了这一步会立刻告诉你循环变量有没有在动、往哪个方向动。打印中间量是我最推荐的调试方式因为它不需要任何高级工具而且能帮你建立程序逐行执行的直觉。等以后你熟练了再用调试器也不迟。5.3 调试的内功先在纸上走查一遍很多新手代码逻辑乱本质上是还没想清楚就开始写。我的建议是写代码之前先画出程序结构的草稿。这个草稿不需要正规流程图用方框和箭头就行先按顺序做哪几步哪个地方要分支哪个地方要循环循环从哪里开始到哪里结束。大概 30 秒就能画完但能帮你避开绝大多数逻辑混乱。画完之后再对着草稿用一个小数据走查一遍。比如判断质数的代码就拿 n2、n9、n49 各走一遍。这个过程看起来很笨但却是建立程序会在哪一步出问题直觉最快的方法。我带过的很多初学者一开始觉得走查浪费时间后来都承认走查五分钟省下调试两小时。5.4 先拆输入、处理、输出再谈结构还有一个更高层的习惯特别想推荐给你拿到任何题目先别想用什么算法先回答三个问题——输入是什么要做的处理是什么最后的输出是什么把这三个问题写清楚程序结构基本就浮出水面了。比如翻转整数输入 12345输出 54321。输入是整数输出是整数那处理过程里你就需要不断取个位再不断拼到结果上这就是循环 取模整除。再比如判断一个数是否是质数输入是整数输出是布尔值直接对应选择 循环。输入决定数据从哪里来输出决定最终目标处理过程决定你使用哪种结构组合。这三步想清楚你甚至能把一个中等难度的题拆成若干个小块每块都是顺序、选择、循环的熟悉套路心理压力会小很多。最后说一点个人的体会我在自己刚学算法的时候也犯过疯狂刷题但收效甚微的毛病。那时候的典型状态是看了题解觉得懂了合上答案自己一写就漏洞百出。后来我才意识到缺的不是解题技巧而是对程序结构本身的敏感度。一个程序从输入到输出中间每一条赋值、每一次判断、每一轮循环都在改变状态你能不能在脑子里运行这段代码才是基本功。现在带新人我几乎不会一上来就让他们刷题。我会让他们先把顺序、选择、循环这三种结构写熟把累加器、计数器、标记变量这些小模式练成本能再手把手拆几个经典题。这个过程虽然朴素但后劲很足。这套方法希望你也能试一试。这系列接下来我打算从数组和链表这些基础数据结构聊起说说它们跟程序结构是怎么配合的。如果你看完这篇有什么困惑最有效的做法不是收藏而是把文里的每段代码自己敲一遍加上几行打印观察它每一步的状态变化。代码这东西看十遍不如跑一遍。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑