算法导论_ch2

Ch2算法基础

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;

}

}

时间: 2024-08-03 08:22:23

算法导论_ch2的相关文章

算法导论学习之插入排序+合并排序

最近准备花时间把算法导论详细的看一遍,强化一下算法和数据结构的基础,将一些总结性的东西写到博客上去. 一.插入排序 算法思想:如果一个数组A,从A[1–n-1]都是有序的,然后我们将A[n]插入到A[1–n-1]的某个合适的位置上去那么就可以保证A[1–n]都是有序的.这就是插入排序的思想:具体实现的时候我们将数组的第一个元素看出有序,然后从第二个元素开始按照上面的步骤进行插入操作,直到插入最后一个元素,然后整个数组都是有序的了. 时间复杂度分析:代码中有两重for循环,很容易看出时间复杂度是n

算法导论——lec 13 贪心算法与图上算法

之前我们介绍了用动态规划的方法来解决一些最优化的问题.但对于有些最优化问题来说,用动态规划就是"高射炮打蚊子",采用一些更加简单有效的方法就可以解决.贪心算法就是其中之一.贪心算法是使所做的选择看起来是当前最佳的,期望通过所做的局部最优选择来产生一个全局最优解. 一. 活动选择问题 [问题]对几个互相竞争的活动进行调度:活动集合S = {a1, a2, ..., an},它们都要求以独占的方式使用某一公共资源(如教室),每个活动ai有一个开始时间si和结束时间fi ,且0 ≤ si &

算法导论--图的遍历(DFS与BFS)

转载请注明出处:勿在浮沙筑高台http://blog.csdn.net/luoshixian099/article/details/51897538 图的遍历就是从图中的某个顶点出发,按某种方法对图中的所有顶点访问且仅访问一次.为了保证图中的顶点在遍历过程中仅访问一次,要为每一个顶点设置一个访问标志.通常有两种方法:深度优先搜索(DFS)和广度优先搜索(BFS).这两种算法对有向图与无向图均适用. 以下面无向图为例: 1.深度优先搜索(DFS) 基本步骤: 1.从图中某个顶点v0出发,首先访问v

算法导论8:数据结构——栈 2016.1.8

栈在暑假的时候接触过了,当时还写了个计算器,用的中缀表达式后缀表达式的栈操作. http://www.cnblogs.com/itlqs/p/4749998.html 今天按照算法导论上的讲解规范了一下代码.主要是栈的初始化.判断空栈.入栈.出栈.遍历栈. #include<stdio.h> #define MAXTOP 10 struct _stack { int top; int num[MAXTOP+1]; }s; void init(struct _stack &S) { S.

红黑树&mdash;&mdash;算法导论(15)

1. 什么是红黑树 (1) 简介     上一篇我们介绍了基本动态集合操作时间复杂度均为O(h)的二叉搜索树.但遗憾的是,只有当二叉搜索树高度较低时,这些集合操作才会较快:即当树的高度较高(甚至一种极端情况是树变成了1条链)时,这些集合操作并不比在链表上执行的快.     于是我们需要构建出一种"平衡"的二叉搜索树.     红黑树(red-black tree)正是其中的一种.它可以保证在最坏的情况下,基本集合操作的时间复杂度是O(lgn). (2) 性质     与普通二叉搜索树不

算法导论5.3-3

转自风清云淡的博客,他给出的解法非常的妙. 问题: 描述RANDOM(a,b)的过程的一种实现,它只调用RANDOM(0,1).作为a和b的函数,你的程序的期望运行时间是多少?注:RANDOM(0,1)以等概率输出0或者1,      要求RANDOM(a,b)以等概率输出[a,b]之间的数(整数) 解决方案: 1,取 n=b-a+1,取最小的正整数m,使得 2^m >= n         2,调用RANDOM(0,1),输出m-bit位整数N   (  N >= 0 and N <=

算法导论第十二章 二叉搜索树

一.二叉搜索树概览 二叉搜索树(又名二叉查找树.二叉排序树)是一种可提供良好搜寻效率的树形结构,支持动态集合操作,所谓动态集合操作,就是Search.Maximum.Minimum.Insert.Delete等操作,二叉搜索树可以保证这些操作在对数时间内完成.当然,在最坏情况下,即所有节点形成一种链式树结构,则需要O(n)时间.这就说明,针对这些动态集合操作,二叉搜索树还有改进的空间,即确保最坏情况下所有操作在对数时间内完成.这样的改进结构有AVL(Adelson-Velskii-Landis)

算法导论--动态规划(装配线调度)

装配线问题: 某个工厂生产一种产品,有两种装配线选择,每条装配线都有n个装配站.可以单独用,装配线1或2加工生产,也可以使用装配线i的第j个装配站后,进入另一个装配线的第j+1个装配站继续生产.现想找出通过工厂装配线的最快方法. 装配线i的第j个装配站表示为Si,j,在该站的装配时间是ai,j 如果从 Si,j装配站生产后,转移到另一个生产线继续生产所耗费的时间为ti,j 进入装配线花费时间ei,完成生产后离开装配线所耗费时间为xi 令f*表示通过生产所有路线中的最快的时间 令fi[j]表示从入

算法导论CLRS答案

目前正在编写算法导论答案,欢迎大家follow me at mygithub 刚完成第9章,中位数和顺序统计学 正在编写第13章,红黑树 想要参与的朋友可以告诉我想要编写的章节,开个branch给你------