【MIT算法导论】算法分析与基础知识

前言

本文是B站《算法导论》MIT公开课的随课笔记,关于内容和笔记格式有问题的朋友欢迎私信交流~


一.基本概念

  1. 算法分析:

是对计算机性能以及计算机资源的研究

  1. 除去【性能】,程序还有哪些重要的性质:
  • 正确性,简洁,可维护性,健壮性,模块性,安全性…

即使存在着以上这些比性能更加重要的特质,那么为什么还要研究算法与性能?

  • 性能的好坏决定可行与否,算法总是处在前沿阶段,设想将不可行变得可行。
    性能之于上面这些其他特质的关系就相当于一叠钞票和一系列生存物资的关系,是基础保障。

    所以我们需要研究算法和性能,是因为有了性能的保障,我们才能更好地追求程序的其他特性。

二.引论问题——排序

问题描述:将一系列元素(a1,a2,…aN)进行排序后重新输出,要求排序后的元素是非递减的。

问题解决:插入排序等多种经典排序方案

(一)插入排序(Insertion Sort)

  1. 概念、过程与思想

(1)本质:就是程序始终维持一个有序的部分,表示数组中已经排序好的部分;而程序运行就是为了能够使得这一部分不断增长,直到数组的所有部分都是排序好的。
在这里插入图片描述

(2)过程:伪代码+过程理解

void InsertionSort(A[],n)//对数组A[1...n]中的元素进行排序
{
  for(int j = 2;j<=n;j++){
    int key = A[j];
    int i = j-1;
    while(c>0 && A[i]>key){
      A[i+1] = A[i];
      i--;
    }
    A[i+1] = key;
  }  
}

①上述第一个for循环,是每次都考虑一个未有序部分的元素。

②第二个while循环,就是对每一个元素,选择一个合适的位置进行插入,而插入点之后的元素,都要顺次后移一个位置。

  1. 运行时间分析

运行时间Runtime相关的因素:
输入本身(结构)/
输入规模 /
运行时间的上界

(1)最好结果
是考虑了某些特定的输入,具有欺骗性质的假象,作用不大。

(2)平均结果
考虑一个加权平均的计算,每一种可能出现的输入的耗时和该情况出现的概率对应的加权平均。

如何得知每种运行结果的概率呢?
——需要做一个假设,假定运行情况结果符合均匀分布。

(3)最坏结果

  • 评估最坏结果时常使用的一些参量:
    计算机 (相对/绝对)速度
  • 最坏结果代表了程序运行的上界,这往往代表着对用户的一种承诺,具有实际意义。

(二)算法的大局观【渐进思想】

——忽略掉那些依赖于机器的常量
——不是去检查实际的运行时间,而是关注运行时间的增长

  1. 【关注时间增长的思路】渐进符号

θ符号,其作用是:为写进的公式,舍去它的低阶项,忽略前面的常数因子。
在这里插入图片描述

  1. 渐进符号的意义

E.g.一个θ(n2)的算法始终要比θ(n2)的算法运行快

①理解1:当n→∞时,你始终可以找到一个n,使得n2小于n3
②理解2:上述结论纵然是在一个慢速计算机上也是适用的。

渐进符号能同时满足我们对相对和绝对速度的双重比较。

  1. 从工程角度合理利用渐进思想
    在这里插入图片描述

①在以上的两个图形比较下,总能找到一个输入规模状态点n0,使得之后所有n3都要慢于n2

②但是很多时候,n0这个点在实际工程问题中可能因为过大而不合理。所以我们常常也会关注一些慢速算法,因为它们仍然可以在合理规模的输入下运行得更快

(三)插入排序worst case下的复杂度的渐进分析

  1. 分析原则

①因为是渐进分析,所以我们把过程中的每项操作都看成是相差无几的基本操作

②我们采用计数的方法,是内存引用计数,实际上访问了某个变量的次数。

③因为是worst case,所以考虑的是对输入逆序的一组元素,通过排序排列成正序的情况。

  1. 分析结果
    在这里插入图片描述

根据渐进分析,在输入规模较小的情况下,插入排序的效率还是可观的,但是输入规模一旦增大,则效率会降低。

(四)归并排序的基本思想和渐进分析

  1. 算法框架和伪码表示
void MergeSort(A[])//对数组A[1...n]中的元素进行有向排序
{
  if(n==1)
      return ;
  sort(A,1,n/2);
  sort(A,n/2+1,n);
  merge(A,1,n/2,n/2+1,n);
}

(1)思想:归并排序的本质就是,分而治之。先让待排序的一组元素,分成两组已经有序的元素,再对这两组已经有序的元素进行合并。

(2)merge操作的理解
在这里插入图片描述

//对有序数组a和有序数组b中的元素进行合并
void merge(){
  int i = 0,j = 0;//指向a,b数组中元素的指针
  int p = 0;
  while(i<a.length()&&j<b.length()){
      if(a[i]<b[j]){
          c[p++] = a[i++];
      }
      else{
          c[p++] = b[j++];
      }
  }
  while(i<a.length()){
      c[p++] = a[i++];
  }
  while(j<b.length()){
      c[p++] = b[j++];
  }
}
  1. 时间复杂度的渐进分析

(1)merge操作的渐进分析

  • 线性时间
  • Time = θ(n) on n total elements

(2)递归程序的时间递归推导
在这里插入图片描述
(3)推导出T(n)的通用表达式——递归树方法
在这里插入图片描述

在渐进情况下,nlogn的复杂度是要低于n2的复杂度的。

也就是说,渐进情况下,归并排序胜过插入排序。

猜你喜欢

转载自blog.csdn.net/kodoshinichi/article/details/108422356