ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

算法入门第一课:什么是算法?从特征到经典算法实战

算法入门第一课:什么是算法?从特征到经典算法实战 算法这个词这几年快被说烂了。大厂面试要考算法竞赛要刷算法连做数据分析、写点自动化脚本都得懂点算法。但你要是真去问一个刚入门的同学算法到底是什么十有八九会得到这样的回答算法就是LeetCode上那些题或者就是快速排序、动态规划那堆代码。我在带新人的这几年里发现能把这个问题讲清楚的人真不多。我自己当年也踩过这个坑——刷了一百多道题背了不少模板等到面试官问了一句“你这个解法为什么比那个快”一下就愣住了。其实算法不是一串代码也不只是一堆公式。地图导航规划路线、短视频推荐你感兴趣的内容、搜索引擎把网页排序背后全是算法在工作。算法是解决问题的一套具体步骤是“怎么把事办成”的思考方式。这篇是算法入门系列的第一篇用大白话讲清楚算法的定义、五个特征、两个入门必会的经典算法例子再给一段能直接上手的实操路径。适合零基础想系统学算法的人也适合刷题刷得满头问号、准备回头打地基的同学。1. 为什么入门第一课要纠结“什么是算法”因为这个问题不掰扯清楚后面所有算法学习都是空中楼阁。你看着是学会了快速排序、学会了KMP但稍微变个问法就不会做根子就在这儿你把算法理解成了“代码段”而不是“思考方式”。1.1 代码只是算法的翻译不是算法本身我先打个比方。你把“宫保鸡丁”的做法用文字写下来鸡胸肉切丁、花生米炸脆、调一碗糖醋汁、起锅热油下鸡丁炒变色最后倒入料汁和花生米翻匀。这套步骤写到纸上就是一个菜谱。同一份菜谱米其林大厨照着做、食堂阿姨照着做、你回家照着做做出来的口味可能有点差异但流程是一样的——因为菜谱本身不依赖任何人。算法就是这么一张“菜谱”。它是一个解决问题的步骤序列和用哪口锅、哪个铲子没关系。同样一个“从一个数组里找到最大的数”的问题用Python写、用Java写、用C写代码完全不一样但思路都一样假设第一个数是最大的然后遍历剩下所有数遇到更大的就更新。这个思路也就是算法可以写成文字、伪代码、流程图甚至用画圈的方式表示。代码只是把思路翻译给计算机听算法才是你真正要设计和理解的东西。我刚入门的时候犯过一个错看到一道题第一反应是“这题用哪个语言能解”。后来才转过弯来算法跟语言无关。这也是面试里经常出现的考察点——面试官不会满足于你会写代码他会追问你想怎么解决时间复杂度多少为什么这样做是对的这些问题全都在问“算法”而不是问“代码”。1.2 面试和刷题真正考的是“思路”不是背代码可能有人会说既然算法不绑定语言那我刷题时候把快速排序的模板、堆排序的模板背下来不也算会算法了吗这里要先泼一盆冷水。背模板能让你写出那几段固定的代码但题目稍微变一变就露馅了。举个面试常见变种题给定一个整数数组返回两个数的下标使这两个数加起来等于某个目标值。如果你只会暴力枚举模板那就是两层循环O(n²)。但你要是只背过“两层for循环”的写法根本想不到用哈希表把时间复杂度降到O(n)。这个能力只能靠理解思路来获得背代码背不出来。所以第一篇一定要把“什么是算法”掰扯清楚。你后面看任何算法教程、任何题解第一步都是去抓它的思路框架而不是先盯代码。思路框架长什么样它就像一份提纲输入是什么输出是什么中间分几步每一步做什么。有了这个框架代码只是把框架填上细节而已。2. 判定算法的五个特征记住这把尺子教科书上给算法下过严格定义但我觉得新手不需要背定义更要紧的是知道“怎么判断一套做法算不算算法”。经典的说法是算法需要满足五个特征有输入、有输出、确定性、可行性、有限性。这五条我逐个拆开说每条都配个生活例子。2.1 有输入、有输出、每一步都确定先看输入和输出。任何算法都要说清楚它处理的是什么最后要给你什么。拿最简单的“找最大值”来说输入是一个数组输出是数组里最大的那个数。如果题目连输入都没说清楚算法根本没法设计。菜谱也一样你得先说清楚食材清单再说清楚成品是什么样。再看确定性。确定性的意思是同样的输入执行的过程和结果必须是明确、可复现的不能出现“看情况”这种模糊指令。比如“如果数字差不多大就随便挑一个”这句话里“差不多”和“随便”都没有明确定义那就不是一条合法的算法步骤。计算机程序里一旦出现未定义行为结果就不可复现。生活里也有类似场景你跟朋友说“有空咱聚聚”什么时候算有空、去哪里聚、几点见全都含糊所以这只是一句客套话不是一个可执行的方案。2.2 能执行、跑得完可行性与有限性可行性要求算法的每一步都得是能实际操作完成的不能只是理论上存在。比如“把数组里所有元素都加1然后输出”可行但“把全世界所有计算机的内存都清零”这种步骤当前条件下做不到不可行。工程上还要考虑另一层如果一个算法需要的内存比宇宙里的原子还多那从现实角度它就没有可行性。有限性要求算法必须能在有限的步骤内结束不能死循环。比如“不断输出1”如果不说什么时候停它就不是一个算法而是一个无休止的过程。写代码时忘了给循环加退出条件程序会卡死——在算法层面这叫缺乏有限性。一个合格的算法必须保证对任意合法输入都在有限步内给出结果。我给新人的建议是拿到任何一个算法描述先用这五条过一遍。没有输入输出先帮忙补上看到模糊指令要追问清楚如果它跑不完那就有bug。这套检查动作做顺手了你看算法题的速度会快很多而且不容易跑偏。3. 两个入门必会的经典算法查找与排序的直觉前面讲的都是概念这一节上点具体的东西。算法入门绕不开两座大山查找和排序。这两个不会后面几乎所有复杂算法都施展不开。我把它们单独拿出来讲不是要你背代码而是要你建立“直觉”。3.1 二分查找从猜数字到折半搜索先来一个生活场景。朋友让你猜他脑子里想的数字范围是1到100每次只能问“是大了还是小了”你要猜几次才能中最笨的办法是从1开始猜1不对2不对3不对……如果要猜的数字是100得猜100次。但如果换个策略先猜50对方说大了说明答案在1到49之间再猜25对方说小了答案在26到49之间继续折半最多7次就能锁定答案。这就是二分查找的核心直觉。放到数组里也一样。假设有序数组是 [2, 5, 7, 9, 13, 18, 20]我们要找13。先看中间位置下标是3内容是9。9比13小说明目标只可能在后半段于是把范围缩到下标4到6再看中间下标5内容是1818比13大把范围缩到下标4到4最后看到下标4恰好是13返回4。整个过程只看了三个数这就是二分查找的威力它的时间复杂度是O(log n)。n翻一倍它只多走1步n翻十倍它才多走3步多。但这里有一个必须记住的前提数组必须有序。数组无序时二分没法用因为你不能保证“中间数比目标值大目标就一定在前半段”。这个前提很多人会忽略面试一紧张就忘。我建议你学这个算法时拿笔纸把1到16写成一排亲手模拟“区间不断减半”的过程走上几遍比看十遍代码都管用。后面你学二叉搜索树、平衡树本质上都是在复用这个“折半”思想。3.2 冒泡排序为什么它叫“冒泡”又为什么不高效排序算法是另一个必练项目冒泡排序是最直观的一个。给你一个无序数组 [5, 3, 8, 6, 2]你想从小到大排。冒泡的思路是从前往后两两比较相邻的元素如果前一个比后一个大就交换它们。这样一轮下来最大的数会像气泡一样“冒”到数组最末尾。具体走一遍。第一轮5和3比交换数组变成 [3, 5, 8, 6, 2]5和8比不换8和6比交换变成 [3, 5, 6, 8, 2]8和2比交换变成 [3, 5, 6, 2, 8]。第一轮结束8已经到末尾。第二轮对前四个数重复6又沉到倒数第二的位置。几轮之后数组就整齐了。写冒泡排序时你会有很直观的感觉每一轮都有一个数字被推到了它最后该待的位置像气泡从水底浮上来。为什么说冒泡排序适合入门因为它贴切人类直觉代码也好写。但它是不高效的排序方式两层循环嵌套时间复杂度是O(n²)。数组只有几十个元素时看不太出来数据到一万、十万就能明显感觉到它“慢”了。这也是算法分析的现实意义——同样的活儿换个做法性能差几个数量级。学的时候还可以多想一步如果某一轮没有任何交换发生说明数组已经有序了可以提前结束。你能主动想到这个优化点说明你开始有算法优化的意识了。4. 算法和数据结构的顺序问题别被“先学什么”卡住新手学算法经常纠结我是不是得先把数据结构全部学完再开始学算法还有人一上来就背“数组、链表、栈、队列、树、图”的定义背完就忘回头还是不会做题。这个顺序问题我直接说结论不要等两者可以同步学但要有主次。4.1 数据结构是食材仓库算法是做菜流程我用厨房打个比方。数据结构就像厨房里的各种容器和食材柜数组是一个带编号的小格子柜链表是一条串起来的链条栈是一摞盘子只能从最上面拿队列是排队打饭的队伍先进先出。算法则是你要做的事把食材挑出来、切好、下锅炒熟。你不可能等把全世界的锅碗瓢盆全买齐了才开始做饭正常做法是手里有什么就用什么缺哪个补哪个。入门阶段通常只需要数组就够用了。查找、排序、递归、动态规划的入门题绝大多数都能在数组上完成。链表、栈、队列、树、哈希表这些结构等遇到具体问题再学效果反而更好。比如你想快速判断一个元素在不在集合里用数组一个个找太慢这时候你自然会想“有没有更快的办法”哈希表就在这个需求下登场你也记得更牢。面试中那些KMP、Tarjan、匈牙利算法名字唬人学起来也都不需要先堆砌完所有数据结构而是按“先读问题、再选容器、再用算法解决”的流程走。所以我的建议是数据结构跟着算法题走别在开头死磕。4.2 零基础也能学算法纸笔演算和画流程图的实操方法另一种常见说法是我连代码都不会写怎么能学算法呢其实学算法和学编程语言可以解耦。你完全可以用文字、伪代码、流程图来学习和设计算法不需要先精通一门语言。我自己带过没写过一行代码的同学做法很朴素先拿纸笔画流程图。比如二分查找他把“取中间数、比较、缩小范围”几个方框画出来用箭头连起来反复走几遍算法步骤就记在脑子里了。后来他学了点Python语法再看伪代码很快就写出真正的代码。这个方法特别适合入门因为它逼着你关注“流程”而不是“语法”。算法本质上是一种流程设计画流程图的习惯如果保持下来以后做项目、排查逻辑问题也都用得上。画流程图有个小技巧不用追求画得规范关键是每个步骤必须写清楚“下一步走哪条分支”。如果某个分支条件含糊比如写了“看情况处理”说明这一步还没想明白得回头补定义。这其实就是前面讲的“确定性”在实操中的体现。等流程图画顺了再翻译成任何一门语言都只是换一套表达方式的问题。5. 算法入门实操伪代码、复杂度和正确的刷题姿势最后聊点能直接上手的东西。很多同学学算法最大的困惑不是“看不懂”而是“不知道怎么练”。这一节我给出一套实操路径先用伪代码拆思路再用复杂度判断方案好不好最后聊正确的刷题姿势。5.1 用伪代码拆思路先别急着开IDE拿到一道算法题最忌讳的是立刻打开IDE开始写代码。新手尤其容易这样写一半卡住再回去读题半天就没了。我的建议是先在纸上写一段伪代码。伪代码长什么样给你写一个二分查找的例子输入有序数组A目标值target 输出target在A中的下标找不到则返回-1 left 0 right len(A) - 1 while left right: mid (left right) / 2 if A[mid] target: return mid elif A[mid] target: left mid 1 else: right mid - 1 return -1这段东西看着像代码又不是代码。它省掉了很多语言细节不用管用Python还是Java不用纠结mid的除法要不要取整更不用处理编译器报错。你只需要关注逻辑本身。能在纸上把伪代码写出来说明思路是通的写不出来说明某个环节没想明白这时候回头补比对着IDE的报错猜半天高效得多。平时在Word里写伪代码我习惯用Consolas或者Courier New这类等宽字体加上缩进看起来就跟程序排版一样舒服也方便标注“这段对应后面的哪几行代码”。伪代码还有个好处方便讨论。在群聊里甩一段伪代码别人一眼看懂你思路比贴一屏报错信息强多了。等你把伪代码写稳再翻译成自己熟悉的语言基本就是体力活。这一步做好了能省掉大量无谓的调试时间。5.2 看懂时间复杂度从生活场景建立直觉复杂度分析是算法的“体检报告”不懂它你就分不清两个解法谁优谁劣。最常用的是时间复杂度用大O记号表示。这儿不讲严格定义先建立直觉。几个常见复杂度我用生活场景对照说明复杂度含义生活类比O(1)操作时间不随数据量变化鞋柜有编号你说拿5号一步拉开O(n)时间与数据量成正比在一堆没标签的纸箱里找东西只能一个个翻O(n²)数据翻倍时间翻四倍给n个朋友两两配对握手人数一多就没完O(log n)数据翻倍时间只加常数查字典时不断把候选范围砍半字典再厚也多不了几次翻页写算法题先看数据范围。如果n是10的5次方O(n²)基本就超时了就得想有没有O(n log n)或者O(n)的做法如果n只有100那O(n²)甚至O(n³)都能接受。这种估算能力面试必考工程实用做题更是核心技能。空间复杂度意思类似就是“额外占了多少地方”能讲清楚时间和空间复杂度一道题基本就站稳一半。刷题姿势方面再给点实在建议按专题刷别按难度刷。今天学二分就做十道二分题明天学排序就做十道排序题。这样能在短时间内反复验证同一个思路形成条件反射。刷题数量不是第一位的能不能把一道题的思路流利讲出来才是。每做完一道题花两分钟在纸上写一遍复杂度分析再对照题解看看漏了哪些边界条件。这种“慢”练习比一天刷五十道然后全忘光要强太多。最后说点我个人的体会。我带过的同学里凡是能把“什么是算法”用自己的话讲清楚的人后面学排序、搜索、动态规划都很顺凡是上来就刷题背模板的过了一个月基本都会回来问“该怎么复习”。算法这两个字听起来高大上本质就是一套解决问题的方法论。你不必背下所有代码但一定要建立这样的习惯拿到问题先拆输入输出再想用什么容器然后设计步骤最后分析复杂度。这篇先把地基打好下一篇我会挑一个具体的经典算法完整走一遍从问题描述到方案推导再到代码实现的过程。读到这里你可以先动手做一件事拿一张纸把二分查找的步骤拆成你最能理解的伪代码。写完你会发现算法入门的第一道门槛已经迈过来了。
返回列表