Ch2算法基础

            whowhoha@outlook.com

2.1 插入排序

输入:n个数的一个序列〈a1,a2,…,an〉。

输出:输入序列的一个排列〈a′1,a′2,…,a′n〉,满足a′1≤a′2≤…≤a′n。

我们希望排序的数也称为关键词。插入排序,插入排序的工作方式像许多人排序一手扑克牌。开始时,我们的左手为空并且桌子上的牌面向下。然后,我们每次从桌子上拿走一张牌并将它插入左手中正确的位置。为了找到一张牌的正确位置,我们从右到左将它与已在手中的每张牌进行比较,如下图所示。拿在左手上的牌总是排序好的,原来这些牌是桌子上牌堆中顶部的牌。

对于插入排序,伪代码过程命名为INSERTION-SORT,其中的参数是一个数组A[1..n],包含长度为n的要排序的一个序列。(在代码中,A中元素的数目n用A.length来表示。)该算法原址排序输入的数:算法在数组A中重排这些数,在过程INSERTION-SORT结束时,输入数组A包含排序好的输出序列。
INSERTION-SORT(A)
1 for j = 2 to A.length
2  key = A[j]
3  // Insert A[j] into the sorted sequence A[1..j - 1].
4  i = j - 1
5  while i > 0 and A[i] > key
6    A[i+1] = A[i]
7    i = i - 1
8  A[i + 1] = key

循环不变式与插入排序的正确性

上图表明对A=〈5,2,4,6,1,3〉该算法如何工作。下标j指出正被插入到手中的“当前牌”。在for循环(循环变量为j)的每次迭代的开始,包含元素A[1..j-1]的子数组构成了当前排序好的左手中的牌,剩余的子数组A[j+1..n]对应于仍在桌子上的牌堆。事实上,元素A[1..j-1]就是原来在位置1到j-1的元素,但现在已按序排列。我们把A[1..j-1]的这些性质形式地表示为一个循环不变式:

在第1~8行的for循环的每次迭代开始时,子数组A[1..j-1]由原来在A[1..j-1]中的元素组成,但已按序排列。

在数组A=〈5,2,4,6,1,3〉上INSERTION-SORT的操作。数组下标出现在长方形的上方,数组位置中存储的值出现在长方形中。(a)~(e)第1~8行for循环的迭代。每次迭代中,黑色的长方形保存取自A[j]的关键字,在第5行的测试中将它与其左边的加阴影的长方形中的值进行比较。加阴影的箭头指出数组值在第6行向右移动一个位置,黑色的箭头指出在第8行关键字被移到的地方。(f)最终排序好的数组

循环不变式主要用来帮助我们理解算法的正确性。关于循环不变式,我们必须证明三条性质:

初始化:循环的第一次迭代之前,它为真。

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

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

当前两条性质成立时,在循环的每次迭代之前循环不变式为真。(当然,为了证明循环不变式在每次迭代之前保持为真,我们完全可以使用不同于循环不变式本身的其他已证实的事实。)注意,这类似于数学归纳法,其中为了证明某条性质成立,需要证明一个基本情况和一个归纳步。这里,证明第一次迭代之前不变式成立对应于基本情况,证明从一次迭代到下一次迭代不变式成立对应于归纳步。

第三条性质也许是最重要的,因为我们将使用循环不变式来证明正确性。通常,我们和导致循环终止的条件一起使用循环不变式。终止性不同于我们通常使用数学归纳法的做法,在归纳法中,归纳步是无限地使用的,这里当循环终止时,停止“归纳”。

让我们看看对于插入排序,如何证明这些性质成立。

初始化:首先证明在第一次循环迭代之前(当j=2时),循环不变式成立?。所以子数组A[1..j-1]仅由单个元素A[1]组成,实际上就是A[1]中原来的元素。而且该子数组是排序好的(当然很平凡)。这表明第一次循环迭代之前循环不变式成立。

保持:其次处理第二条性质:证明每次迭代保持循环不变式。非形式化地,for循环体的第4~7行将A[j-1]、A[j-2]、A[j-3]等向右移动一个位置,直到找到A[j]的适当位置,第8行将A[j]的值插入该位置。这时子数组A[1..j]由原来在A[1..j]中的元素组成,但已按序排列。那么对for循环的下一次迭代增加j将保持循环不变式。

第二条性质的一种更形式化的处理要求我们对第5~7行的while循环给出并证明一个循环不变式。然而,这里我们不愿陷入形式主义的困境,而是依赖以上非形式化的分析来证明第二条性质对外层循环成立。

终止:最后研究在循环终止时发生了什么。导致for循环终止的条件是j>A.length=n。因为每次循环迭代j增加1,那么必有j=n+1。在循环不变式的表述中将j用n+1代替,我们有:子数组A[1..n]由原来在A[1..n]中的元素组成,但已按序排列。注意到,子数组A[1..n]就是整个数组,我们推断出整个数组已排序。因此算法正确。

插入排序C实现代码如下

int src[6] = {5,3,7,9,2,4};

int len = sizeof(src) / sizeof(int);

void sort_insert(int *array,int len)

{

int i,j,key;

for (j= 1;j<len;j++)

{

key = array[j];

i=j-1;

while( i >=0 && array[i]>key)//改为 while( i >=0 && array[i]<key)升序排序(练习2-1-2)

{

array[i+1]=array[i];

i--;

}

array[i+1] = key;

}

}

算法导论_ch2的更多相关文章

  1. B树——算法导论(25)

    B树 1. 简介 在之前我们学习了红黑树,今天再学习一种树--B树.它与红黑树有许多类似的地方,比如都是平衡搜索树,但它们在功能和结构上却有较大的差别. 从功能上看,B树是为磁盘或其他存储设备设计的, ...

  2. 红黑树——算法导论(15)

    1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极 ...

  3. 基本数据结构(2)——算法导论(12)

    1. 引言     这一篇博文主要介绍链表(linked list),指针和对象的实现,以及有根树的表示. 2. 链表(linked list) (1) 链表介绍      我们在上一篇中提过,栈与队 ...

  4. 堆排序与优先队列——算法导论(7)

    1. 预备知识 (1) 基本概念     如图,(二叉)堆是一个数组,它可以被看成一个近似的完全二叉树.树中的每一个结点对应数组中的一个元素.除了最底层外,该树是完全充满的,而且从左向右填充.堆的数组 ...

  5. quickSort算法导论版实现

    本文主要实践一下算法导论上的快排算法,活动活动. 伪代码图来源于 http://www.cnblogs.com/dongkuo/p/4827281.html // imp the quicksort ...

  6. [算法导论]二叉查找树的实现 @ Python

    <算法导论>第三版的BST(二叉查找树)的实现: class Tree: def __init__(self): self.root = None # Definition for a b ...

  7. 算法导论第十八章 B树

    一.高级数据结构 本章以后到第21章(并查集)隶属于高级数据结构的内容.前面还留了两章:贪心算法和摊还分析,打算后面再来补充.之前的章节讨论的支持动态数据集上的操作,如查找.插入.删除等都是基于简单的 ...

  8. 算法导论----VLSI芯片测试; n个手机中过半是好的,找出哪些是好手机

    对于分治(Divide and Conquer)的题目,最重要是 1.如何将原问题分解为若干个子问题, 2.子问题中是所有的都需要求解,还是选择一部分子问题即可. 还有一点其实非常关键,但是往往会被忽 ...

  9. [置顶] 《算法导论》习题解答搬运&&学习笔记 索引目录

    开始学习<算法导论>了,虽然是本大部头,可能很难一下子看完,我还是会慢慢地研究的. 课后的习题和思考有些是很有挑战性的题目,我等蒻菜很难独立解决. 然后发现了Google上有挺全的algo ...

随机推荐

  1. sqlserver 关于快照

    数据库快照:是数据库某一时间点的视图,快照涉及最初目的是为了报表服务,快照还可以和镜像结合来达到读写分离的目的 数据库快照:是sqlserver数据库的只读静态视图快照的作用:1 提供了一个静态的视图 ...

  2. Cocos2d-x使用瓦片地图

    图所示的复杂地图可以使用瓦片地图技术,瓦片地图是用一些小图片(瓦片)拼接而成,这样可以大大地减少内存消耗.如图所示的瓦片地图,只需要如图所示的三个瓦片就可以了. 瓦片地图 地图中的瓦片 瓦片地图的分类 ...

  3. 通过Curator操作Zookeeper的简单例子代码

    Curator主要解决了三类问题: 一个是ZooKeeper client与ZooKeeper server之间的连接处理; 一个是提供了一套Fluent风格的操作API; 一个是ZooKeeper各 ...

  4. 在O(1)时间删除链表结点

    题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 链表结点与函数的定义如下: struct ListNode { int m_nValue; ListNode* m_p ...

  5. iOS开发基础之排序

    Objective-C 有排序的API,省了我们很多事. 主要有以下3种方法. NSComparator NSArray *unsortedArray = @[@5,@3,@8,@1,@7]; NSA ...

  6. 使用MySQL数据库将汉字转换成拼音的一个C语言小程序

    环境: mysql:mysql-5.1.65 centos:centos 6.5 编译命令: gcc -o chinesetopinyin chinesetopinyin.c -L/usr/lib/m ...

  7. struts2中的常量

    struts2中的常量: 在:struts2-core-2.1.8.1\org\apache\struts2\default.properties 文件里 <!-- 配制i18n国际化--> ...

  8. WinForms 小型HTML服务器

    最近教学,使用到了Apache和IIS,闲着无聊,有种想自己写个小服务器的冲动. 在网上找了半天的资料,最后终于搞定了,测试可以访问.效果图如下: 因为只是处理简单的请求,然后返回请求的页面,所以没有 ...

  9. 【转】 .NET中STAThread和MTAThread

    ref:http://blog.csdn.net/dyllove98/article/details/9735955 1 COM中的公寓 本文讨论进程内COM组件.以一个示例直观演示STAThread ...

  10. 静态的html页面想要设置使用浏览器缓存

    设置html页面缓存方法: 静态的html页面想要设置使用缓存: 通过HTTP的META设置expires和cache-control code 1.  <meta http-equiv=&qu ...