尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

初识算法与数据结构:《Hello 算法》第一章小结精讲——从生活案例到源码级验证

初识算法与数据结构:《Hello 算法》第一章小结精讲——从生活案例到源码级验证 初识算法与数据结构《Hello 算法》第一章小结精讲——从生活案例到源码级验证【免费下载链接】hello-algo《Hello 算法》动画图解、一键运行的数据结构与算法教程。支持简中、繁中、English、日本語提供 Python, Java, C, C, C#, JS, Go, Swift, Rust, Ruby, Kotlin, TS, Dart 等代码实现项目地址: https://gitcode.com/GitHub_Trending/he/hello-algo算法并非遥不可及的高深理论它早已融入我们的日常生活查字典是二分查找、整理扑克是插入排序、货币找零是贪心策略。本文以《Hello 算法》俄文版第一章小结ru/docs/chapter_introduction/summary.md为骨架系统回顾算法与数据结构的核心定义、两者关系以及为什么程序员仍然需要学习算法这一经典追问并结合仓库内 C 语言源码实现逐条印证帮助读者完成从直觉认知到代码落地的完整闭环。本小结对应俄文版《Hello 算法》第一章内容其正文分别位于 ru/docs/chapter_introduction/algorithms_are_everywhere.md算法无处不在与 ru/docs/chapter_introduction/what_is_dsa.md算法是什么。重点回顾七个必须掌握的核心结论第一章小结以Ключевые выводы重点回顾的形式沉淀了七个关键结论它们构成了后续所有章节的地基算法无处不在算法在日常生活中随处可见并非遥不可及的高深知识。事实上我们已在不知不觉中学会了许多算法并用它们解决生活中的大小问题。查字典 二分查找查字典的原理与二分查找算法一致而二分查找体现了分而治之divide and conquer这一重要算法思想。整理扑克 插入排序整理扑克的过程与插入排序算法非常类似而插入排序特别适合排序小型数据集。货币找零 贪心算法货币找零的步骤本质上是贪心算法——每一步都采取当前看来最好的选择。算法的定义算法是在有限时间内解决特定问题的一组指令或操作步骤数据结构则是计算机中组织和存储数据的方式。两者的关系数据结构与算法紧密相连——数据结构是算法的基石算法为数据结构注入生命力。拼装积木类比可以将数据结构与算法类比为拼装积木积木代表数据积木的形状和连接方式代表数据结构拼装积木的步骤对应算法。下面逐条展开并结合仓库源码验证每一项结论。算法无处不在三个生活案例与三种经典算法例一查字典与二分查找分而治之在拼音字典中每个汉字对应一个拼音而字典按拼音字母顺序排列。要查找拼音首字母为 $r$ 的字只需反复执行翻开字典约一半页数查看该页首字母若 $r$ 在 $m$ 之后则排除前半部分将查找范围缩小到后半部分——不断重复直到找到目标页码。这正是著名的二分查找binary search利用数据的有序性每一步把搜索区间缩小一半。仓库中以 C 语言实现了完整可运行的版本见 codes/c/chapter_searching/binary_search.c/* 二分查找双闭区间 */ int binarySearch(int *nums, int len, int target) { int i 0, j len - 1; // 初始化双闭区间 [0, n-1] while (i j) { // 当搜索区间为空时跳出 int m i (j - i) / 2; // 计算中点索引 m if (nums[m] target) // target 在区间 [m1, j] 中 i m 1; else if (nums[m] target) // target 在区间 [i, m-1] 中 j m - 1; else // 找到目标元素 return m; } return -1; // 未找到返回 -1 }代码中的每一步都与查字典的直觉一一对应i、j划定当前查找区间m是翻开的一半页数比较nums[m]与target决定舍弃前半还是后半。该文件还提供了左闭右开区间的变体binarySearchLCRO用于对照理解区间边界表示对循环条件的影响。更完整的理论推导与分步图解可继续阅读 ru/docs/chapter_searching/binary_search.md。例二整理扑克与插入排序打牌时我们习惯把手中的扑克牌从小到大排列先将牌分为有序与无序两部分初始时最左 1 张视为有序再从无序部分抽出一张插入有序部分的正确位置循环直至全部有序。这本质上就是插入排序insertion sort在处理小型数据集时非常高效许多编程语言排序库函数中都有它的身影。仓库中的实现见 codes/c/chapter_sorting/insertion_sort.c/* 插入排序 */ void insertionSort(int nums[], int size) { // 外循环已排序区间为 [0, i-1] for (int i 1; i size; i) { int base nums[i], j i - 1; // 内循环将 base 插入到已排序区间 [0, i-1] 中的正确位置 while (j 0 nums[j] base) { nums[j 1] nums[j]; // 将 nums[j] 向右移动一位 j--; } nums[j 1] base; // 将 base 赋值到正确位置 } }外层循环的i就是有序部分的边界内层while模拟了把新牌向前比较并后移空位的动作最终把base放到正确位置。从源码结构看插入排序最好情况数据已有序时间复杂度为 $O(n)$最坏与平均情况为 $O(n^2)$这正解释了它适合小型数据集的定位。例三货币找零与贪心算法购买 $69$ 元商品、付 $100$ 元找零 $31$ 元时收银员会自然地在 $1/5/10/20$ 元面值中先拿最大的 $20$ 元再拿 $10$ 元、$1$ 元最终凑出 $20 10 1 31$ 元。每一步都选当前最好面值最大且不超过剩余金额的方案本质上是贪心算法greedy algorithm。仓库中的通用实现见 codes/c/chapter_greedy/coin_change_greedy.c/* 零钱兑换贪心 */ int coinChangeGreedy(int *coins, int size, int amt) { int i size - 1; // 假设 coins 列表有序 int count 0; while (amt 0) { // 循环贪心选择直到无剩余金额 while (i 0 coins[i] amt) { i--; // 找到小于且最接近剩余金额的硬币 } amt - coins[i]; // 选择 coins[i] count; } return amt 0 ? count : -1; // 未找到可行方案返回 -1 }值得注意的是该文件的 Driver Code 同时给出了三个测试用例面值 $[1,5,10,20,50,100]$ 时贪心总能得到最优解而面值 $[1,20,50]$ 凑 $60$ 元时贪心给出 $501\times10$共 11 枚最优解实为 $202020$3 枚面值 $[1,49,50]$ 凑 $98$ 元时贪心给出 $501\times48$49 枚最优解实为 $4949$2 枚。这说明贪心算法简单高效但不保证全局最优这正是小结中每一步都采取当前看来最好的选择这句话背后需要警惕的边界。更完整的正反例分析见 ru/docs/chapter_greedy/greedy_algorithm.md。算法与数据结构的精确定义算法algorithm算法是在有限时间内解决特定问题的一组指令或操作步骤具有以下特性问题明确包含清晰的输入和输出定义可行性能够在有限步骤、时间和内存空间内完成确定性每个步骤都有确定的含义在相同输入和运行条件下输出始终相同。数据结构data structure数据结构是组织和存储数据的方式涵盖数据内容、数据之间的关系和数据操作方法其设计目标包括空间占用尽量少以节省计算机内存数据操作尽可能快涵盖访问、添加、删除、更新等提供简洁的数据表示与逻辑信息以便算法高效运行。设计即权衡小结提醒我们数据结构设计是一个充满权衡trade-off的过程想在某方面提升往往需要在另一方面妥协。例如链表相比数组增删数据更便捷但牺牲了数据访问速度图相比链表逻辑信息更丰富但需要占用更大内存。仓库中可对照验证线性表章节同时提供了基于连续内存的数组与基于指针链接的链表两套实现前者随机访问是 $O(1)$、插入删除是 $O(n)$后者恰好相反——这就是权衡最直观的代码证据。数据结构与算法的关系基石与生命力小结强调二者紧密相连具体表现为三个方面数据结构是算法的基石数据结构为算法提供结构化存储的数据及操作方法算法为数据结构注入生命力数据结构本身只存储信息结合算法才能解决具体问题算法可基于不同数据结构实现但效率差异可能很大选择合适的数据结构是关键。拼装积木类比数据结构与算法犹如下图所示的拼装积木一套积木除了许多零件还附有详细组装说明书按说明书一步步操作就能组装出精美模型。两者的详细对应关系如下表所示数据结构与算法拼装积木输入数据未拼装的积木数据结构积木组织形式包括形状、大小、连接方式等算法把积木拼成目标形态的一系列操作步骤输出数据积木模型值得说明的是数据结构与算法是独立于编程语言的。正因为如此《Hello 算法》才能在 codes 目录下为同一套内容提供 Python、Java、C、C、C#、Go、Swift、Rust、Ruby、Kotlin、TypeScript、Dart 等多语言实现。此外还有一个约定俗成的简称实际讨论中数据结构与算法通常简称为算法例如 LeetCode 的算法题目实际上同时考查两者。QA 深解程序员为什么仍然需要学习算法小结收录了一个极具代表性的提问Q作为一名程序员我在日常工作中从未用算法解决过问题常用算法都被编程语言封装好了直接用就可以了这是否意味着我们工作中的问题还没有到达需要算法的程度小结的回答将具体工作技能比作武功的招式而基础科目更像是内功。学习算法及其他基础科目的意义不在于工作中从零实现它而在于基于所学知识在解决问题时作出专业判断提升整体工作质量。书中以每种编程语言都内置了排序函数为例展开对比未学习数据结构与算法给定任何数据可能都直接交给内置排序函数。运行顺畅、性能不错表面上看不出问题。学习过算法之后会知道内置排序函数的时间复杂度为 $O(n \log n)$而当数据是固定位数的整数例如学号时可以用效率更高的基数排序radix sort将时间复杂度降为 $O(nk)$其中 $k$ 为位数。当数据体量很大时节省的运行时间能创造可观的价值成本降低、体验提升。这一论断在仓库中有完整的源码支撑codes/c/chapter_sorting/radix_sort.c 实现了从低位到高位逐位执行计数排序的完整流程通过exp 10^(k-1)逐位处理将时间复杂度控制在 $O(nk)$。对比同一目录下的内置排序典型实现归并排序$O(n \log n)$二者的适用场景差异一目了然——这正是基于学到的知识作出专业判断的具体写照。小结最后给出一个工程哲学层面的总结在工程领域中大量问题难以达到最优解许多问题只是被差不多地解决了。问题的难易程度一方面取决于问题本身的性质另一方面取决于观测问题的人的知识储备——人的知识越完备、经验越多分析问题就越深入问题就能被解决得更优雅。如何继续深入学习路径建议本章小结是一系列算法主题的起点建议按以下路径在仓库中继续进阶夯实基础概念完整阅读 ru/docs/chapter_introduction/index.md 下 algorithms_are_everywhere.md 与 what_is_dsa.md 两个正文文档理解各生活案例的完整分步图解亲手运行源码分别编译运行 binary_search.c、insertion_sort.c、radix_sort.c 与 coin_change_greedy.c观察输出并与正文结论对照亦可切换至 codes/python、codes/java 等语言的同名文件体会多语言实现的异同按主题纵向深入二分查找可延伸至 ru/docs/chapter_searching 的插入位置、边界查找等变体贪心算法可延伸至 ru/docs/chapter_greedy 中的分数背包、最大容量等问题排序可延伸至 ru/docs/chapter_sorting 的归并、快排、堆排与计数/基数排序等全套算法关注权衡思想后续章节如链表 vs 数组、哈希表、堆都会反复体现空间换时间逻辑丰富 vs 内存开销等设计权衡这正是本章数据结构设计是充满权衡的过程这一结论的延续。【免费下载链接】hello-algo《Hello 算法》动画图解、一键运行的数据结构与算法教程。支持简中、繁中、English、日本語提供 Python, Java, C, C, C#, JS, Go, Swift, Rust, Ruby, Kotlin, TS, Dart 等代码实现项目地址: https://gitcode.com/GitHub_Trending/he/hello-algo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表