📜  数据结构与算法

📅  最后修改于: 2020-10-14 06:41:13             🧑  作者: Mango

数据结构与算法

什么是算法?

算法是执行计算或某些其他问题解决操作(尤其是计算机)所需的过程或一组规则。算法的形式定义是它包含有限的指令集,这些指令以特定的顺序执行以执行特定的任务。它不是完整的程序或代码;它只是问题的解决方案(逻辑),可以使用流程图或伪代码将其表示为非正式描述。

算法的特征

以下是算法的特征:

  • 输入:算法具有一些输入值。我们可以将0或某些输入值传递给算法。
  • 输出:在算法结束时,我们将获得1个或更多输出。
  • 明确:算法应明确,这意味着算法中的指令应清晰,简单。
  • 有限度:算法应具有有限度。在这里,有限性意味着算法应包含有限数量的指令,即,这些指令应该是可数的。
  • 有效性:一种算法应该有效,因为算法中的每条指令都会影响整个过程。
  • 语言无关:算法必须与语言无关,以便算法中的指令可以用任何具有相同输出的语言来实现。

算法的数据流

  • 问题:问题可以是现实问题,也可以是我们需要为其创建程序或指令集的现实问题中的任何实例。该指令集被称为算法。
  • 算法:将针对逐步设计的问题设计算法。
  • 输入:设计算法后,将所需的和所需的输入提供给算法。
  • 处理单元:输入将被提供给处理单元,处理单元将产生所需的输出。
  • 输出:输出是程序的结果或结果。

为什么我们需要算法?

由于以下原因,我们需要算法:

  • 可伸缩性:它有助于我们了解可伸缩性。当我们遇到一个现实中的大问题时,我们需要将其按比例缩小为小到小的步骤,以轻松分析问题。
  • 性能:现实世界不容易分解为较小的步骤。如果可以轻松地将问题分解为更小的步骤,则表明该问题可行。

让我们通过一个真实的例子来了解算法。假设我们要制作柠檬汁,那么以下是制作柠檬汁所需的步骤:

步骤1:首先,我们将柠檬切成两半。

步骤2:尽可能多地挤压柠檬,然后将其汁液放入容器中。

步骤3:在其中加入两汤匙糖。

步骤4:搅拌容器直至糖溶解。

步骤5:当糖溶解后,在其中加一些水和冰。

第6步:将果汁在冰箱中存放5到几分钟。

步骤7:现在,准备喝酒了。

可以将上述实际情况与算法的定义直接进行比较。我们无法在步骤2之前执行步骤3,我们需要按照特定的顺序来制作柠檬汁。算法还指出,每条指令都应按照特定的顺序执行特定的任务。

现在我们来看一个编程算法的例子。

我们将编写一种算法,将用户输入的两个数字相加。

以下是将用户输入的两个数字相加所需的步骤:

步骤1:开始

步骤2:声明三个变量a,b和sum。

步骤3:输入a和b的值。

步骤4:将a和b的值相加并将结果存储在sum变量中,即sum = a + b。

步骤5:列印总和

步骤6:停止

算法因素

以下是设计算法时需要考虑的因素:

  • 模块化:如果给出任何问题,我们可以将该问题分解为小到小的模块或小到小的步骤,这是算法的基本定义,这意味着该功能已为算法完美设计。
  • 正确性:算法的正确性定义为给定输入产生所需输出时的含义,这意味着该算法已被设计为算法。算法分析已经正确完成。
  • 可维护性:在这里,可维护性意味着应该以非常简单的结构化方式设计算法,以便在我们重新定义算法时,不会对算法进行重大更改。
  • 功能:它考虑了解决现实问题的各种逻辑步骤。
  • 鲁棒性:鲁棒性是指算法如何清楚地定义我们的问题。
  • 用户友好:如果算法不友好,那么设计人员将无法向程序员解释。
  • 简便性:如果算法简单,那么很容易理解。
  • 可扩展性:如果其他算法设计者或程序员想要使用您的算法,则它应该是可扩展的。

算法的重要性

  • 理论上的重要性:当给我们任何现实世界中的问题时,我们将问题分解为小小的模块。为了解决这个问题,我们应该了解所有的理论方面。
  • 实际意义:我们知道,没有实际执行就无法完成理论。因此,算法的重要性既可以在理论上也可以在实践中考虑。

算法问题

以下是设计算法时出现的问题:

  • 如何设计算法:我们知道算法是一个分步过程,因此我们必须遵循一些步骤来设计算法。
  • 如何分析算法效率

算法方法

在考虑了设计算法的理论和实际重要性之后,使用以下方法:

  • 蛮力算法:采用通用逻辑结构设计算法。也称为穷举搜索算法,它搜索所有可能性以提供所需的解决方案。这种算法有两种类型:
    1. 优化:找到问题的所有解决方案,然后取出最佳解决方案,或者如果知道最佳解决方案的价值,那么如果知道最佳解决方案,它将终止。
    2. 牺牲:找到最佳解决方案后,它将停止。
  • 分而治之:这是算法的一种非常实现。它允许您逐步设计算法。它分解了算法以不同的方法解决问题。它使您可以将问题分解为不同的方法,并为有效输入生成有效输出。此有效输出将传递给其他函数。
  • 贪婪算法:这是一种算法范例,可以在每次迭代中做出最佳选择,以期获得最佳解决方案。它易于实现,执行时间更快。但是,在极少数情况下,它可以提供最佳解决方案。
  • 动态编程:通过存储中间结果,使算法更有效。它遵循五个不同的步骤来找到问题的最佳解决方案:
    1. 它将问题分解为一个子问题,以找到最佳解决方案。
    2. 解决问题后,它会从这些子问题中找到最佳解决方案。
    3. 存储子问题的结果称为记忆。
    4. 重用结果,以便对于相同的子问题无法重新计算。
    5. 最后,它计算复杂程序的结果。
  • 分支定界算法:分支定界算法只能应用于整数编程问题。这种方法将所有可行的解决方案集合分成较小的子集。进一步评估这些子集以找到最佳解决方案。
  • 随机算法:正如我们在常规算法中看到的那样,我们具有预定义的输入和所需的输出。具有确定的一组输入和所需的输出,并遵循某些描述的步骤的那些算法称为确定性算法。当将随机变量引入随机算法时会发生什么?在随机算法中,该算法引入了一些随机位并将其添加到输入中以产生输出,该输出本质上是随机的。随机算法比确定性算法更简单有效。
  • 回溯:回溯是一种算法技术,可以递归解决问题并在不满足问题约束的情况下将其删除。

算法的主要类别如下:

  • 排序:为按特定顺序对项目进行排序而开发的算法。
  • 搜索:为搜索数据结构中的项目而开发的算法。
  • 删除:为从数据结构中删除现有元素而开发的算法。
  • 插入:为在数据结构内插入项目而开发的算法。
  • 更新:为更新数据结构中现有元素而开发的算法。

算法分析

可以从两个级别分析算法,即,第一级是在创建算法之前,第二级是在创建算法之后。以下是对算法的两种分析:

  • 先验分析:这里,先验分析是对算法的理论分析,是在实现算法之前进行的。在实现算法之前,可以考虑各种因素,例如处理器速度,这对实现部分没有影响。
  • 后验分析:在这里,后验分析是对算法的实际分析。通过使用任何编程语言来实现算法,可以进行实际分析。该分析基本上评估了该算法占用了多少运行时间和空间。

算法复杂度

可以从两个因素来衡量算法的性能:

  • 时间复杂度:算法的时间复杂度是完成执行所需的时间。算法的时间复杂度用大O表示法表示。在这里,大O符号是代表时间复杂度的渐进符号。时间复杂度主要通过计算完成执行的步骤数来计算。让我们通过一个例子来了解时间的复杂性。
sum=0;
// Suppose we have to calculate the sum of n numbers.
for i=1 to n
sum=sum+i;
// when the loop ends then sum holds the sum of the n numbers
return sum;

在上面的代码中,循环语句的时间复杂度至少为n,并且如果n的值增加,则时间复杂度也会增加。尽管代码的复杂度(即返回和)将是恒定的,因为其值不依赖于n的值,并且仅一步即可提供结果。我们通常考虑最坏时间的复杂性,因为它是任何给定输入大小所花费的最长时间。

  • 空间复杂度:算法的空间复杂度是解决问题并产生输出所需的空间量。与时间复杂度类似,空间复杂度也以大O表示法表示。

对于算法,出于以下目的需要空间:

  • 存储程序指令
  • 存储常数值
  • 存储变量值
  • 跟踪函数调用,跳转语句等

辅助空间:算法所需的额外空间(不包括输入大小)被称为辅助空间。空间复杂度同时考虑了空间(即辅助空间)和输入所使用的空间。

所以,

空间复杂度=辅助空间+输入大小。

算法类型

以下是算法的类型:

  • 搜索算法
  • 排序算法

搜索算法

每天,我们都会在日常生活中寻找一些东西。类似地,对于计算机而言,海量数据存储在计算机中,每当用户请求任何数据时,计算机就会在内存中搜索该数据并将其提供给用户。主要有两种技术可用于搜索数组中的数据:

  • 线性搜索
  • 二进制搜索

线性搜寻

线性搜索是一种非常简单的算法,它从数组的开头开始搜索元素或值,直到找不到所需的元素为止。它将要搜索的元素与数组中的所有元素进行比较,如果找到匹配项,则返回元素的索引,否则返回-1。该算法可以在未排序列表上实现。

二元搜寻

二进制算法是最快速地搜索元素的最简单算法。它用于从排序列表中搜索元素。元素必须以顺序顺序或排序方式存储,以实现二进制算法。如果元素以随机方式存储,则无法执行二进制搜索。它用于查找列表的中间元素。

排序算法

排序算法用于按升序或降序重新排列数组或给定数据结构中的元素。比较运算符决定元素的新秩序。