1. 逻辑结构 & 存储结构
逻辑结构:
集合结构:数据元素之间的关系是属于同一个集合。关系松散。
线性结构:数据元素之间存在一对一关系。
树形结构:数据元素之间存在一对多关系。
图形结构:元素之间存在着多对多关系。
存储结构:
顺序存储:逻辑上相邻的元素存储在物理位置相邻的存储单元中。
链式:额外存储元素之间的关系,不要求物理相邻。
散列存储:Hash。
2. 时间复杂度
O(1) -> O(log2n) -> O(n) -> O(nlog2n) -> O(n2) -> O(n3) -> O(2n)
计算一个递归的复杂度:反向替代法得到执行次数的地推关系和初始条件,形成类似M(n) = M(n-i)+i 的公式,再把 i 替换为 n 得到复杂度
3. 分治和递归
分治(Divide and Conquer)算法是一种解决问题的方法论,而递归(Recursion)是一种编程技巧或方法。它们之间有关联,但也有明显的区别:
分治算法:
-
定义:分治算法是一种算法设计范式,它通过将问题分解为更小的子问题来解决,然后递归地解决这些子问题,最后将子问题的解合并以形成原问题的解。
-
步骤:通常包括三个步骤:分解(Divide)、解决(Conquer)、合并(Combine)。
-
应用:分治算法常用于可以自然分解为相似子问题的问题,如排序算法(快速排序、归并排序)、二分搜索、矩阵乘法等。
递归算法:
-
定义:递归是一种编程技巧,其中一个函数直接或间接地调用自身。
-
基本形式:递归通常包括递归基本情况(base case)和递归步骤(recursive case)。
-
应用:递归可用于实现分治算法,也可用于实现其他需要回溯或重复调用相同逻辑的算法,如树和图的遍历、阶乘计算、斐波那契数列等。
区别:
-
概念层面:分治是一种解决问题的策略,而递归是一种实现算法的技术。
-
实现方式:分治算法不一定需要递归来实现,也可以通过迭代的方式(例如,使用循环)来实现;递归算法也不一定是分治算法。
-
重点:分治算法的重点在于问题的分解和子问题的解的合并;递归的重点在于函数自我调用的逻辑和终止条件。
-
效率问题:递归实现可能导致不必要的重复计算和高空间复杂度(由于调用栈),而迭代通常更高效,但可能难以表达某些问题。
联系:
-
分治算法经常使用递归来实现,因为递归提供了一种自然的方式来分解问题和合并结果。
-
递归可以视为分治策略的一种编程实现。
4. 动态规划
动态规划(Dynamic Programming,简称DP)算法是一种在数学、管理科学、计算机科学、经济学和生物信息学等领域中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划算法的特点:
-
最优子结构:动态规划算法依赖于问题具有最优子结构这一特点,即问题的最优解包含其子问题的最优解。
-
重叠子问题:动态规划算法通常用于解决那些具有重叠子问题的问题,即在递归算法中重复计算多次的子问题。
-
记忆化:动态规划通过存储子问题的解(通常是在表或数组中),避免重复计算,从而提高效率。这个过程称为“记忆化”。
-
自底向上:动态规划通常采用自底向上的方法,即先解决所有子问题,然后逐步构建原问题的解。
-
迭代与填表:动态规划算法通常使用迭代而非递归,通过填充一个表格(通常是一维或多维数组)来解决问题。
动态规划与分治算法的区别:
-
适用问题:分治算法适用于可以分解为独立子问题的问题,而动态规划适用于具有重叠子问题和最优子结构的问题。
-
存储子问题解:分治算法通常不需要存储所有子问题的解,而动态规划算法需要存储之前计算的子问题解以避免重复计算。
-
方法:分治算法可以递归实现,也可以迭代实现;动态规划通常迭代实现,通过填表方法逐步构建解。
-
重复工作:分治算法中,子问题通常不会重复出现;而动态规划算法中,子问题可能会被多次计算,因此需要记忆化来优化。
-
方向性:分治算法可以是递归的,从上到下进行;动态规划通常是自底向上,从最基本的子问题开始解决。
动态规划与递归的区别:
-
记忆化:递归可能不包含记忆化机制,而动态规划算法通过记忆化减少重复计算。
-
迭代 vs 递归:递归是一种调用自身的方法,而动态规划通常使用迭代方法,通过循环和数组来解决问题。
-
问题类型:递归可以用于实现分治算法,也可以用于实现动态规划算法,但动态规划算法通常不使用递归,而是使用迭代来避免栈溢出和重复计算。
5. 回溯算法
回溯算法和递归是两种在编程中常用的技术,尤其是在解决复杂问题时。它们有一些相似之处,但也存在一些关键的区别:
相似之处
-
重复调用:两者都涉及到函数或方法的重复调用。
-
树形结构:很多问题可以通过树形结构来表示,递归和回溯算法都可以用来遍历这种结构。
-
基础和递归案例:在递归和回溯算法中,通常都有基础案例(base case)和递归案例(recursive case)。
区别
-
目的:
-
递归:递归的主要目的是通过将问题分解为更小的子问题来解决问题。递归函数通常有一个或多个基础案例,当达到这些案例时,递归会停止。
-
回溯算法:回溯算法的主要目的是通过试错来找到问题的解决方案。它不仅需要找到解决方案,还需要在找到解决方案的过程中回溯,撤销之前的操作。
-
-
逻辑结构:
-
递归:递归逻辑通常是自上而下的,从问题的初始状态开始,逐步分解问题。
-
回溯算法:回溯算法逻辑是自上而下的,但在找到解决方案的过程中,如果发现当前路径不可行,会回溯到上一步,尝试其他可能的路径。
-
-
存储空间:
-
递归:递归通常使用系统堆栈来存储中间状态,可能导致堆栈溢出。
-
回溯算法:回溯算法通常使用显式的栈或数组来存储路径,可以更灵活地控制存储空间。
-
-
应用场景:
-
递归:适用于可以自然分解为子问题的问题,如树的遍历、图的搜索、阶乘计算等。
-
回溯算法:适用于需要探索所有可能解决方案的问题,如八皇后问题、数独解、迷宫求解等。
-
-
效率:
-
递归:可能会重复计算相同的子问题,效率可能较低。
-
回溯算法:通过剪枝(剪去不可行的路径)来提高效率,避免重复计算。
-