排序算法与算法分析基础

输入:n个数(a1, a2, a3, …an)。
输出:输入序列的一个排列,重新排序(b1, b2, b3,…bn),满足b1<=b2<=b3<=…<=bn。

排序算法

插入排序(INSERTION_SORT)

首先是插入排序,这是一个针对少量元素排序的有效算法,很形象的是和我们在打牌时整理手中的牌的顺序差不多。先看一下它的伪代码:
key是当前待排序的数。

INSERTION_SORT(A) pseudocode
for j<--- 2 to length[A]do key<---A[j]//Insert A[j] into the sorted sequence A[1,2,...,j-1]i<---j-1while i>0 and A[i]>keydo A[i+1]<---A[i] i<---i-1A[i+1]<---key

实现程序

#include<iostream>
#include<cstdio>
using namespace std;void INSERTION_SORT(int *array, int size)
{int key;for(int i=1;i<size;++i){key=*(array+i);int j=i-1;while((j>=0)&&(*(array+j)>=key)){*(array+j+1)=*(array+j);--j;}*(array+j+1)=key;}
}int main()
{int Num[10]={8,12,3,4,1,23,11,9,24,31};INSERTION_SORT(Num,10);for (int i=0;i<10;++i){printf("%d\t",Num[i]);}cin.get();return 0;
}

合并排序(MERGE SORT)

有很多算法在结构上是递归的,通过一次或多次调用自身来递归解决相关问题。这些算法通常采用分治策略:将原问题划分为n个规模较小而结构与原问题相同的子问题,递归地解决这些子问题,然后再合并其结果,就得到了原问题的解。
分治模式在每一层递归上都有三个步骤:

  • 分解:将原问题分解为一系列的子问题
  • 解决:递归地解决各个子问题,若子问题足够小,则直接求解
  • 合并:将子问题的解合并成原问题的解

合并排序算法完全依照了上述模式:

  • 分解:将n个元素分解为各含n/2个元素的子序列
  • 解决:用合并排序法对两个子序列递归地进行排序
  • 合并:合并两个已排序的子序列得到结果

在对子序列进行排序时,长度为1时递归结束,单个元素认为是已经排好序的。合并排序的关键步骤在于排序过程中两个已排序好的序列的合并,在此引入一个子过程辅助合并MERGE(A,p,q,r),其中A是数组,p、q、r是数组的下标,满足p<=q<r,该过程实现两个已经排序好的数组A[p,q]、A[q+1,r]的合并。
MERGE(A,p,q,r)的时间代价是θ(n),n=r-p+1为元素的个数,因为最多进行n次比较。它的实现过程是:每次取两个序列中最小的元素(每个排序好的序列的第一个或者最后一个)进行比较,然后将两者中较小的一个按序放在输出数组中,重复此步骤直到某个序列被比较完。在程序的实现过程中,为了避免每次检查序列是否被比较完,在此引入“哨兵”的概念。在每个序列末尾添加一个哨兵,其包含一个特殊值,用来指示序列的结束(C++容器中就是用此概念标志容器的结束)。在这里用“∞”作为哨兵值,因为他不可能是两者中比较小的,除非是两个序列都到了结束的地方,而此时已经进行了n次比较,算法可以停止了。

MERGE(A,p,q,r)
A[p,q]   n1<---q-p+1
A[q+1,r] n2<---r-q
//create arrays L[1,...,n1+1] and R[1,...,n2+1]
for i<---1 to n1L[i]<---A[p+i-1]
for j<---1 to n2R[j]<---A[q+j]
L[n1+1]<---∞
R[]n2+1<---∞
i<---1
j<---1
for k<---p to rdo if L[i]<=R[j]then A[k]<---L[i]i<---i+1else A[k]<---R[j]j<---j+1

现在就可以把MERGE作为一个子过程用在MERGE_SORT过程中了,MERGE_SORT(A,p,r) 实现了对数组A[p…r]的排序。如果p>=r,则只有一个元素,就是已经排序好的了。若p<r,分解步骤就计算出下一个下标q,将A[p…r]分解为A[p…q]和A[q+1…r]。

MERGE_SORT(A,p,r)
if p<rthen q<---[(p+r)/2](向下取整)MERGE_SORT(A,p,q)MERGE_SORT(A,q+1,r)MERGE(A,p,q,r)

下图自底向上展示了当n为2的幂数时,整个过程的操作。算法将递归到每个序列元素为1,然后将两个长度为1的序列合并成已排序好的、长度为2的序列,接着又把两个长度为2的已排序的序列合并成长度为4的已排序好的序列。一直进行到将两个长度为n/2的已排序好的序列合并成长度为n的排序好的序列。

实现程序

#include<iostream>
#include<cmath>
#define INT_MAX 9999//~(1<<(sizeof(int)*8-1))
using namespace std;void MERGE(int *array, 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;++i){L[i]=*(array+p+i);}for(int i=0;i!=n2;++i){R[i]=*(array+q+1+i);}L[n1]=INT_MAX;R[n2]=INT_MAX;for(int k=p,i=0,j=0;k!=r+1;++k){if(L[i]<=R[j]){*(array+k)=L[i];++i;}else{*(array+k)=R[j];++j;}}delete [] L;delete [] R;
}void MERGE_SORT(int *array, int p, int r)
{if(p<r){int q=floor((p+r)/2);MERGE_SORT(array,p,q);MERGE_SORT(array,q+1,r);MERGE(array,p,q,r);}
}
int main()
{int Num[10]={8,12,3,4,1,23,11,9,24,31};MERGE_SORT(Num,0,9);for(int i=0;i!=10;++i){cout<<Num[i]<<"\t"<<flush;}cin.get();return 0;
}

算法分析

算法分析指对一个算法所需要的资源进行预测。内存、通信带宽或者计算机硬件等资源是我们经常需要关心的,但一般资源指的是我们希望测度的计算时间。当采用RAM模型作为实现技术的时候,RAM包含了真实计算机中常见的指令:算术指令(加、减、乘、除、取余、向上取整、向下取整)、数据移动指令(装入、存储、复制等指令)和控制指令(条件和非条件转移、子程序调用和返回指令),其中每条指令所需的时间都为常量。
算法的运行时间指的是,在特定输入的情况下,所执行的基本操作数,可以方便地定义独立于机器的“步骤”的概念。采用以下观点:每个步骤执行都需要花费一定的时间,虽然每个步骤所需时间不同,但是我们假定每次执行第i行所花费的时间为常量ci。

插入排序分析


该算法的运行时间是所有语句执行的时间之和:

T(n)=c1n+c2(n−1)+c4(n−1)+c5∑i=2ntj+c6∑i=2n(tj−1)+c7∑i=2n(tj−1)+c8(n−1)

T(n)=c_1n+c_2(n-1)+c_4(n-1)+c_5\sum_{i=2}^nt_j+c_6\sum_{i=2}^n(t_j-1)+c_7\sum_{i=2}^n(t_j-1)+c_8(n-1)
如果给定输入非常理想,序列都已经按序排列好,即对于每个j,A[j]都大于A[1,..,j-1],即 A[i]<keyA[i] 恒成立,所以第6行和第7行没有执行过,对于第5行,由于 A[i]>keyA[i]>key 不成立,所以 tj=1t_j=1 。则算法最佳运行时间为:

T(n)=c1n+c2(n−1)+c4(n−1)+c5(n−1)+c8(n−1)=(c1+c2+c4+c5+c8)n−(c2+c4+c5+c8)

T(n)=c_1n+c_2(n-1)+c_4(n-1)+c_5(n-1)+c_8(n-1) =(c_1+c_2+c_4+c_5+c_8)n-(c_2+c_4+c_5+c_8)
可以表示为 an+ban+b,依赖于时间常量 cic_i,为输入规模的线性函数。
而如果输入序列是逆序输入的,则会出现最坏的情况,A[1,…,j-1]都需要和key进行比较,因而对于j=2…n, tj=jt_j=j,此时最长的算法时间为:

T(n)=c1n+c2(n−1)+c4(n−1)+c5(n(n+1)2−1)+c6n(n−1)2+c7n(n−1)2+c8(n−1)=(c52+c62+c72)n2+(c1+c2+c52−c62−c72)n−(c2+c4+c5+c8)

T(n)=c_1n+c_2(n-1)+c_4(n-1)+c_5(\frac{n(n+1)}{2}-1)+c_6\frac{n(n-1)}{2}+c_7\frac{n(n-1)}{2}+c_8(n-1) =(\frac{c_5}{2}+\frac{c_6}{2}+\frac{c_7}{2})n^2+(c_1+c_2+\frac{c_5}{2}-\frac{c_6}{2}-\frac{c_7}{2})n-(c_2+c_4+c_5+c_8)
此时运行时间为输入规模的二次函数。即最坏情况下其增长率为 θ(n2)θ(n^2)。

合并排序分析

当一个算法中含有对自身的递归调用时,其运行时间可以用一个递归方程来表示。该方程通过描述子问题与原问题之间的关系,来给出总的运行时间。
设T(n)T(n) 为规模n的总的运行时间,假设我们把原问题分解为a个子问题,每一个子问题大小是原问题的1/b(对于合并算法,a=b=2,但是在许多其他分治算法中,a≠b),如果分解该问题和合并解的时间分别为D(n)和C(n)D(n)和C(n), 则:

T(n)={θ(1),aT(n/b)+D(n)+C(n),if n <=c(较小的一个常量)否则

T(n)=\begin{cases} θ(1), & \text{if $n$
在这里假定MERGE_SORT的输入规模为2的幂数,这样每一次分解所产生的序列长度都为n/2,考虑最坏情况下的运行时间:

  • 分解:这一步仅仅是计算出了子数组的中间位置,需要常量时间,因此时间为θ(1);
  • 解决:递归解决两个规模为n/2子问题,时间为2T(n/2)2T(n/2);
  • 合并:进行n次比较赋值,因此时间为θ(n);
T(n)={θ(1),2T(n/2)+θ(n),if n =1if n >1

T(n)=\begin{cases} θ(1), & \text{if $n$ =1} \\ 2T(n/2)+θ(n), & \text{if $n$ >1} \end{cases}
可将上式具体表示如下:

T(n)={c,2T(n/2)+cn,if n =1if n >1

T(n)=\begin{cases} c, & \text{if $n$ =1} \\ 2T(n/2)+cn, & \text{if $n$ >1} \end{cases}
将递归扩展成一种等价树进行表示, cncn是树根, 2T(n/2)2T(n/2)是两个两颗子树,每一个子树节点的代价都是 cn/2cn/2,继续在树中扩展节点,直到问题规模降到了1。

接下来给这棵树的每一层加上相应的代价,树根的代价为 cncn,下一层的两个节点的代价分别为 cn/2cn/2。一般来说顶层之下第i拥有 2i2^i个节点,而相应的每个节点的代价为 (cn/2i)(cn/2^i),因而每层的总代价都为 cncn。”递归树“中总的层数为 lgn+1lgn+1,比如n为2时,树有2层。在该树中,每一层的代价为 cncn,层数为 lgn+1lgn+1,所以总的代价为:

cn(lgn+1)=cnlgn+cn

cn(lgn+1)=cnlgn+cn
忽略低阶项和常量项,可得到其时间增长率为 θ(nlgn)θ(nlgn)。也就是说但规模n达到一定程度时,最坏情况下的合并排序也比最佳情况下的插入排序速度要快( lgnlgn比任何线性函数都要增长的慢)。

算法真是一个神奇而又强有力的利器,特别是在用到递归算法的时候感触特别深,一个递归运算可以精简多少行代码啊。这只是对算法有一个初步的认识,下面将要深入学习一下算法设计过程中的循环不变式,当算法复杂到一定程度时,使用循环不变式对算法进行分析论证是不可避免的。

祝枫
2016年7月4日于深圳

算法学习一:排序算法实现与算法性能分析相关推荐

  1. Python与Matlab算法学习一文通(快速排序算法)(更新中)

    想利用一些空余时间学一学python与matlab,与同学建立不知道能坚持多久的学习联盟,每周一部分题目,利用一周时间完成原理文档与程序编写.由于主要研究方向为其他方向,因此只会利用很少的空闲时间来学 ...

  2. 基础算法学习大纲(附加yxc大佬算法模板)

    基础算法学习大纲总结 学习算法路线 1.基础算法 模板 1.排序 2.二分 3.高精度 4.前缀和与差分 5.双指针算法 6.位运算 7.离散化 8.区间合并 2.数据结构 模板 1.链表与邻接链表( ...

  3. 折半查找的思想及源码_结构与算法(04):排序规则与查找算法

    一.递归算法 递归就是方法自己调用自己,每次调用时传入不同的变量,可以让代码变得简洁.递归算法在计算机科学中是指一种通过重复将问题分解为同类的子问题而解决问题的方法,递归式方法可以被用于解决很多的计算 ...

  4. 结构与算法(04):排序规则与查找算法

    本文源码:GitHub·点这里 || GitEE·点这里 一.递归算法 递归就是方法自己调用自己,每次调用时传入不同的变量,可以让代码变得简洁.递归算法在计算机科学中是指一种通过重复将问题分解为同类的 ...

  5. lm opencv 算法_Levenberg–Marquardt算法学习(和matlab的LM算法对比)

    回顾高斯牛顿算法,引入LM算法 惩罚因子的计算(迭代步子的计算) 完整的算法流程及代码样例 1.      回顾高斯牛顿,引入LM算法 根据之前的博文:Gauss-Newton算法学习 假设我们研究如 ...

  6. 算法学习笔记13:哈希算法

    哈希算法(上):如何防止数据库中的用户信息被脱库 什么是哈希算法 应用一:安全加密 应用二:唯一标识 应用三:数据校验 应用四:散列函数 解答开篇 哈希算法(下):哈希算法在分布式系统中有哪些应用 应 ...

  7. 算法学习笔记 网络流之最大流算法

    文章目录 26.1 流网络 1. 流网络和流 2. 流的一个例子 3. 使用反平行边来建模问题 4. 具有多个源点和多个汇点的网络 26.2 *Ford-Fulkerson* 方法 1. 残存网络 2 ...

  8. 选择排序 C++代码实现及性能分析 恋上数据结构笔记

    文章目录 复习梗概 算法思想及时间复杂度 选择排序的优化 代码及输出 完整代码 复习梗概 选择排序算法图解 选择排序在什么地方进行元素的调换 选择排序在什么地方优化,优化后的算法 时间复杂度分析 算法 ...

  9. 算法学习之排序(1)--插入排序

    一.算法描述 一般来说,插入排序都采用in-place在数组上实现.具体算法描述如下: 1. 从第一个元素开始,该元素可以认为已经被排序 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描 3. ...

  10. 【推荐算法 学习与复现】-- 逻辑回归算法族 -- LR

    协同过滤仅仅使用有限的用户行为信息,逻辑回归算法模型大多引入用户行为.用户特征.物品特征和上下文特征等,从CF逐步过渡到综合不同特征的机器学习模型. (1)逻辑回归模型 将用户特征(年龄.性别等).用 ...

最新文章

  1. 学习 PHP SOAP 扩展的一些笔记
  2. WPF之DataGrid篇:DataGridComboBoxColumn
  3. 输入法——讨厌的全角
  4. oracle清理asm归档日志,【Oracle】 rman 删除归档日志的命令
  5. telnet服务下载 Linux,linux telnet服务安装包
  6. 获取数据库内容放入下拉框中
  7. libevent在windows平台下通过vs进行编译
  8. Linux系列在线培训五月盛情开幕!!(5月9日,10日,16日,17日,23日,24日)18:30 - 21:30,
  9. android无网络状态栏,Android中检查网络连接状态的变化,无网络时跳转到设置界面...
  10. 最近,前端开发把我恶心着了,为了甩锅,我写了个牛逼的日志切面!
  11. CMU 15-213 Introduction to Computer Systems学习笔记(11) Cache Memories
  12. Trello:轻量级团队流程协作和列表管理平台[转自http://www.36kr.com/p/46852.html]
  13. LSTM神经网络图解
  14. 区块链技术正大肆颠覆价值数十亿美元的金融科技产业
  15. php几种常用的加密解密算法
  16. Bag of Tricks for Image Classification with Convolutional Neural Networks(卷积神经网络在图像分类中的技巧)
  17. 奇异值分解的物理意义
  18. 使用vi编辑器创建文本文件
  19. SolidWorks工装 焊接 夹具 治具 检具 3D图档-350套(9.2G)
  20. 如何搭建一套免费开源的微信群机器人问答系统?

热门文章

  1. java代理模式与装饰者模式
  2. 码表的理解(ASCII,GBK,Unicode,UTF-8等)。
  3. HDU 1027 G - Can you answer these queries?
  4. Java多线程、主线程等待所有子线程执行完毕、共享资源
  5. Div Vertical Menu ver2
  6. 【Java从0到架构师】Spring - 纯注解开发
  7. [转]MySQL实现over partition by(分组后对组内数据排序)
  8. Linux命令之sed使用入门概述
  9. 南昌大学利用计算机作弊怎样处分,关于江西南昌大学医学院计算机中心教师组织全国计算机二级考试集体作弊的意见书...
  10. 大厂HR:“不会数据分析,你还想干运营?”