算法入门章——引出贯穿《算法导论》全书的算法分析和设计框架
刚刚认真学习了第二章,习题还未做。现在趁热打铁,先来凭空总结和回忆一下整个过程。
本章主要线索:通过引入两个算法,从插入排序分析和设计排序算法,引出了整本书后续各章节的算法设计和分析的框架。这个框架归纳起来即是:
引入问题并以实际情景进行思考-->抽象出精确的算法描述(这里就包含了所用的数据结构)-->伪码表示-->证明算法的正确性-->算法分析-->算法设计。
本章先引入的是插入排序算法。
1.实际情景:
插入排序可以以这个实际情景来设想:有一堆牌拿在右手上,你左手开始是空的。每次从右手拿一张牌,与左手已有的排从右到左的进行比较,并插在正确的位置,则左手在每一次插入后均是排好序的。直到右手没有牌时,则终止,此时全部排序完成。
2.算法描述:
输入:含有n个待排序的数组A。(a[1]、a[2]、...、a[n]);
输出:含有n个已排序的数组A。(a`[1]、a`[2]、...、a`[n]);
3.伪码表示:
INSERTION-SORT(A) //输入:待排序数组A
for j <-- 2 to length[A]
do key <-- A[j]
》对每一个待排序数组的元素,将其与已排序数组的元素从右向左的比较,找到合适位置时停止比较并插入
i <-- j-1
while(i > 0 and A[i] > key)//“从右向左比较,找到合适位置停止”包含两层意思:1.向左递进到起始位置则停止,2.找到合适位置则停止;所以这话不妨改为:“从右到左进行比较,到达起始位置或者找到合适位置时停止。(未到达且未找到时不停止)”
A[i+1] <-- A[i] /
i <-- i-1
A[i+1] <-- key
4.算法正确性证明:
先引入所谓"loop invariant'的概念:一般而言,用这个式子表示希望得到的结果,如果在循环的每一步,这个式子都是正确的,那么循环结束后,这个式子也正确,并得到了期望的结果.(有人建议译为循环不变性,因为这是种性质不一定是式子)
循环不变式证明遵循以下三步:1.初始化 2.保持 3.终止
先假设的循环不变式为:在每轮循环开始前,A[1]..A[j-1]是排好序的。
1.初始化:对于for循环就是在第一次测试之前,这里即是在j赋值为2并且在测试j<=length[A]之前。可以看到,这时j=2,此时A[1]是单个元素,是排好序的,满足。
2.保持:假设在某轮循环(j = k时)开始前,循环不变式成立,即A[1]..A[k-1]有序,则在下一轮循环(j = k+1)开始前,已经执行了循环部分,此时A[1]..A[k]成立,满足。
3.终止:j=n+1时,A[1]..A[j-1]即A[1]..A[n]有序,成立。
5.算法分析:
对于算法分析,需要一个模型,我们设计的是一种单处理器的随机存取机模型,指令一条条执行,没有并发。在算法分析中,实际要考虑的因素往往很多,而我们为了方便,只考虑大的因素,小的因素忽略掉。
现在开始分析。
先通过代码标注执行时间:
INSERTION-SORT(A) costtimes
for j <-- 2 to length[A]c1n
do key <-- A[j]c2n-1
》对每一个待排序数组的元素,将其与已排序数组的元素从右向左的比较,找到合适位置时停止比较并插入
i <-- j-1 c4n-1
while(i > 0 and A[i] > key)c5求和(j=2..n)*t(j)//由于while测试次数不定,故设为t(j)
A[i+1] <-- A[i] c6求和(j=2..n)*(t(j)-1)
i <-- i-1 c7求和(j=2..n)*(t(j)-1)
A[i+1] <-- keyc8n-1
所以T(n)=c1n+c2(n-1)+c4(n-1)+c5(求和(j=2..n)*t(j))+c6(求和(j=2..n)*(t(j)-1))+c7(求和(j=2..n)*(t(j)-1))+c8(n-1)
最好情况:即当数组本身就有序,则A[i] <= key,只测试一次,所以t(j)=1,化简得T(n) = (c1+c2+c4+c5+c8)n-(c2+c4+c5+c8),所以是满足@(n)的(@表示sita)
最坏情况:即循环j-1次,亦即while处测试j次,此时T(n) = (...)n^2+(...)n-(...)
在实际的算法分析中,若平均情况不好估计,一般分析的是最坏情况, 因为最坏情况一般与平均情况一样差,比如这里平均情况是:对于一个插入元素A[j],有一半元素小于它一半大于它,那么t(j) = j/2,亦得一个二次函数,故运行效率相等。
但有时我们仍然对平均情况或期望感兴趣,那么我们可以根据后面会介绍的概率分析技术来进行分析。
下面我们可对执行时间进行简化,因为考虑大规模输入时,相对于增长率来说,系数是次要的,故这里可以改写成:@(n^2)。不解释了吧。
6.设计更好的算法
我们既然分析了算法,就应该找出时间耗费的地方,根据其他的技术或思想来设计更好的算法。开始吧。
我们知道插入排序使用的思想是增量式的设计,还有一种常见的思想则是分治法。
分治法的框架是:1.分解 2.解决3.合并
根据这个框架我们设计出合并排序算法,同样遵循步骤:
1.实际情景:略
2.算法描述:略
3.伪码表示:略
4.算法正确性证明:增量式设计的算法如插入排序使用的证明方法是循环不变式;而对于这种分治法则采用递归主定理来证明即可。
5.算法分析:(可以画递归树来分析)
执行时间写成一般表达式是:
T(n)= @(1)n<=c时
aT(n/b)+D(n)+C(n)否则
解释如下:当规模小于c时,可以直接解决,执行常量时间;否则分解为a个子问题,每个子问题大小是原问题的1/b,D(n)是分解所用时间,C(n)是合并所用时间。
针对这个特定问题,c=1,a=b=2,D(n) = @(n),C(n) = @(n)
不用主定理的话,可以画出递归树来看,可以得到T(n) = cn * (lg n +1)
故T(n) = @(nlgn)
以上就是我对第二章的主线和内容总结。
下面是实现:
插入排序:
#include <iostream>
using namespace std;
void InsertionSort(int A[], int n){ //输入含有n个数的数组Aint length_A = n;for(int j=1; j<=length_A-1; j++){ //从右边挨个取数进行迭代比较int key = A[j];int i = j-1;while(i>-1 && A[i]>key){A[i+1] = A[i];i = i-1;}A[i+1] = key;}
}
//测试
int main(){int A[]={10,9,4,7,8,3,1};InsertionSort(A, 7);for(int i=0; i<7; i++)cout<<A[i]<<" ";return 0;
}
合并排序:
#include <iostream>
using namespace std;
#define SENTINEL 10000; //用一个比所有数字都大的值作为哨兵
void Merge(int* A, int p, int q, int r){int n1 = q-p+1;int n2 = r-q;int *L = new int[n1+1];int *R = new int[n2+1];for(int i=0; i<=n1-1; i++)L[i] = A[p+i-1];for(int j=0; j<=n2-1; j++)R[j] = A[q+j];L[n1] = SENTINEL;R[n2] = SENTINEL;int i = 0;int j = 0;for(int k=p-1; k<=r-1; k++){if(L[i]<=R[j]){A[k] = L[i];i++;}else{A[k] = R[j];j++;}}delete[] L;delete[] R;
}
void MergeSort(int* A, int p,int r){if(p<r){int q = (p+r)/2;MergeSort(A, p, q);MergeSort(A, q+1, r);Merge(A, p, q, r);}
}
int main(){int A[]={10,9,4,7,8,3,1,15,12};int length_A = 9;;MergeSort(A, 1, length_A);for(int i=0; i<length_A; i++)cout<<A[i]<<" ";return 0;
}
算法入门章——引出贯穿《算法导论》全书的算法分析和设计框架相关推荐
- 算法入门篇八 贪心算法
牛客网 左程云老师的算法入门课 贪心算法 贪心算法的解题步骤 例子 题目要求 解题策略 按照结束时间早的会议先安排,比如先安排[2,4],当4结束了,所有开始时间小于4的全部淘汰,[1,7].[3 ...
- 令人拍案叫绝的算法学习网站新手算法入门到精通,算法面试冲刺资料这里都有
(9月已更)学算法认准这6个网站就够了! 写在前面:作为ACM铜牌选手,从FB到腾讯,从事算法&java岗位工作也是5年有余.在工作中接触到了很多同学,在算法学习和算法面试这件事上我还是很有发 ...
- 令人拍案叫绝的算法学习网站,算法入门到精通,算法面试冲刺资料这里都有
前言 作为ACM铜牌选手,从FB到腾讯,从事算法&java岗位工作也是5年有余.在工作中接触到了很多同学,在算法学习和算法面试这件事上我还是很有发言权的. 今天就跟想学算法的同学分享一下我私藏 ...
- 有这样一套AI算法入门书,学习算法不再难
本系列图书将向读者介绍人工智能领域的各种热门主题.由于人工智能是一个庞大而繁杂的领域,并且其涵盖的内容与日俱增,任何一本书都只可能专注于特定领域,因此本书也无意成为一本巨细靡遗的人工智能教程. 本系列 ...
- 猿创征文|【算法入门必刷】数据结构-栈(二)
[算法入门必刷]算法入门-数据结构-栈(二) 前言 算法入门刷题训练 题目AB2: 栈的压入.弹出序列 题目分析 理论准备 题解 小结
- 猿创征文 |【算法入门必刷】数据结构-栈(三)
[算法入门必刷]算法入门-数据结构-栈(三) 前言 算法入门刷题训练 题目AB3:有效括号序列 题目分析 理论准备 题解 小结
- 猿创征文 |【算法入门必刷】数据结构-栈(四)
[算法入门必刷]算法入门-数据结构-栈(四) 前言 算法入门刷题训练 AB4:逆波兰表达式求值 题目分析 理论准备 题解 小结
- LDA算法入门(转)
LDA算法入门 一. LDA算法概述: 线性判别式分析(Linear Discriminant Analysis, LDA),也叫做Fisher线性判别(Fisher Linear Discrimin ...
- cordic算法反正切c语言,Cordic 算法之 反正切
在通信的算法中,常采用Cordic算法之一,知道角度产生正交的的正弦余弦, 或者知道正弦和余弦求角度,求反正切. 1. 求正弦和余弦值. 方法:旋转角度,得到正弦余弦值: 再旋转角度,到达下一个正弦余 ...
最新文章
- 小程序这件事 撸起袖子加油干
- mysql 分页测试,
- 1-36随机生成6个不重复的数
- Jpa规范原始编程步骤
- 1.RTMP流媒体服务器搭建
- Angular应用页面里_ngcontent属性的生成逻辑
- dll放在unity哪个文件夹下_unity中调用dll文件总结
- 软件集成策略故事连载----构建错误是怎么来的
- loadrunner11破解技巧
- 搭建Ubuntu下c/c++编译环境
- 前端导出excel,单独设置表头
- 5月电脑攒机配置推荐!
- 基于HFSS的圆形左旋圆极化贴片天线仿真分析
- 三阶魔方层先还原方法图解
- latex中表格、图片的排版
- 20145212罗天晨 逆向及Bof基础实践
- 低配本用win10服务器系统,低配电脑用win7还是win10比较好_低配置电脑装win7还是win10系统合适...
- 盛邀相聚贵阳,共赴“计算”之约,CNCC2022新闻发布会举行
- 从WAVE SUMMIT+2021,寻找新一代AI人不可或缺的“凝视”
- Flutter尽然还能有这种操作!隔壁都馋哭了
热门文章
- 利用gensim里word2vec训练实例——分析三国里人物关系
- Eclipse打开报错,The Eclipse executable launcher was unable to locate its companion shared library.
- APS系统如何让企业实现“多赢”?看高博通信是怎么做的
- kingcms php 下载,KingCMS企业版(PHP) v6.1.1641(Sp2)
- ubuntu装后的常用软件的安装与配置
- 从用户文件到系统驱动,全面清理c盘
- 今天,昆山向全世界发出邀请!
- 先进先出SQL Server 语句
- python+opencv遇到的错误(长期更新)
- 趣味数学:解24点游戏小技巧