 Hello 算法 1.1.0 C++ 版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。 本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请各位老师和同学批评 指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 379 页 | 18.47 MB | 1 年前3 Hello 算法 1.1.0 C++ 版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。 本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请各位老师和同学批评 指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 379 页 | 18.47 MB | 1 年前3
 Hello 算法 1.0.0 C++版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请 各位老师和同学批评指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为「尾递归 tail recursion」。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 378 页 | 17.59 MB | 1 年前3 Hello 算法 1.0.0 C++版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请 各位老师和同学批评指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为「尾递归 tail recursion」。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 378 页 | 17.59 MB | 1 年前3
 Hello 算法 1.2.0 简体中文 C++ 版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。 本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请各位老师和同学批评 指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 379 页 | 18.48 MB | 10 月前3 Hello 算法 1.2.0 简体中文 C++ 版我深深赞同费曼教授所言:“Knowledge isn’t free. You have to pay attention.”从这个意义上看,这本 书并非完全“免费”。为了不辜负你为本书所付出的宝贵“注意力”,我会竭尽所能,投入最大的“注意力” 来完成本书的创作。 本人自知学疏才浅,书中内容虽然已经过一段时间的打磨,但一定仍有许多错误,恳请各位老师和同学批评 指正。 本书中的代码附有可一键运行的源文件,托管于 github ”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为尾递归(tail recursion)。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无须继续执行其他 操作,因此系统无须保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归:0 码力 | 379 页 | 18.48 MB | 10 月前3
 Hello 算法 1.2.0 繁体中文 C++ 版我深深認同費曼教授所言:“Knowledge isn’t free. You have to pay attention.”從這個意義上看,這本 書並非完全“免費”。為了不辜負你為本書所付出的寶貴“注意力”,我會竭盡所能,投入最大的“注意力” 來完成本書的創作。 本人自知學疏才淺,書中內容雖然已經過一段時間的打磨,但一定仍有許多錯誤,懇請各位老師與同學批評 指正。 本書中的程式碼附有可一鍵執行的原始檔,託管於 github capacity 觀察以上公式,當雜湊表容量 capacity 固定時,雜湊演算法 hash() 決定了輸出值,進而決定了鍵值對在雜 湊表中的分佈情況。 這意味著,為了降低雜湊衝突的發生機率,我們應當將注意力集中在雜湊演算法 hash() 的設計上。 6.3.1 雜湊演算法的目標 為了實現“既快又穩”的雜湊表資料結構,雜湊演算法應具備以下特點。 ‧ 確定性:對於相同的輸入,雜湊演算法應始終產生相同的輸出。這樣才能確保雜湊表是可靠的。 left subtree 左子树 左子樹 right subtree 右子树 右子樹 root node 根节点 根節點 leaf node 叶节点 葉節點 edge 边 邊 level 层 層 degree 度 度 height 高度 高度 depth 深度 深度 perfect binary tree 完美二叉树 完美二元樹 complete binary tree 完全二叉树0 码力 | 379 页 | 18.79 MB | 10 月前3 Hello 算法 1.2.0 繁体中文 C++ 版我深深認同費曼教授所言:“Knowledge isn’t free. You have to pay attention.”從這個意義上看,這本 書並非完全“免費”。為了不辜負你為本書所付出的寶貴“注意力”,我會竭盡所能,投入最大的“注意力” 來完成本書的創作。 本人自知學疏才淺,書中內容雖然已經過一段時間的打磨,但一定仍有許多錯誤,懇請各位老師與同學批評 指正。 本書中的程式碼附有可一鍵執行的原始檔,託管於 github capacity 觀察以上公式,當雜湊表容量 capacity 固定時,雜湊演算法 hash() 決定了輸出值,進而決定了鍵值對在雜 湊表中的分佈情況。 這意味著,為了降低雜湊衝突的發生機率,我們應當將注意力集中在雜湊演算法 hash() 的設計上。 6.3.1 雜湊演算法的目標 為了實現“既快又穩”的雜湊表資料結構,雜湊演算法應具備以下特點。 ‧ 確定性:對於相同的輸入,雜湊演算法應始終產生相同的輸出。這樣才能確保雜湊表是可靠的。 left subtree 左子树 左子樹 right subtree 右子树 右子樹 root node 根节点 根節點 leaf node 叶节点 葉節點 edge 边 邊 level 层 層 degree 度 度 height 高度 高度 depth 深度 深度 perfect binary tree 完美二叉树 完美二元樹 complete binary tree 完全二叉树0 码力 | 379 页 | 18.79 MB | 10 月前3
 Hello 算法 1.0.0b4 C++版省略所有系数。例如,循环 2? 次、5? + 1 次等,都可以简化记为 ? 次,因为 ? 前面的系数对时间复 杂度没有影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别 套用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度通常为 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 // === File: time_complexity 个互不重复的元素,求其所有可能的排列方案,方案数量为: ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘通常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,以此类推,直至第 ? 层时终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 343 页 | 27.39 MB | 1 年前3 Hello 算法 1.0.0b4 C++版省略所有系数。例如,循环 2? 次、5? + 1 次等,都可以简化记为 ? 次,因为 ? 前面的系数对时间复 杂度没有影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别 套用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度通常为 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 // === File: time_complexity 个互不重复的元素,求其所有可能的排列方案,方案数量为: ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘通常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,以此类推,直至第 ? 层时终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 343 页 | 27.39 MB | 1 年前3
 Hello 算法 1.0.0b5 C++版”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为「尾递归 tail recursion」。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无需继续执行其他 操作,因此系统无需保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归。 省略所有系数。例如,循环 2? 次、5? + 1 次等,都可以简化记为 ? 次,因为 ? 前面的系数对时间复 杂度没有影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别 套用第 1. 点和第 2. 点的技巧。 给定一个函数,我们可以用上述技巧来统计操作数量。 void algorithm(int n) { int a = 1; // +0(技巧0 码力 | 377 页 | 30.69 MB | 1 年前3 Hello 算法 1.0.0b5 C++版”。 2. 归:触发“终止条件”后,程序从最深层的递归函数开始逐层返回,汇聚每一层的结果。 而从实现的角度看,递归代码主要包含三个要素。 1. 终止条件:用于决定什么时候由“递”转“归”。 2. 递归调用:对应“递”,函数调用自身,通常输入更小或更简化的参数。 3. 返回结果:对应“归”,将当前递归层级的结果返回至上一层。 观察以下代码,我们只需调用函数 recur(n) ,就可以完成 间效率上与迭代相当。这种情况被称为「尾递归 tail recursion」。 ‧ 普通递归:当函数返回到上一层级的函数后,需要继续执行代码,因此系统需要保存上一层调用的上下 文。 ‧ 尾递归:递归调用是函数返回前的最后一个操作,这意味着函数返回到上一层级后,无需继续执行其他 操作,因此系统无需保存上一层函数的上下文。 以计算 1 + 2 + ⋯ + ? 为例,我们可以将结果变量 res 设为函数参数,从而实现尾递归。 省略所有系数。例如,循环 2? 次、5? + 1 次等,都可以简化记为 ? 次,因为 ? 前面的系数对时间复 杂度没有影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别 套用第 1. 点和第 2. 点的技巧。 给定一个函数,我们可以用上述技巧来统计操作数量。 void algorithm(int n) { int a = 1; // +0(技巧0 码力 | 377 页 | 30.69 MB | 1 年前3
 《深入浅出MFC》2/e感到生疏,主要是函数的运用和函数的参数十分复杂。我对WINDOWS SDK 编程较少,是 否应该要熟悉WINDOWS API 函数后,结合MFC 框架编程? 侯俊杰回复:的确如此。MFC 其实就是把Windows API 做了一层薄薄包装,包装于各个设 计良好的classes 而已。所以,掌握了MFC framework 架构组织之后,接下来在programming 实务方面,就是去了解并运用各个classes,而各个classes 看过重量级战斗吗?重量级战斗都有「一棒击沉」的威力。如果现实生活中发生重量级 战斗-- 使人生涯结束、生活受威胁的那种,那么战况之激烈不言可知。如果这场战斗关 系到你的程序员生涯,铃声响起时你最好付出高度注意力。 我说的是application framework。换个角度来说,我指的是整合型(全套服务的)C++ 软 体开发平台。目前,所有重要厂商包括Microsoft、Borland、Symantec、Metaware 的软件我也举得起来)。 当我们面临软件工业革命,我们的第一个考量点是:我的软件开发技术要从哪一个技术 面切入?从raw API 还是从高阶一点的工具?如果答案是后者,第二个考量点是我使用 哪一层级的工具?GUI toolkits 还是Class Library 还是Application Framework?如果答 案又是后者,第三个考量点是我使用哪一套产品?MFC 或OWL 或Open Class0 码力 | 1009 页 | 11.08 MB | 1 年前3 《深入浅出MFC》2/e感到生疏,主要是函数的运用和函数的参数十分复杂。我对WINDOWS SDK 编程较少,是 否应该要熟悉WINDOWS API 函数后,结合MFC 框架编程? 侯俊杰回复:的确如此。MFC 其实就是把Windows API 做了一层薄薄包装,包装于各个设 计良好的classes 而已。所以,掌握了MFC framework 架构组织之后,接下来在programming 实务方面,就是去了解并运用各个classes,而各个classes 看过重量级战斗吗?重量级战斗都有「一棒击沉」的威力。如果现实生活中发生重量级 战斗-- 使人生涯结束、生活受威胁的那种,那么战况之激烈不言可知。如果这场战斗关 系到你的程序员生涯,铃声响起时你最好付出高度注意力。 我说的是application framework。换个角度来说,我指的是整合型(全套服务的)C++ 软 体开发平台。目前,所有重要厂商包括Microsoft、Borland、Symantec、Metaware 的软件我也举得起来)。 当我们面临软件工业革命,我们的第一个考量点是:我的软件开发技术要从哪一个技术 面切入?从raw API 还是从高阶一点的工具?如果答案是后者,第二个考量点是我使用 哪一层级的工具?GUI toolkits 还是Class Library 还是Application Framework?如果答 案又是后者,第三个考量点是我使用哪一套产品?MFC 或OWL 或Open Class0 码力 | 1009 页 | 11.08 MB | 1 年前3
 Hello 算法 1.0.0b1 C++版省略所有系数。例如,循环 2? 次、5? + 1 次、⋯⋯,都可以化简记为 ? 次,因为 ? 前面的系数对时间 复杂度也不产生影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套 用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度都是 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 2. 复杂度分析 hello‑algo.com 个互不重复的元素,求其所有可能的排列方案,则方案数量为 ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,⋯⋯,直至到第 ? 层时 终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 187 页 | 14.71 MB | 1 年前3 Hello 算法 1.0.0b1 C++版省略所有系数。例如,循环 2? 次、5? + 1 次、⋯⋯,都可以化简记为 ? 次,因为 ? 前面的系数对时间 复杂度也不产生影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套 用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度都是 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 2. 复杂度分析 hello‑algo.com 个互不重复的元素,求其所有可能的排列方案,则方案数量为 ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,⋯⋯,直至到第 ? 层时 终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 187 页 | 14.71 MB | 1 年前3
 Hello 算法 1.0.0b2 C++版省略所有系数。例如,循环 2? 次、5? + 1 次、⋯⋯,都可以化简记为 ? 次,因为 ? 前面的系数对时间 复杂度也不产生影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套 用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度都是 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 2. 复杂度分析 hello‑algo.com 个互不重复的元素,求其所有可能的排列方案,则方案数量为 ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,⋯⋯,直至到第 ? 层时 终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 197 页 | 15.72 MB | 1 年前3 Hello 算法 1.0.0b2 C++版省略所有系数。例如,循环 2? 次、5? + 1 次、⋯⋯,都可以化简记为 ? 次,因为 ? 前面的系数对时间 复杂度也不产生影响。 3. 循环嵌套时使用乘法。总操作数量等于外层循环和内层循环操作数量之积,每一层循环依然可以分别套 用上述 1. 和 2. 技巧。 以下示例展示了使用上述技巧前、后的统计结果。 ?(?) = 2?(? + 1) + (5? + 1) + 2 完整统计 (‑.‑|||) = logRecur(float n) { if (n <= 1) return 0; return logRecur(n / 2) + 1; } 线性对数阶 ?(? log ?) 线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 ?(log ?) 和 ?(?) 。 主流排序算法的时间复杂度都是 ?(? log ?) ,例如快速排序、归并排序、堆排序等。 2. 复杂度分析 hello‑algo.com 个互不重复的元素,求其所有可能的排列方案,则方案数量为 ?! = ? × (? − 1) × (? − 2) × ⋯ × 2 × 1 阶乘常使用递归实现。例如以下代码,第一层分裂出 ? 个,第二层分裂出 ? − 1 个,⋯⋯,直至到第 ? 层时 终止分裂。 // === File: time_complexity.cpp === /* 阶乘阶(递归实现) */ int factorialRecur(int0 码力 | 197 页 | 15.72 MB | 1 年前3
 C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化free(a[i]); • free(a); • ↑ 有 Java 病的人,才会这样分配二维数组,又低效,又不方便。 • 造成了 m + 1 次 malloc 调用,内存都是分散的,每次访问都要解开两层指针,非常低效。 • 分配 n*m 二维数组,正确的方式永远是: float *a = malloc(n * m * sizeof(float)); • 也不要用 vector C++高性能并行编程与优化 -  课件 - 07 深入浅出访存优化free(a[i]); • free(a); • ↑ 有 Java 病的人,才会这样分配二维数组,又低效,又不方便。 • 造成了 m + 1 次 malloc 调用,内存都是分散的,每次访问都要解开两层指针,非常低效。 • 分配 n*m 二维数组,正确的方式永远是: float *a = malloc(n * m * sizeof(float)); • 也不要用 vector- > 序的循环,其 X 是外层循环体,在先后执行的时间上是不连续的。 • 从而在硬件看来,以 YX 序遍历,就和顺序访问一维数组没什么两样,从而缓存预取能正 常运作,甚至编译器可以优化成一个 nx*ny 的一层循环。 • 而如果以 XY 序遍历,就像跳跃着访问一样,不连续,缓存得不到利用,每次读取只用了 其中 4 字节,浪费了缓存行剩下的 60 字节,非常低效。 • 结论: • 对于 YX 序(列主序, 造成的,一部分是因为跳 跃的访存让 CPU 没有办法自动预取造成的 。 封装成 ndarray 类 ndarray.h ,同学们可以在作业或 是自己的项目里随意使用。 不要再用 Java 式的二层三层指针 了,用 ndarray<2, float> 声明一 个二维浮点数组, ndarray<3, int> 声明一个三维整型数组。 这里的 ndarray 通过 a(x, y) 来 索引,看起来像 0 码力 | 147 页 | 18.88 MB | 1 年前3
共 23 条
- 1
- 2
- 3













