这个寒假打算学习算法导论这本书,每学完一部分我都会尽量更新,希望在寒假结束时有一些收获!

书山有路勤为径,加油!

一、基础知识

1.算法在计算中的作用

算法其实就是任何良定义的计算过程,算法把给定的输入通过规定的计算步骤运算转换为输出。

数据结构是一种存储和组织数据的方式,为了使程序访问修改更加便利。

算法应该被作为一种技术,巧妙设计与合理使用算法将可以在空间和时间上降低复杂度,将可以有效地使用计算机的计算资源与存储空间。

2.算法基础

2.1 插入排序

以插入排序开始我们的算法之路。(我只会介绍算法的使用,具体复杂度分析暂不讨论)

对于少量元素的排序,插入排序是一个有效的算法。其算法的核心思想就是,首先将待排序的数组分为两类,一类是已排好序的部分,一类是暂时未排序的部分,初始状态下,已排序部分为空,未排序部分就是整个数组。接下来,每次取未排序部分的第一个元素,并从已排序部分找到合适的位置将这个元素插入,这个位置使得元素插入后已排序部分仍然有序。重复此过程,直至未排序部分为空,那么整个数组将完成排序。

对数组A[1,..,n]进行插入排序的算法如下(后续的算法均以类似C/C++风格的伪码给出):

INSERTION-SORT(A)
for(j=2;j<=A.length;j++){key=A[j];i=j-1;   //将A[j]插入到有序数组A[1,..,j-1]中,从小到大排列while(i>0 && A[i]>key){A[i+1]=A[i];i=i-1;}A[i+1]=key;
}

书中使用循环不变式证明算法的正确性,我将不会给出具体的证明过程,只来简要介绍一下循环不变式。

循环不变式必须证明三条性质:

初始化:循环的第一次迭代之前,它为真,即符合输出的目标要求。

保持:如果循环的某次迭代之前它为真,那么下次迭代之前它仍未真。

终止:在循环终止时,不变式提供了一个有用的性质,该性质有助于证明算法是正确的。

其实这类似于数学归纳法,需要证明基本情况与归纳步骤的正确性,不同之处在于循环不变式必须终止,终止的时候即停止归纳并且可以证明算法的正确性。

2.2 分析算法

分析算法的过程主要集中于求最坏情况运行时间,因为最坏情况运行时间给出了任何输入的运行时间的一个上界。

更简化的抽象一下,我们真正感兴趣的是运行时间的增长率,当输入规模n很大时,我们只考虑低阶项与系数就不重要了,于是可以记插入排序的最坏情况运行时间为Θ(n^2)。

如果一个算法的最坏情况运行时间具有比另一个算法更低的增长量级,那么通常认为前者比后者更有效。

2.3 设计算法

许多有用的算法在结构上是递归的,这些算法典型地遵循分治法的思想:

将原问题分解为规模较小的但类似于原问题的子问题,递归地求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

分治模式在每层递归时都有三个步骤:

分解原问题为若干子问题,这些子问题是原问题的规模较小的实例。

解决这些子问题,递归的求解各子问题。然而,若子问题的规模足够小,则直接求解。

合并这些子问题的解完成原问题的解。

以归并算法为例来说明分治法的思想:

分解:将待排序的n个元素分成各具n/2个元素的两个子序列

解决:使用归并排序递归递归地排序两个子序列,当子序列的长度为1的时候,可以直接求解即该子序列已排序

合并:将两个已排序的子序列合并以产生最终排序的结果

归并排序中将已排序的子序列A[p,..,q]和A[q+1,..,r]合并为有序子序列A[p,..,r]的算法如下:

MERGE(A,p,q,r)
n1=q-p+1;
n2=r-q;   //两个子序列的长度
//建立新数组L[1,..,n1+1]与R[1,..,n2+1]保存两个子序列
for(i=1;i<=n1;i++)
L[i]=A[p+i-1];
for(j=1;j<=n2;j++)
R[j]=A[q+j];
L[n1+1]=99999999;
R[n2+1]=99999999;   //哨兵牌,认为所有的数不会比它大,也可以用INT_MAX,实际使用时取一个极大的数就行
i=1,j=1;
for(k=p;k<=r;k++){  //每次取最小的数合并到数组A中,合并完成后A[p,..,r]有序
if(L[i]<=R[j]){
A[k]=L[i];
i++;
}
else{
A[k]=R[j];
j++;
}
}

于是依据分治法的思想,可以写出归并排序的算法:

MERGE-SORT(A,p,r)
if(p<r){     //如果数组中元素个数大于1则分解,否则不做处理即相当于求出一个仅含一个数的有序子序列
q=(p+r)/2;
MERGE-SORT(A,p,q);
MERGE-SORT(A,q+1,r);   //分解
MERGE(A,p,q,r);      //合并
}

可以证明,对于足够大的输入,在最坏情况下,运行时间为Θ(nlgn)的归并排序优于运行时间为Θ(n^2)的插入排序。

对于归并排序的最坏情况运行时间T(n)的递归式为:

T(n)=c  ,若n=1

T(n)=2T(n/2)+cn ,若n>1

依据此递归式或画出递归树,忽略低阶项与常系数可以得出归并排序的运行时间为Θ(nlgn)。

思考题中还提到了一种排序算法--冒泡排序,这种排序算法的思想就是反复交换相邻的未按次序排列的元素。每次将无序数列中的最大或最小值都通过交换放在无序数列的最后或最前,通过n-1次找出当前最值的过程便可以将数组排序。其算法如下:

BUBBLESORT(A)            //冒泡排序将A[1,..,n]从小到大排序
for(i=1;i<A.length;i++)
for(j=A.length;j>=i+1;j--)
if(A[j]<A[j-1]){
将A[j]与A[j-1]交换位置
}

3.函数的增长

这一章中记号很多,公式也不少,我不打算全部都说,对算法运行时间想有深入研究的话,可以再去钻研钻研。

3.1 渐进记号

这里我只说明一个记号--Θ记号,之前已经说过插入排序的最坏情况运行时间为T(n)=Θ(n^2),那么Θ的含义是什么呢?对于一个给定的函数g(n),用Θ(g(n))来表示以下函数的集合:

Θ(g(n))={f(n):存在正常量c1,c2和n0,使得对所有n>=n0,有0<=c1g(n)<=f(n)<=c2g(n)}

也就是说,对所有n>=n0,函数f(n)在一个常量因子内等于g(n)。称g(n)是f(n)的一个渐进紧确界。

一般来说,对任意多项式p(n)=a0*n^0+a1*n^1+...+ad*n^d,其中ai为常量且ad>0,那么p(n)=Θ(n^d)。

也把任意常量函数表示为Θ(1)。

3.2 标准记号与常用函数

这一节基本上是数学相关的知识与公式,可以自行学习。

4.分治策略

再来复习一遍分治策略的三个步骤:

分解(Divide)步骤将问题划分为一些子问题,子问题的形式与原问题一样,只是规模更小。

解决(Conquer)步骤递归地求解出子问题。如果子问题的规模足够小,则停止递归,直接求解。

合并(Combine)步骤将子问题的解组合成原问题的解。

把需要递归求解的子问题称为递归情况,把可以直接求解的子问题称为基本情况。

4.1 最大子数组问题

问题:给定数组A,寻找数组A中和最大的非空连续子数组。

如果使用暴力法枚举的话可以得出结果,不过这样时间复杂度太高。

考虑使用分治策略求解,假定我们要求A[low,..,high]的最大子数组A[i,..,j],A[low,..,high]的中央位置为mid,那么A[i,..,j]所处的位置必为以下三种情况之一:

①完全位于子数组A[low..mid]中,此时low<=i<=j<=mid

②完全位于子数组A[mid+1..high]中,此时mid+1<=i<=j<=high

③跨越中点,此时low<=i<=mid<j<=high

所以可递归求解A[low..mid]和A[mid+1..high]的最大子数组,再寻找跨越中点的最大子数组,然后在三种情况中选取和最大者。求解第三种情况时的最大子数组的算法描述如下:

FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high)
left_sum=-∞
sum=0
//跨越中点的子数组由A[i..mid]与A[mid+1..j]组成
for(i=mid;i>=low;i--){    //求最大子数组A[i..mid]
sum=sum+A[i];
if(sum>left_sum){
left_sum=sum;
max_left=i;
}
}
right_sum=-∞
sum=0
for(j=mid+1;j<=high;j++){  //求最大子数组A[mid+1..j]
sum=sum+A[j];
if(sum>right_sum){
right_sum=sum;
max_right=j;
}
}
return (max_left,max_right,left_sum+right_sum);  //返回跨越中点的最大子数组的下标与值

有了一个线性时间的FIND-MAX-CROSSING-SUBARRAY在手,就可以设计出采用分治策略求 A[low..high]的最大子数组问题的算法了:

FIND-MAXIMUM-SUBARRAY(A,low,high)
if(high==low)
return (low,high,A[low]);     //基本情况,返回下标及最大值
else{
mid=(low+high)/2;
(left_low,left_high,left_sum)=FIND-MAXIMUM-SUBARRAY(A,low,mid);  //情况①的最大子数组
(right_low,right_high,right_sum)=FIND-MAXIMUM-SUBARRAY(A,mid+1,high);  //情况②的最大子数组
(cross_low,cross_right,cross_sum)=FIND-MAX-CROSSING-SUBARRAY(A,low,mid,high);   //情况③的最大子数组
if(left_sum>=right_sum && left_sum>=cross_sum) return(left_low,left_high,left_sum);
else if(right_sum>=left_sum && right_sum>=cross_num) return(right_low,right_high,right_sum);
else return(cross_low,cross_high.cross_sum);    //返回三种情况中子数组和最大的子数组
}

可以计算得分治法求最大子数组问题的运行时间T(n)=Θ(nlgn),其渐进复杂性优于暴力求解。

实际上本题还可以采用动态规划进行求解,可以自行思考。

4.2 矩阵乘法的Strassen算法

按照线性代数中的矩阵乘法运算规则,两个n*n的矩阵进行乘法需要花费Θ(n^3)时间。

Strassen算法的核心思想是在分治求解的过程中让递归树不那么茂盛,即每次只递归求解7次而不是8次n/2*n/2矩阵的乘法。可以证明其运行时间为Θ(n^lg7),要优于普通的矩阵乘法计算方法。其算法用常数次的矩阵加减法减少了一次矩阵乘法,这个算法不是特别直观,我不做具体介绍,但通过这个算法我们可以知道通过减小递归树的宽度可以减小分治法过程的计算复杂度。

4.3 求解递归式

用递归式可以刻画分治算法的运行时间,求解递归式一般有三种方法:

①代入法

第一步,猜测解的形式。(书中说到猜测需要经验、创造力和一些启发式规则)

第二步,用数学归纳法求出解中的常数,并证明解是正确的。

②递归树方法

在递归树中,每个结点代表一个单一子问题的代价,子问题对应某次递归函数调用。我们将树中每层中的代价求和,得到每层代价,然后将所有层的代价求和,得到所有层次递归调用的总代价。根结点的代价支配了整棵树的代价。

③主方法

主方法依赖于主定理:

令a>=1和b>1是常数,f(n)是一个函数,T(n)是定义在非负整数上的递归式:

T(n)=aT(n/b)+f(n),则有以下结论:

(1)若

那么
(2)若

那么
(3)若

且对于某个常数c<1和所有充分大的

n有
那么

对于上述三种方法在此没有具体介绍,如果对分治算法需要具体分析可以参考书本进一步研究。

5.概率分析和随机算法

这一章需要掌握概率论的基本知识,我在此也不会特别具体的介绍,需要深入了解的话还请参考书本。

仅列举几个概念:

①数字1到n的n!种排列构成的排列表,如果n!种可能的排列每一种以等概率出现,则称为均匀随机排列

②在概率分析中,对所有可能输入产生的运行时间取平均,称为平均情况运行时间

③把一个随机算法的运行时间称为期望运行时间

④给定一个样本空间S和一个事件A,那么事件A对应的指示器随机变量I{A}定义为:

I{A}=1,如果A发生

I{A}=0,如果A不发生

⑤介绍两种随机排列数组的算法,这两种算法均可产生一个均匀随机排列。

第一种方法是为数组的每个元素A[i]赋一个随机的优先级P[i],然后依据优先级对数组A中的元素进行排序。这个过程如下:

PERMUTE-BY-SORTING(A)
n=A.length
int *P=new int[n+1] //新建数组P[1..n]
for(i=1;i<=n;i++)
P[i]=RANDOM(1,n^3)   //随机赋优先级
sort A using P as sort keys   //根据优先级对A进行重排

第二种方法是进行n次迭代,进行第i次迭代时,元素A[i]从A[i]到A[n]中随机选择产生,第i次迭代之后A[i]就不会变了。这个过程如下:

RANDOMIZE-IN-PLACE(A)
n=A.length
for(i=1;i<=;i++)
swap A[i] with A[RANDOM(i,n)]   //确定A[i]

书山有路之学习算法导论(一)--基础知识相关推荐

  1. 书山有路勤为径 学海无涯苦作舟(AI引领时代浪潮)

    书山有路勤为径 学海无涯苦作舟 AI模型是如何训练的? 数据准备 在训练AI模型之前,需要准备大量的数据集.数据集的质量和多样性直接影响模型训练的效果.数据集准备的主要工作包括: 数据清洗:清除数据中 ...

  2. 解读韩愈治学名联-书山有路勤为径,学海无涯苦作舟

    出处:中国唐代著名诗人.哲学家韩愈所写对联:"书山有路勤为径,学海无涯苦作舟". 解读:要登上知识的顶峰,勤奋就是上山的小路:要渡过无边学海,刻苦就是渡海的小船. 自身领会:现代知 ...

  3. 书山有路勤为径(一)

    书山有路勤为径(一) 这篇记录读书,激励多读些书吧 before2013 三国演义 作者:罗贯中 工作之前,除了教科书练习册外,乱七八糟书看了不少,有军事的,讲飞机坦克军舰:有天文的,讲亿万光年以外: ...

  4. 学习python需要什么基础-学习Python需要哪些基础知识?

    今天是腊月二十七,给各位朋友拜个早年! Python学习可以分为几个阶段,入门.进阶.应用. 先说说入门需要哪些基本的知识储备. Python因为易于学习的特点,入门很简单,掌握基本的Python知识 ...

  5. 学python需要什么基础-学习Python需要哪些基础知识?

    今天是腊月二十七,给各位朋友拜个早年! Python学习可以分为几个阶段,入门.进阶.应用. 先说说入门需要哪些基本的知识储备. Python因为易于学习的特点,入门很简单,掌握基本的Python知识 ...

  6. 小猪的Python学习之旅 —— 1.基础知识储备

    小猪的Python学习之旅 -- 1.基础知识储备 引言: (文章比较长,建议看目录按需学习-) 以前刚学编程的时候就对Python略有耳闻,不过学校只有C,C++,Java,C#. 和PHP有句&q ...

  7. 【系统分析师之路】第六章 多媒体基础知识

    [系统分析师之路]第六章 多媒体基础知识 要求考生掌握的知识点有: 1.信息系统综合知识:多媒体压缩编码与存储技术.一般上午会考的知识点有:多媒体应用框架标准,蓝光DVD,媒体分类,采样,量化,JPE ...

  8. 【系统分析师之路】第十七章 多媒体基础知识(章节重点)

    [系统分析师之路]第十七章 多媒体基础知识(章节重点) 第十七章 多媒体基础知识章节重点 [系统分析师之路]第十七章 多媒体基础知识(章节重点) 章节重点 一. 多媒体技术基本概念(★★) 1)基本概 ...

  9. 《Java并发编程实践》学习笔记之一:基础知识

    <Java并发编程实践>学习笔记之一:基础知识 1.程序与进程 1.1 程序与进程的概念 (1)程序:一组有序的静态指令,是一种静态概念:  (2)进程:是一种活动,它是由一个动作序列组成 ...

最新文章

  1. 8088微型计算机pdf,微型计算机原理与接口技术:第3章 8086-8088微处理器及其体系结构.pdf...
  2. linux c++ 程序运行时间,总结UNIX/LINUX下C++程序计时的方法
  3. 你与弄懂promise之间可能只差这篇文章(二)
  4. 2个字节能存多少个16进制_Java语言中最大的整数再加1等于多少?看完秒懂
  5. UIAutomator输入中文
  6. 互联网寒冬来袭,有一家公司却逆流而上!
  7. MFC中,多个Button响应同一个事件
  8. python写我爱你_12个精选Python教程我的初恋故事。
  9. mysql索引简单介绍
  10. linux中硬链接为什么不能跨分区
  11. ActivityScenario启动失败Activity never becomes requested state [RESUMED, STARTED, CREATED, DESTROYED]
  12. Jetson Nano | darknet (yolov3.4-tiny)摄像头实时检测
  13. 【文献数据速递】CEO绿色经历能否促进企业绿色创新
  14. 使用hardhat开发以太坊智能合约-测试合约
  15. 白鹭安装node_Mac OS X 系统下安装和部署Egret引擎开发环境
  16. Query意图识别分析
  17. Visual C++ 运行窗口一闪而过的解决方法
  18. AtCoder Grand Contest 004 A - Divide a Cuboid 题解
  19. 移动千牛开放体验治理实践与防治方案
  20. 组件化开发之路由器模块详解(ActivityRouter源码详解)

热门文章

  1. Ant Design Vue-cascader:修改默认值
  2. QM报告:闲置交易跻身“宅经济”前5,转转集团领跑二手手机交易
  3. android 系统打开USB调试模式
  4. 咖说 | 硅谷“加密黑帮”大揭秘:一览 37 家科技圈区块链初创企业
  5. 华丽成长为IT高富帅、IT白富美(十三)
  6. 【华为OD机试B卷,GO题解独家】最佳植树距离、种树
  7. 内存管理(操作系统笔记四)
  8. CyclicBarrier
  9. GIS矢量数据的复制粘贴
  10. 学习Python:从零到英雄