算法导论-2

%%转载

前两天看到一篇介绍二分原理 的帖子,想起了以前写二分法的事情。二分法看似简单,但实际写的时候却发现 +1 -1 的地方很容易弄错。幸好之前看过循环不变量的介绍。

所谓循环不变量,是指在循环过程中保持不变的量。具体取什么样的量呢?显然,pi之类的常量在任何循环中都保持不变,但对分析循环并没有用处。

因此,为便于分析,循环不变量一般会取一个关于循环中的变量 V 的布尔函数 F,在整个循环过程中,F(V)为真,而当循环结束后,(F(V)->R)为真,R是循环的目的。这样,只要证明 F(V) ,即证明了这个循环达到了所要求的目的。

以二分法为例:已知 a[1..n] 是单调递增的数列,求 a 中所有大于或等于 v 的值中,最小的那一个的序号。

如果只是求 a 中等于 v 的值的序号,程序是很容易写出的:

[c-sharp] view plain copy

  1. find( a[1..n], v )
  2. {
  3. int min = 1, max = n;
  4. while( min < max )
  5. {
  6. int mid = (min + max)/2;
  7. if( a[mid] == v ) return mid;
  8. else if( a[mid] < v ) min = mid + 1;
  9. else max = mid - 1;
  10. }
  11. if( min == max && a[min] == v ) return min;  // a[min] 并没有比较过
  12. else return -1;  // 没找到
  13. }

在看下面的分析前,不妨试着先写一个找 a 中大于或等于 v 的最小值的函数,看看循环不变量究竟有没有用。

(说明一下 ":=" 表示“定义为”,"="表示“赋值”,"=="表示“相等”)

在这循环中用到的变量是min,max,首先想到的循环不变量是 F(min,max) := (a[min] <= v <= a[max]),但其中的问题也是显而易见的:循环开始时,v 可能小于 a[1],也可能大于 a[n],而在循环过程中,如果 a[mid-1] < v < a[mid],或者 a[mid] < v < a[mid+1],也会导致 F(min,max) 为假。

解决的方法是在 a 的首尾添加哨兵,即假设 a[0] == -∞, a[n+1] == +∞,循环开始时,min = 0,max = n+1,F(min,max) := (a[min] < v < a[max]) 为真。

循环过程中,当 a[mid] < v 时 min = mid,于是有F(min,max) := (a[min] < v < a[max]) == (a[mid] < v < a[max]) 为真;同样的,当 v < a[mid] 时 max = mid,于是有 F(min,max) := (a[min] < v < a[max]) == (a[min] < v < a[mid]) 为真。

由于 F(min,max) 为真,必然有 min < max (否则,min>=max 且 a[1..n] 升序 -> a[min] >= a[max],与 F(min,max)为真矛盾),因此循环应当在 min == max-1 时结束。此时有:a[max-1] == a[min] < v < a[max],即 a[max] 是 a 中大于或等于 v 的最小值。

程序如下:

[c-sharp] view plain copy

  1. upBound( a[1..n], v )
  2. {
  3. int min = 0, max = n+1;
  4. while( min < max-1 )
  5. {
  6. int mid = (min + max)/2;
  7. if( a[mid] == v ) return mid;
  8. else if( a[mid] < v ) min = mid;
  9. else max = mid;
  10. }
  11. return max;
  12. }

循环不变量,并不能用来设计出一个算法,但却可以帮助程序员在写循环时不致因为一些细节问题出错,也可以用于证明循环的正确性,便于阅读者分析循环,增强对代码的信心。

时间: 2024-10-27 10:35:43

算法导论-2的相关文章

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

最近准备花时间把算法导论详细的看一遍,强化一下算法和数据结构的基础,将一些总结性的东西写到博客上去. 一.插入排序 算法思想:如果一个数组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给你------

《算法导论》思考题15-2 整齐打印

画外音:没想做到15-2题也是费了一番周折,看来<算法导论>里题都不是白给的 整齐打印问题: 考虑在一个打印机上整齐地打印一段文章的问题.输入的正文是n个长度分别为L1.L2.…….Ln(以字符个数度量)的单词构成的序列.我们希望将这个段落在一些行上整齐地打印出来,每行至多M个字符.“整齐度”的标准如下:如果某一行包含从i到j的单词(i<j),且单词之间只留一个空格,则在行末多余的空格字符个数为 M - (j-i) - (Li+ …… + Lj),它必须是非负值才能让该行容纳这些单词.我